Skip to content

Commit b8619ef

Browse files
committed
add exectuable complete tests
1 parent 013e7a3 commit b8619ef

File tree

4 files changed

+76
-20
lines changed

4 files changed

+76
-20
lines changed

django_typer/management/commands/shellcompletion.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
It invokes typer's shell completion installation logic, but does have to patch the
88
installed scripts. This is because there is only one installation for all django
99
management commands, not each individual command. The completion logic here will
10-
failover to django's builtin autocomplete if the command in question is not a
10+
failover to django's builtin autocomplete if the command in question is not a
1111
TyperCommand. To promote compatibility with other management command libraries or
1212
custom completion logic, a fallback completion function can also be specified. A
1313
needed refactoring here would be to provide root hooks for completion logic in django

django_typer/tests/completion_tests.py

+54-16
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,15 @@ def tearDown(self):
7676
self.remove()
7777
super().tearDown()
7878

79-
def verify_install(self, script=manage_script):
79+
def verify_install(self, script=None):
8080
pass
8181

82-
def verify_remove(self, script=manage_script):
82+
def verify_remove(self, script=None):
8383
pass
8484

85-
def install(self, script=manage_script):
85+
def install(self, script=None):
86+
if not script:
87+
script = self.manage_script
8688
kwargs = {}
8789
if self.shell:
8890
kwargs["shell"] = self.shell
@@ -91,7 +93,9 @@ def install(self, script=manage_script):
9193
self.command.install(**kwargs)
9294
self.verify_install(script=script)
9395

94-
def remove(self, script=manage_script):
96+
def remove(self, script=None):
97+
if not script:
98+
script = self.manage_script
9599
kwargs = {}
96100
if self.shell:
97101
kwargs["shell"] = self.shell
@@ -185,20 +189,49 @@ def test_shell_complete(self):
185189
self.install()
186190

187191

192+
class _InstalledScriptTestCase(_DefaultCompleteTestCase):
193+
"""
194+
These shell completes use an installed script available on the path
195+
instead of a script directly invoked by path. The difference may
196+
seem trivial - but it is not given how most shells determine if completion
197+
logic should be invoked for a given command.
198+
"""
199+
200+
MANAGE_SCRIPT_TMPL = Path(__file__).parent / "django_manage.py"
201+
manage_script = "django_manage"
202+
launch_script = "django_manage"
203+
204+
def setUp(self):
205+
lines = []
206+
with open(self.MANAGE_SCRIPT_TMPL, "r") as f:
207+
for line in f.readlines():
208+
if line.startswith("#!{{shebang}}"):
209+
line = f"#!{sys.executable}\n"
210+
lines.append(line)
211+
exe = Path(sys.executable).parent / self.manage_script
212+
with open(exe, "w") as f:
213+
for line in lines:
214+
f.write(line)
215+
216+
# make the script executable
217+
os.chmod(exe, os.stat(exe).st_mode | 0o111)
218+
super().setUp()
219+
220+
188221
@pytest.mark.skipif(shutil.which("zsh") is None, reason="Z-Shell not available")
189222
class ZshShellTests(_DefaultCompleteTestCase, TestCase):
190223

191224
shell = "zsh"
192225
directory = Path("~/.zfunc").expanduser()
193226

194-
def verify_install(self, script=_DefaultCompleteTestCase.manage_script):
227+
def verify_install(self, script=None):
195228
if not script:
196-
script = self.command.manage_script_name
229+
script = self.manage_script
197230
self.assertTrue((self.directory / f"_{script}").exists())
198231

199-
def verify_remove(self, script=_DefaultCompleteTestCase.manage_script):
232+
def verify_remove(self, script=None):
200233
if not script:
201-
script = self.command.manage_script_name
234+
script = self.manage_script
202235
self.assertFalse((self.directory / f"_{script}").exists())
203236

204237

@@ -218,17 +251,22 @@ def set_environment(self, fd):
218251
os.write(fd, f"source ~/.bashrc\n".encode())
219252
os.write(fd, f"source .venv/bin/activate\n".encode())
220253

221-
def verify_install(self, script=_DefaultCompleteTestCase.manage_script):
254+
def verify_install(self, script=None):
222255
if not script:
223-
script = self.command.manage_script_name
256+
script = self.manage_script
224257
self.assertTrue((self.directory / f"{script}.sh").exists())
225258

226-
def verify_remove(self, script=_DefaultCompleteTestCase.manage_script):
259+
def verify_remove(self, script=None):
227260
if not script:
228-
script = self.command.manage_script_name
261+
script = self.manage_script
229262
self.assertFalse((self.directory / f"{script}.sh").exists())
230263

231264

265+
@pytest.mark.skipif(shutil.which("bash") is None, reason="Bash not available")
266+
class BashExeShellTests(_InstalledScriptTestCase, TestCase):
267+
pass
268+
269+
232270
@pytest.mark.skipif(shutil.which("pwsh") is None, reason="Powershell not available")
233271
class PowerShellTests(_DefaultCompleteTestCase, TestCase):
234272

@@ -257,18 +295,18 @@ def test_shell_complete(self):
257295
self.install()
258296
self.remove()
259297

260-
def verify_install(self, script=_DefaultCompleteTestCase.manage_script):
298+
def verify_install(self, script=None):
261299
if not script:
262-
script = self.command.manage_script_name
300+
script = self.manage_script
263301
self.assertTrue((self.directory / f"Microsoft.PowerShell_profile.ps1").exists())
264302
self.assertTrue(
265303
f"Register-ArgumentCompleter -Native -CommandName {script} -ScriptBlock $scriptblock"
266304
in (self.directory / f"Microsoft.PowerShell_profile.ps1").read_text()
267305
)
268306

269-
def verify_remove(self, script=_DefaultCompleteTestCase.manage_script):
307+
def verify_remove(self, script=None):
270308
if not script:
271-
script = self.command.manage_script_name
309+
script = self.manage_script
272310
if (self.directory / f"Microsoft.PowerShell_profile.ps1").exists():
273311
contents = (
274312
self.directory / f"Microsoft.PowerShell_profile.ps1"

django_typer/tests/django_manage.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!{{shebang}}
2+
import os
3+
import sys
4+
5+
6+
def main():
7+
"""Run administrative tasks."""
8+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_typer.tests.settings")
9+
try:
10+
from django.core.management import execute_from_command_line
11+
except ImportError as exc:
12+
raise ImportError(
13+
"Couldn't import Django. Are you sure it's installed and "
14+
"available on your PYTHONPATH environment variable? Did you "
15+
"forget to activate a virtual environment?"
16+
) from exc
17+
execute_from_command_line(sys.argv)
18+
19+
20+
if __name__ == "__main__":
21+
sys.exit(main())

pyproject.toml

-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ packages = [
3838
]
3939
exclude = ["django_typer/tests"]
4040

41-
[tool.poetry.scripts]
42-
django_complete = "django_typer.autocomplete:main"
43-
4441
[tool.poetry.dependencies]
4542
python = ">=3.9,<4.0"
4643
Django = ">=3.2,<6.0"

0 commit comments

Comments
 (0)