Require runnable dispatcher subcommands (#1585)
* Require runnable dispatcher subcommands * Use modern dispatcher test loader
This commit is contained in:
@@ -68,6 +68,10 @@ def _short_help(path: Path) -> str:
|
|||||||
return first
|
return first
|
||||||
|
|
||||||
|
|
||||||
|
def _is_runnable_subcommand(path: Path) -> bool:
|
||||||
|
return path.exists() and path.is_file() and os.access(path, os.X_OK)
|
||||||
|
|
||||||
|
|
||||||
def _print_listing() -> None:
|
def _print_listing() -> None:
|
||||||
"""`odysseus` with no args (or `odysseus help`) — print the table."""
|
"""`odysseus` with no args (or `odysseus help`) — print the table."""
|
||||||
sys.stdout.write(f"odysseus {VERSION} — every feature, on the shell.\n\n")
|
sys.stdout.write(f"odysseus {VERSION} — every feature, on the shell.\n\n")
|
||||||
@@ -101,7 +105,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
_print_listing()
|
_print_listing()
|
||||||
return 0
|
return 0
|
||||||
sub = SCRIPTS_DIR / f"odysseus-{argv[1]}"
|
sub = SCRIPTS_DIR / f"odysseus-{argv[1]}"
|
||||||
if not sub.exists():
|
if not _is_runnable_subcommand(sub):
|
||||||
sys.stderr.write(f"odysseus: unknown subcommand {argv[1]!r}\n")
|
sys.stderr.write(f"odysseus: unknown subcommand {argv[1]!r}\n")
|
||||||
return 1
|
return 1
|
||||||
return subprocess.call([str(sub), "--help"])
|
return subprocess.call([str(sub), "--help"])
|
||||||
@@ -109,7 +113,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
# `odysseus foo ...` → exec `odysseus-foo ...` under the project venv.
|
# `odysseus foo ...` → exec `odysseus-foo ...` under the project venv.
|
||||||
name = argv[0]
|
name = argv[0]
|
||||||
sub = SCRIPTS_DIR / f"odysseus-{name}"
|
sub = SCRIPTS_DIR / f"odysseus-{name}"
|
||||||
if not sub.exists():
|
if not _is_runnable_subcommand(sub):
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
f"odysseus: unknown subcommand {name!r}. "
|
f"odysseus: unknown subcommand {name!r}. "
|
||||||
f"Try `odysseus help` to see available ones.\n"
|
f"Try `odysseus help` to see available ones.\n"
|
||||||
|
|||||||
24
tests/test_odysseus_dispatcher.py
Normal file
24
tests/test_odysseus_dispatcher.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import importlib.machinery
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def _load_dispatcher():
|
||||||
|
path = Path(__file__).resolve().parent.parent / "scripts" / "odysseus"
|
||||||
|
loader = importlib.machinery.SourceFileLoader("odysseus_dispatcher_under_test", str(path))
|
||||||
|
spec = importlib.util.spec_from_loader(loader.name, loader)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_runnable_subcommand_requires_executable_file(tmp_path):
|
||||||
|
cli = _load_dispatcher()
|
||||||
|
sub = tmp_path / "odysseus-demo"
|
||||||
|
sub.write_text("#!/bin/sh\n")
|
||||||
|
sub.chmod(0o644)
|
||||||
|
|
||||||
|
assert cli._is_runnable_subcommand(sub) is False
|
||||||
|
|
||||||
|
sub.chmod(0o755)
|
||||||
|
assert cli._is_runnable_subcommand(sub) is True
|
||||||
Reference in New Issue
Block a user