Merging upstream version 1.13.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
4e93acdb1a
commit
fe812793aa
6 changed files with 146 additions and 30 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,3 +1,15 @@
|
||||||
|
## 1.13.2 - 2024-11-24
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
* Read the version from the git tag using setuptools-scm
|
||||||
|
|
||||||
|
## 1.13.0 - 2024-11-23
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `\pipe_once` / `\|` commands for sending output to a command
|
||||||
|
|
||||||
## 1.12.4 - 2024-11-11
|
## 1.12.4 - 2024-11-11
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
__version__ = "1.12.4"
|
import importlib.metadata
|
||||||
|
|
||||||
|
__version__ = importlib.metadata.version("litecli")
|
||||||
|
|
|
@ -472,6 +472,7 @@ class LiteCli(object):
|
||||||
result_count += 1
|
result_count += 1
|
||||||
mutating = mutating or is_mutating(status)
|
mutating = mutating or is_mutating(status)
|
||||||
special.unset_once_if_written()
|
special.unset_once_if_written()
|
||||||
|
special.unset_pipe_once_if_written()
|
||||||
except EOFError as e:
|
except EOFError as e:
|
||||||
raise e
|
raise e
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@ -658,6 +659,7 @@ class LiteCli(object):
|
||||||
self.log_output(line)
|
self.log_output(line)
|
||||||
special.write_tee(line)
|
special.write_tee(line)
|
||||||
special.write_once(line)
|
special.write_once(line)
|
||||||
|
special.write_pipe_once(line)
|
||||||
|
|
||||||
if fits or output_via_pager:
|
if fits or output_via_pager:
|
||||||
# buffering
|
# buffering
|
||||||
|
@ -824,7 +826,7 @@ class LiteCli(object):
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("-V", "--version", is_flag=True, help="Output litecli's version.")
|
@click.version_option(__version__, "-V", "--version")
|
||||||
@click.option("-D", "--database", "dbname", help="Database to use.")
|
@click.option("-D", "--database", "dbname", help="Database to use.")
|
||||||
@click.option(
|
@click.option(
|
||||||
"-R",
|
"-R",
|
||||||
|
@ -857,7 +859,6 @@ class LiteCli(object):
|
||||||
def cli(
|
def cli(
|
||||||
database,
|
database,
|
||||||
dbname,
|
dbname,
|
||||||
version,
|
|
||||||
prompt,
|
prompt,
|
||||||
logfile,
|
logfile,
|
||||||
auto_vertical_output,
|
auto_vertical_output,
|
||||||
|
@ -874,11 +875,6 @@ def cli(
|
||||||
- litecli lite_database
|
- litecli lite_database
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if version:
|
|
||||||
print("Version:", __version__)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
litecli = LiteCli(
|
litecli = LiteCli(
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
logfile=logfile,
|
logfile=logfile,
|
||||||
|
|
|
@ -21,7 +21,10 @@ from litecli.packages.prompt_utils import confirm_destructive_query
|
||||||
use_expanded_output = False
|
use_expanded_output = False
|
||||||
PAGER_ENABLED = True
|
PAGER_ENABLED = True
|
||||||
tee_file = None
|
tee_file = None
|
||||||
once_file = once_file_args = written_to_once_file = None
|
once_file = None
|
||||||
|
written_to_once_file = None
|
||||||
|
pipe_once_process = None
|
||||||
|
written_to_pipe_once_process = False
|
||||||
favoritequeries = FavoriteQueries(ConfigObj())
|
favoritequeries = FavoriteQueries(ConfigObj())
|
||||||
|
|
||||||
|
|
||||||
|
@ -376,9 +379,12 @@ def write_tee(output):
|
||||||
aliases=("\\o", "\\once"),
|
aliases=("\\o", "\\once"),
|
||||||
)
|
)
|
||||||
def set_once(arg, **_):
|
def set_once(arg, **_):
|
||||||
global once_file_args
|
global once_file, written_to_once_file
|
||||||
|
try:
|
||||||
once_file_args = parseargfile(arg)
|
once_file = open(**parseargfile(arg))
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
raise OSError("Cannot write to file '{}': {}".format(e.filename, e.strerror))
|
||||||
|
written_to_once_file = False
|
||||||
|
|
||||||
return [(None, None, None, "")]
|
return [(None, None, None, "")]
|
||||||
|
|
||||||
|
@ -386,26 +392,66 @@ def set_once(arg, **_):
|
||||||
@export
|
@export
|
||||||
def write_once(output):
|
def write_once(output):
|
||||||
global once_file, written_to_once_file
|
global once_file, written_to_once_file
|
||||||
if output and once_file_args:
|
if output and once_file:
|
||||||
if once_file is None:
|
|
||||||
try:
|
|
||||||
once_file = open(**once_file_args)
|
|
||||||
except (IOError, OSError) as e:
|
|
||||||
once_file = None
|
|
||||||
raise OSError("Cannot write to file '{}': {}".format(e.filename, e.strerror))
|
|
||||||
|
|
||||||
click.echo(output, file=once_file, nl=False)
|
click.echo(output, file=once_file, nl=False)
|
||||||
click.echo("\n", file=once_file, nl=False)
|
click.echo("\n", file=once_file, nl=False)
|
||||||
|
once_file.flush()
|
||||||
written_to_once_file = True
|
written_to_once_file = True
|
||||||
|
|
||||||
|
|
||||||
@export
|
@export
|
||||||
def unset_once_if_written():
|
def unset_once_if_written():
|
||||||
"""Unset the once file, if it has been written to."""
|
"""Unset the once file, if it has been written to."""
|
||||||
global once_file, once_file_args, written_to_once_file
|
global once_file, written_to_once_file
|
||||||
if once_file and written_to_once_file:
|
if once_file and written_to_once_file:
|
||||||
once_file.close()
|
once_file.close()
|
||||||
once_file = once_file_args = written_to_once_file = None
|
once_file = written_to_once_file = None
|
||||||
|
|
||||||
|
|
||||||
|
@special_command("\\pipe_once", "\\| command", "Send next result to a subprocess.", aliases=("\\|",))
|
||||||
|
def set_pipe_once(arg, **_):
|
||||||
|
global pipe_once_process, written_to_pipe_once_process
|
||||||
|
pipe_once_cmd = shlex.split(arg)
|
||||||
|
if len(pipe_once_cmd) == 0:
|
||||||
|
raise OSError("pipe_once requires a command")
|
||||||
|
written_to_pipe_once_process = False
|
||||||
|
pipe_once_process = subprocess.Popen(
|
||||||
|
pipe_once_cmd,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
bufsize=1,
|
||||||
|
encoding="UTF-8",
|
||||||
|
universal_newlines=True,
|
||||||
|
)
|
||||||
|
return [(None, None, None, "")]
|
||||||
|
|
||||||
|
|
||||||
|
@export
|
||||||
|
def write_pipe_once(output):
|
||||||
|
global pipe_once_process, written_to_pipe_once_process
|
||||||
|
if output and pipe_once_process:
|
||||||
|
try:
|
||||||
|
click.echo(output, file=pipe_once_process.stdin, nl=False)
|
||||||
|
click.echo("\n", file=pipe_once_process.stdin, nl=False)
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
pipe_once_process.terminate()
|
||||||
|
raise OSError("Failed writing to pipe_once subprocess: {}".format(e.strerror))
|
||||||
|
written_to_pipe_once_process = True
|
||||||
|
|
||||||
|
|
||||||
|
@export
|
||||||
|
def unset_pipe_once_if_written():
|
||||||
|
"""Unset the pipe_once cmd, if it has been written to."""
|
||||||
|
global pipe_once_process, written_to_pipe_once_process
|
||||||
|
if written_to_pipe_once_process:
|
||||||
|
(stdout_data, stderr_data) = pipe_once_process.communicate()
|
||||||
|
if len(stdout_data) > 0:
|
||||||
|
print(stdout_data.rstrip("\n"))
|
||||||
|
if len(stderr_data) > 0:
|
||||||
|
print(stderr_data.rstrip("\n"))
|
||||||
|
pipe_once_process = None
|
||||||
|
written_to_pipe_once_process = False
|
||||||
|
|
||||||
|
|
||||||
@special_command(
|
@special_command(
|
||||||
|
|
|
@ -5,9 +5,7 @@ description = "CLI for SQLite Databases with auto-completion and syntax highligh
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
license = { text = "BSD" }
|
license = { text = "BSD" }
|
||||||
authors = [
|
authors = [{ name = "dbcli", email = "litecli-users@googlegroups.com" }]
|
||||||
{name = "dbcli", email = "litecli-users@googlegroups.com"}
|
|
||||||
]
|
|
||||||
urls = { "homepage" = "https://github.com/dbcli/litecli" }
|
urls = { "homepage" = "https://github.com/dbcli/litecli" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cli-helpers[styles]>=2.2.1",
|
"cli-helpers[styles]>=2.2.1",
|
||||||
|
@ -19,9 +17,15 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools >= 61.0"]
|
requires = [
|
||||||
|
"setuptools>=64.0",
|
||||||
|
"setuptools-scm>=8;python_version>='3.8'",
|
||||||
|
"setuptools-scm<8;python_version<'3.8'",
|
||||||
|
]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.setuptools_scm]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
litecli = "litecli.main:cli"
|
litecli = "litecli.main:cli"
|
||||||
|
|
||||||
|
@ -42,8 +46,5 @@ exclude = ["screenshots", "tests*"]
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
litecli = ["liteclirc", "AUTHORS"]
|
litecli = ["liteclirc", "AUTHORS"]
|
||||||
|
|
||||||
[tool.setuptools.dynamic]
|
|
||||||
version = {attr = "litecli.__version__"}
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 140
|
line-length = 140
|
||||||
|
|
59
tests/test_special_iocommands.py
Normal file
59
tests/test_special_iocommands.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import litecli.packages.special
|
||||||
|
|
||||||
|
|
||||||
|
def test_once_command():
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
litecli.packages.special.execute(None, ".once")
|
||||||
|
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
litecli.packages.special.execute(None, ".once /proc/access-denied")
|
||||||
|
|
||||||
|
litecli.packages.special.write_once("hello world") # write without file set
|
||||||
|
# keep Windows from locking the file with delete=False
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
litecli.packages.special.execute(None, ".once " + f.name)
|
||||||
|
litecli.packages.special.write_once("hello world")
|
||||||
|
if os.name == "nt":
|
||||||
|
assert f.read() == b"hello world\r\n"
|
||||||
|
else:
|
||||||
|
assert f.read() == b"hello world\n"
|
||||||
|
|
||||||
|
litecli.packages.special.execute(None, ".once -o " + f.name)
|
||||||
|
litecli.packages.special.write_once("hello world line 1")
|
||||||
|
litecli.packages.special.write_once("hello world line 2")
|
||||||
|
f.seek(0)
|
||||||
|
if os.name == "nt":
|
||||||
|
assert f.read() == b"hello world line 1\r\nhello world line 2\r\n"
|
||||||
|
else:
|
||||||
|
assert f.read() == b"hello world line 1\nhello world line 2\n"
|
||||||
|
# delete=False means we should try to clean up
|
||||||
|
try:
|
||||||
|
if os.path.exists(f.name):
|
||||||
|
os.remove(f.name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred while attempting to delete the file: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_pipe_once_command():
|
||||||
|
with pytest.raises(IOError):
|
||||||
|
litecli.packages.special.execute(None, "\\pipe_once")
|
||||||
|
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
litecli.packages.special.execute(None, "\\pipe_once /proc/access-denied")
|
||||||
|
|
||||||
|
if os.name == "nt":
|
||||||
|
litecli.packages.special.execute(None, '\\pipe_once python -c "import sys; print(len(sys.stdin.read().strip()))"')
|
||||||
|
litecli.packages.special.write_pipe_once("hello world")
|
||||||
|
litecli.packages.special.unset_pipe_once_if_written()
|
||||||
|
else:
|
||||||
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
litecli.packages.special.execute(None, "\\pipe_once tee " + f.name)
|
||||||
|
litecli.packages.special.write_pipe_once("hello world")
|
||||||
|
litecli.packages.special.unset_pipe_once_if_written()
|
||||||
|
f.seek(0)
|
||||||
|
assert f.read() == b"hello world\n"
|
Loading…
Add table
Reference in a new issue