Skip to content

Commit 76895fd

Browse files
authored
Merge pull request #4 from MAK-Relic-Tool/add-cli-support
Hotfix for `run_with` & `_load`
2 parents 4107caf + b2daa72 commit 76895fd

3 files changed

Lines changed: 64 additions & 29 deletions

File tree

src/relic/core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""
22
Core files shared between other Relic-Tool packages.
33
"""
4-
__version__ = "1.1.0"
4+
__version__ = "1.1.1"

src/relic/core/cli.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
import sys
44
from argparse import ArgumentParser, Namespace
5-
from typing import Optional, TYPE_CHECKING, Protocol, Any
5+
from typing import Optional, TYPE_CHECKING, Protocol, Any, Union
66

77
import pkg_resources
88

9-
if TYPE_CHECKING:
10-
# Circumvent mypy/pylint shenanigans
11-
class _SubParsersAction: # pylint: disable= too-few-public-methods # typechecker only, ignore warnings
12-
def add_parser( # pylint: disable=redefined-builtin, unused-argument # typechecker only, ignore warnings
13-
self,
14-
name: str,
15-
*,
16-
prog: Optional[str] = None,
17-
aliases: Optional[Any] = None,
18-
help: Optional[str] = None,
19-
**kwargs: Any,
20-
) -> ArgumentParser:
21-
...
9+
10+
# Circumvent mypy/pylint shenanigans ~
11+
class _SubParsersAction: # pylint: disable= too-few-public-methods # typechecker only, ignore warnings
12+
def add_parser( # pylint: disable=redefined-builtin, unused-argument # typechecker only, ignore warnings
13+
self,
14+
name: str,
15+
*,
16+
prog: Optional[str] = None,
17+
aliases: Optional[Any] = None,
18+
help: Optional[str] = None,
19+
**kwargs: Any,
20+
) -> ArgumentParser:
21+
raise NotImplementedError
2222

2323

2424
class CliEntrypoint(Protocol): # pylint: disable= too-few-public-methods
@@ -30,24 +30,28 @@ class _CliPlugin: # pylint: disable= too-few-public-methods
3030
def __init__(self, parser: ArgumentParser):
3131
self.parser = parser
3232

33-
def _run(self, ns: Namespace) -> None:
33+
def _run(self, ns: Namespace) -> int:
3434
if not hasattr(ns, "cmd"):
3535
raise NotImplementedError(
3636
"Command defined in argparse, but it's function was not specified."
3737
)
3838
cmd = ns.cmd
39-
result = cmd(ns)
39+
result: Optional[int] = cmd(ns)
4040
if result is None: # Assume success
4141
result = 0
42-
sys.exit(result)
42+
return result
4343

44-
def run_with(self, *args: Any) -> None:
45-
ns = self.parser.parse_args(args)
46-
self._run(ns)
44+
def run_with(self, *args: Any) -> Union[str, int, None]:
45+
try:
46+
ns = self.parser.parse_args(args)
47+
return self._run(ns)
48+
except SystemExit as sys_exit:
49+
return sys_exit.code
4750

4851
def run(self) -> None:
4952
ns = self.parser.parse_args()
50-
self._run(ns)
53+
exit_code = self._run(ns)
54+
sys.exit(exit_code)
5155

5256

5357
class CliPluginGroup(_CliPlugin): # pylint: disable= too-few-public-methods
@@ -64,6 +68,7 @@ def __init__(
6468
parser = self._create_parser(parent)
6569
super().__init__(parser)
6670
self.subparsers = self._create_subparser_group(parser)
71+
self._load()
6772

6873
def _create_parser(
6974
self, command_group: Optional[_SubParsersAction] = None

tests/test_cli.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,40 @@
11
import subprocess
22

3-
43
# Local testing requires running `pip install -e "."`
5-
def test_cli():
6-
cmd = subprocess.run(["relic", "-h"], capture_output=True, text=True)
7-
data = cmd.stdout
8-
assert "usage: relic [-h]" in data
9-
exit_status = cmd.returncode
10-
assert exit_status == 0
4+
from contextlib import redirect_stdout
5+
import io
6+
from typing import Sequence
7+
8+
import pytest
9+
10+
11+
class CommandTests:
12+
def test_run(self, args: Sequence[str], output: str, exit_code: int):
13+
_args = ["relic", *args]
14+
cmd = subprocess.run(_args, capture_output=True, text=True)
15+
result = cmd.stdout
16+
status = cmd.returncode
17+
print(f"'{result}'") # Visual Aid for Debugging
18+
assert output in result
19+
assert status == exit_code
20+
21+
def test_run_with(self, args: Sequence[str], output: str, exit_code: int):
22+
from relic.core.cli import cli_root
23+
with io.StringIO() as f:
24+
with redirect_stdout(f):
25+
status = cli_root.run_with(*args)
26+
f.seek(0)
27+
result = f.read()
28+
print(f"'{result}'") # Visual Aid for Debugging
29+
assert output in result
30+
assert status == exit_code
31+
32+
_HELP = ["-h"], """usage: relic [-h] {} ...""", 0
33+
34+
_TESTS = [_HELP]
35+
_TEST_IDS = [' '.join(_[0]) for _ in _TESTS]
36+
37+
38+
@pytest.mark.parametrize(["args", "output", "exit_code"], _TESTS, ids=_TEST_IDS)
39+
class TestRelicCli(CommandTests):
40+
...

0 commit comments

Comments
 (0)