Skip to content

Commit 775fe15

Browse files
committed
zshell tests working again #156
1 parent 1451da7 commit 775fe15

File tree

6 files changed

+35
-42
lines changed

6 files changed

+35
-42
lines changed

django_typer/management/commands/shellcompletion.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import re
2626
import sys
2727
import typing as t
28-
from functools import cached_property
2928
from importlib.resources import files
3029
from pathlib import Path
3130
from types import ModuleType
@@ -311,6 +310,7 @@ class Command(TyperCommand):
311310
ANSI_ESCAPE_RE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
312311

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

315315
@property
316316
def fallback(
@@ -337,7 +337,7 @@ def fallback_import(self) -> t.Optional[str]:
337337
else None
338338
)
339339

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

362-
@cached_property
365+
@property
363366
def manage_script_name(self) -> str:
364367
"""
365368
Get the name of the manage script as a command available from the shell's path.
366369
"""
367-
return str(getattr(self.manage_script, "name", self.manage_script))
370+
if isinstance(self.manage_script, Path):
371+
return self.manage_script.name
372+
return self.manage_script
368373

369374
@property
370375
def shell(self) -> str:
@@ -486,6 +491,7 @@ def install(
486491
:convert-png: latex
487492
"""
488493
self.fallback = fallback # type: ignore[assignment]
494+
self.manage_script = manage_script # type: ignore[assignment]
489495
if isinstance(self.manage_script, Path):
490496
if not self.shell_class.supports_scripts:
491497
raise CommandError(
@@ -559,6 +565,7 @@ def uninstall(
559565
:convert-png: latex
560566
561567
"""
568+
self.manage_script = manage_script # type: ignore[assignment]
562569
self.shell_class(
563570
prog_name=str(manage_script or self.manage_script_name),
564571
command=self,

django_typer/templates/shell_complete/zsh.sh

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
# we need to pass these to the complete script - they may be necessary to find the command!
1010
local settings_option=""
1111
local pythonpath_option=""
12-
13-
{% if is_installed %}
14-
(( ! $+commands[%(prog_name)s] )) && return 1
12+
local manage="{% if is_installed %}{{ manage_script_name }}{% endif %}"
13+
{% if not is_installed %}
14+
if [[ ${words[2]} == *{{manage_script_name}} ]]; then
15+
manage="${words[1]} ${words[2]}"
16+
else
17+
manage="${words[1]}"
18+
fi
1519
{% endif %}
1620

1721
for ((i=1; i<$CURRENT; i++)); do
@@ -31,17 +35,7 @@
3135
esac
3236
done
3337

34-
{% if not is_installed %}
35-
if [[ ${words[2]} == *{{manage_script_name}} ]]; then
36-
cmd="${words[1]} ${words[2]}"
37-
else
38-
cmd="${words[1]}"
39-
fi
40-
{% else %}
41-
cmd = "{{ manage_script_name }}"
42-
{% endif %}
43-
44-
response=("${(@f)$("${cmd}" {{ django_command }} --shell zsh ${settings_option:+${settings_option}} ${pythonpath_option:+${pythonpath_option}} {{ color }} complete "${words[*]}")}")
38+
response=("${(@f)$("${manage}" {{ django_command }} --shell zsh ${settings_option:+${settings_option}} ${pythonpath_option:+${pythonpath_option}} {{ color }} complete "${words[*]}")}")
4539

4640
for type key descr in ${response}; do
4741
if [[ "$type" == "plain" ]]; then

django_typer/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def get_usage_script(script: t.Optional[str] = None) -> t.Union[Path, str]:
4141
if shutil.which(cmd_pth.name):
4242
return cmd_pth.name
4343
try:
44-
return cmd_pth.absolute().relative_to(Path(os.getcwd()))
44+
return cmd_pth.absolute().relative_to(Path(os.getcwd())).absolute()
4545
except ValueError:
4646
return cmd_pth.absolute()
4747

tests/shellcompletion/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,19 @@ def read(fd):
148148
print(read(master_fd))
149149
# Send a command with a tab character for completion
150150

151-
os.write(master_fd, (" ".join(cmds)).encode())
151+
cmd = " ".join(cmds)
152+
os.write(master_fd, cmd.encode())
152153
time.sleep(0.5)
153154

154-
print(f'"{(" ".join(cmds))}"')
155+
print(f'"{cmd}"')
155156
os.write(master_fd, b"\t\t")
156157

157158
time.sleep(0.5)
158159

159160
# Read the output
160161
output = read_all_from_fd_with_timeout(master_fd, 3)
161162

163+
# todo - avoid large output because this can mess things up
162164
if "do you wish" in output or "Display all" in output:
163165
os.write(master_fd, b"y\n")
164166
time.sleep(0.5)
@@ -194,7 +196,7 @@ def run_command_completion(self):
194196
# annoingly in CI there are some spaces inserted between the incomplete phrase
195197
# and the completion on linux in bash specifically
196198
self.assertTrue(re.match(r".*complet\s*ion.*", completions))
197-
completions = self.get_completions(self.launch_script, " ")
199+
completions = self.get_completions(self.launch_script)
198200
self.assertIn("adapted", completions)
199201
self.assertIn("help_precedence", completions)
200202
self.assertIn("closepoll", completions)

tests/shellcompletion/test_zsh.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55
from django.test import TestCase
66

7-
from tests.shellcompletion import _DefaultCompleteTestCase
7+
from tests.shellcompletion import _DefaultCompleteTestCase, _InstalledScriptTestCase
88

99

1010
@pytest.mark.skipif(shutil.which("zsh") is None, reason="Z-Shell not available")
@@ -21,3 +21,8 @@ def verify_remove(self, script=None):
2121
if not script:
2222
script = self.manage_script
2323
self.assertFalse((self.directory / f"_{script}").exists())
24+
25+
26+
@pytest.mark.skipif(shutil.which("zsh") is None, reason="Z-Shell not available")
27+
class ZshExeShellTests(_InstalledScriptTestCase, ZshShellTests, TestCase):
28+
shell = "zsh"

tests/utils.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -248,18 +248,3 @@ def read_django_parameters():
248248

249249
def to_platform_str(path: str) -> str:
250250
return path.replace("/", os.path.sep)
251-
252-
253-
def install_manage_script(name="manage"):
254-
"""
255-
We don't want to deliver bogus entry_points so we manually install
256-
manage.py to our distro's bin directory for shell complete testing.
257-
"""
258-
bin_dir = Path(sys.executable).parent
259-
with open(bin_dir / name, "wt") as ms:
260-
ms.write(f"#!{sys.executable}{os.linesep}")
261-
ms.write(f"import sys{os.linesep}")
262-
ms.write(f"from tests.manage import main{os.linesep}")
263-
ms.write(f"{os.linesep}")
264-
ms.write(f"if __name__ == '__main__':{os.linesep}")
265-
ms.write(f" sys.exit(main()){os.linesep}")

0 commit comments

Comments
 (0)