Skip to content

Commit

Permalink
zshell tests working again #156
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Dec 18, 2024
1 parent 1451da7 commit 775fe15
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 42 deletions.
23 changes: 15 additions & 8 deletions django_typer/management/commands/shellcompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import re
import sys
import typing as t
from functools import cached_property
from importlib.resources import files
from pathlib import Path
from types import ModuleType
Expand Down Expand Up @@ -311,6 +310,7 @@ class Command(TyperCommand):
ANSI_ESCAPE_RE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")

_fallback: t.Optional[t.Callable[[t.List[str], str], t.List[CompletionItem]]] = None
_manage_script: t.Optional[t.Union[str, Path]] = None

@property
def fallback(
Expand All @@ -337,7 +337,7 @@ def fallback_import(self) -> t.Optional[str]:
else None
)

@cached_property
@property
def manage_script(self) -> t.Union[str, Path]:
"""
Returns the name of the manage command as a string if it is available as a command
Expand All @@ -354,17 +354,22 @@ def manage_script(self) -> t.Union[str, Path]:
# with a manage.py script being invoked directly as a script. Completion should work in
# this case as well, but it does complicate the installation for some shell's so we must
# first figure out which mode we are in.
script = get_usage_script()
if isinstance(script, Path):
return script.absolute()
return script
if not self._manage_script:
self._manage_script = get_usage_script()
return self._manage_script

@manage_script.setter
def manage_script(self, script: t.Optional[str]):
self._manage_script = get_usage_script(script)

@cached_property
@property
def manage_script_name(self) -> str:
"""
Get the name of the manage script as a command available from the shell's path.
"""
return str(getattr(self.manage_script, "name", self.manage_script))
if isinstance(self.manage_script, Path):
return self.manage_script.name
return self.manage_script

@property
def shell(self) -> str:
Expand Down Expand Up @@ -486,6 +491,7 @@ def install(
:convert-png: latex
"""
self.fallback = fallback # type: ignore[assignment]
self.manage_script = manage_script # type: ignore[assignment]
if isinstance(self.manage_script, Path):
if not self.shell_class.supports_scripts:
raise CommandError(
Expand Down Expand Up @@ -559,6 +565,7 @@ def uninstall(
:convert-png: latex
"""
self.manage_script = manage_script # type: ignore[assignment]
self.shell_class(
prog_name=str(manage_script or self.manage_script_name),
command=self,
Expand Down
22 changes: 8 additions & 14 deletions django_typer/templates/shell_complete/zsh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
# we need to pass these to the complete script - they may be necessary to find the command!
local settings_option=""
local pythonpath_option=""

{% if is_installed %}
(( ! $+commands[%(prog_name)s] )) && return 1
local manage="{% if is_installed %}{{ manage_script_name }}{% endif %}"
{% if not is_installed %}
if [[ ${words[2]} == *{{manage_script_name}} ]]; then
manage="${words[1]} ${words[2]}"
else
manage="${words[1]}"
fi
{% endif %}

for ((i=1; i<$CURRENT; i++)); do
Expand All @@ -31,17 +35,7 @@
esac
done

{% if not is_installed %}
if [[ ${words[2]} == *{{manage_script_name}} ]]; then
cmd="${words[1]} ${words[2]}"
else
cmd="${words[1]}"
fi
{% else %}
cmd = "{{ manage_script_name }}"
{% endif %}

response=("${(@f)$("${cmd}" {{ django_command }} --shell zsh ${settings_option:+${settings_option}} ${pythonpath_option:+${pythonpath_option}} {{ color }} complete "${words[*]}")}")
response=("${(@f)$("${manage}" {{ django_command }} --shell zsh ${settings_option:+${settings_option}} ${pythonpath_option:+${pythonpath_option}} {{ color }} complete "${words[*]}")}")

for type key descr in ${response}; do
if [[ "$type" == "plain" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion django_typer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get_usage_script(script: t.Optional[str] = None) -> t.Union[Path, str]:
if shutil.which(cmd_pth.name):
return cmd_pth.name
try:
return cmd_pth.absolute().relative_to(Path(os.getcwd()))
return cmd_pth.absolute().relative_to(Path(os.getcwd())).absolute()
except ValueError:
return cmd_pth.absolute()

Expand Down
8 changes: 5 additions & 3 deletions tests/shellcompletion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,19 @@ def read(fd):
print(read(master_fd))
# Send a command with a tab character for completion

os.write(master_fd, (" ".join(cmds)).encode())
cmd = " ".join(cmds)
os.write(master_fd, cmd.encode())
time.sleep(0.5)

print(f'"{(" ".join(cmds))}"')
print(f'"{cmd}"')
os.write(master_fd, b"\t\t")

time.sleep(0.5)

# Read the output
output = read_all_from_fd_with_timeout(master_fd, 3)

# todo - avoid large output because this can mess things up
if "do you wish" in output or "Display all" in output:
os.write(master_fd, b"y\n")
time.sleep(0.5)
Expand Down Expand Up @@ -194,7 +196,7 @@ def run_command_completion(self):
# annoingly in CI there are some spaces inserted between the incomplete phrase
# and the completion on linux in bash specifically
self.assertTrue(re.match(r".*complet\s*ion.*", completions))
completions = self.get_completions(self.launch_script, " ")
completions = self.get_completions(self.launch_script)
self.assertIn("adapted", completions)
self.assertIn("help_precedence", completions)
self.assertIn("closepoll", completions)
Expand Down
7 changes: 6 additions & 1 deletion tests/shellcompletion/test_zsh.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest
from django.test import TestCase

from tests.shellcompletion import _DefaultCompleteTestCase
from tests.shellcompletion import _DefaultCompleteTestCase, _InstalledScriptTestCase


@pytest.mark.skipif(shutil.which("zsh") is None, reason="Z-Shell not available")
Expand All @@ -21,3 +21,8 @@ def verify_remove(self, script=None):
if not script:
script = self.manage_script
self.assertFalse((self.directory / f"_{script}").exists())


@pytest.mark.skipif(shutil.which("zsh") is None, reason="Z-Shell not available")
class ZshExeShellTests(_InstalledScriptTestCase, ZshShellTests, TestCase):
shell = "zsh"
15 changes: 0 additions & 15 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,18 +248,3 @@ def read_django_parameters():

def to_platform_str(path: str) -> str:
return path.replace("/", os.path.sep)


def install_manage_script(name="manage"):
"""
We don't want to deliver bogus entry_points so we manually install
manage.py to our distro's bin directory for shell complete testing.
"""
bin_dir = Path(sys.executable).parent
with open(bin_dir / name, "wt") as ms:
ms.write(f"#!{sys.executable}{os.linesep}")
ms.write(f"import sys{os.linesep}")
ms.write(f"from tests.manage import main{os.linesep}")
ms.write(f"{os.linesep}")
ms.write(f"if __name__ == '__main__':{os.linesep}")
ms.write(f" sys.exit(main()){os.linesep}")

0 comments on commit 775fe15

Please sign in to comment.