From f2fb1be8158b2a238b0618edfcbd98ba86a17b2d Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sat, 14 Oct 2023 16:34:32 -0700 Subject: [PATCH 1/7] Create cli.py --- src/relic/sga/core/cli.py | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/relic/sga/core/cli.py diff --git a/src/relic/sga/core/cli.py b/src/relic/sga/core/cli.py new file mode 100644 index 0000000..85dc335 --- /dev/null +++ b/src/relic/sga/core/cli.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from argparse import ArgumentParser, Namespace +from typing import Optional + +from relic.core.cli import CliPluginGroup, _SubParsersAction, CliPlugin + + +class RelicSgaCli(CliPluginGroup): + GROUP = "relic.cli.sga" + + def __init__(self, parent: _SubParsersAction, **kwargs): + super().__init__(parent, **kwargs) + + def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + if command_group is None: + return ArgumentParser("sga") + else: + return command_group.add_parser("sga") + + +class RelicSgaUnpackCli(CliPlugin): + + def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + parser: ArgumentParser + if command_group is None: + parser = ArgumentParser("unpack") + else: + parser = command_group.add_parser("unpack") + + # TODO populate parser + + return parser + + def command(self, ns: Namespace) -> Optional[int]: + raise NotImplementedError + + +class RelicSgaPackCli(CliPlugin): + + def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + parser: ArgumentParser + if command_group is None: + parser = ArgumentParser("pack") + else: + parser = command_group.add_parser("pack") + + # TODO populate parser + + return parser + + def command(self, ns: Namespace) -> Optional[int]: + raise NotImplementedError From b94780b023384c28f9395f939098b891de5e0946 Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sat, 14 Oct 2023 16:34:46 -0700 Subject: [PATCH 2/7] Add CLI entry points --- setup.cfg | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup.cfg b/setup.cfg index 2cc5042..05a4fac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,12 @@ install_requires = fs.opener = sga = relic.sga.core.filesystem:EssenceFSOpener +relic.cli = + sga = relic.sga.core.cli:RelicSgaCli + +relic.cli.sga = + unpack = relic.sga.core.cli:RelicSgaUnpackCli + pack = relic.sga.core.cli:RelicSgaPackCli [options.packages.find] where = src \ No newline at end of file From 5c9440f734f683cbc88f0ab44440bae855583201 Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sun, 15 Oct 2023 00:06:02 -0700 Subject: [PATCH 3/7] Pack / Unpack Logic Pack logic belongs to the individual plugins Unpack also delegates to plugins, but can be done so automatically thanks to PyFilesystem --- src/relic/sga/core/cli.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/relic/sga/core/cli.py b/src/relic/sga/core/cli.py index 85dc335..0f1ca42 100644 --- a/src/relic/sga/core/cli.py +++ b/src/relic/sga/core/cli.py @@ -3,15 +3,14 @@ from argparse import ArgumentParser, Namespace from typing import Optional +import fs.copy +from fs.base import FS from relic.core.cli import CliPluginGroup, _SubParsersAction, CliPlugin class RelicSgaCli(CliPluginGroup): GROUP = "relic.cli.sga" - def __init__(self, parent: _SubParsersAction, **kwargs): - super().__init__(parent, **kwargs) - def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: if command_group is None: return ArgumentParser("sga") @@ -28,15 +27,27 @@ def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> A else: parser = command_group.add_parser("unpack") - # TODO populate parser + parser.add_argument("src_sga", type=str, help="Source SGA File") + parser.add_argument("out_dir", type=str, help="Output Directory") return parser def command(self, ns: Namespace) -> Optional[int]: - raise NotImplementedError + infile: str = ns.src_sga + outdir: str = ns.out_dir + + print(f"Unpacking `{infile}`") + + def _callback(_1: FS, srcfile: str, _2: FS, _3: str): + print(f"\t\tUnpacking File `{srcfile}`") + fs.copy.copy_fs(f"sga://{infile}", f"osfs://{outdir}", on_copy=_callback) -class RelicSgaPackCli(CliPlugin): + return None # To shut-up mypy + + +class RelicSgaPackCli(CliPluginGroup): + GROUP = "relic.cli.sga.pack" def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: parser: ArgumentParser @@ -45,9 +56,7 @@ def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> A else: parser = command_group.add_parser("pack") - # TODO populate parser + # pack further delegates to version plugins return parser - def command(self, ns: Namespace) -> Optional[int]: - raise NotImplementedError From 0bf114d1f3aff97d53e56ecb39d3d4d1e9a493aa Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sun, 15 Oct 2023 00:06:16 -0700 Subject: [PATCH 4/7] Specify relic-tool-core version --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 05a4fac..aface05 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,6 +13,7 @@ classifiers = Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 License :: OSI Approved :: GNU General Public License v3 (GPLv3) [options] @@ -24,7 +25,7 @@ python_requires = >=3.9 install_requires = mak-serialization-tools >= 2022.0a19 - relic-tool-core + relic-tool-core >= 1.1.1 fs [options.entry_points] From fcf701077e98fe5508efe57b8e868cd27bb5df06 Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sun, 15 Oct 2023 00:06:53 -0700 Subject: [PATCH 5/7] Add tests for CLI currently just ensures they exist, since without an SGA version we cant test functionality --- tests/test_cli.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_cli.py diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..7c2305b --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,42 @@ +import io +import subprocess +# Local testing requires running `pip install -e "."` +from contextlib import redirect_stdout +from typing import Sequence + +import pytest + + +class CommandTests: + def test_run(self, args: Sequence[str], output: str, exit_code: int): + _args = ["relic", *args] + cmd = subprocess.run(_args, capture_output=True, text=True) + result = cmd.stdout + status = cmd.returncode + print(f"'{result}'") # Visual Aid for Debugging + assert output in result + assert status == exit_code + + def test_run_with(self, args: Sequence[str], output: str, exit_code: int): + from relic.core.cli import cli_root + with io.StringIO() as f: + with redirect_stdout(f): + status = cli_root.run_with(*args) + f.seek(0) + result = f.read() + print(f"'{result}'") # Visual Aid for Debugging + assert output in result + assert status == exit_code + + +_SGA_HELP = ["sga", "-h"], """usage: relic sga [-h] {pack,unpack} ...""", 0 +_SGA_PACK_HELP = ["sga", "pack", "-h"], """usage: relic sga pack [-h] {} ...""", 0 +_SGA_UNPACK_HELP = ["sga", "unpack", "-h"], """usage: relic sga unpack [-h]""", 0 + +_TESTS = [_SGA_HELP, _SGA_PACK_HELP, _SGA_UNPACK_HELP] +_TEST_IDS = [' '.join(_[0]) for _ in _TESTS] + + +@pytest.mark.parametrize(["args", "output", "exit_code"], _TESTS, ids=_TEST_IDS) +class TestRelicSgaCli(CommandTests): + ... From 50a462b20a34be2c5fe419549f750e3c94bffa15 Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sun, 15 Oct 2023 00:07:00 -0700 Subject: [PATCH 6/7] Bump to 1.1.0 --- src/relic/sga/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/relic/sga/core/__init__.py b/src/relic/sga/core/__init__.py index d31364f..2f6604a 100644 --- a/src/relic/sga/core/__init__.py +++ b/src/relic/sga/core/__init__.py @@ -3,4 +3,4 @@ """ from relic.sga.core.definitions import Version, MagicWord, StorageType, VerificationType -__version__ = "1.0.0" +__version__ = "1.1.0" From 4866ee5b8d8d8758024ad9cce8dd0319f1d4baae Mon Sep 17 00:00:00 2001 From: Marcus Kertesz Date: Sun, 15 Oct 2023 00:27:22 -0700 Subject: [PATCH 7/7] MyPy / Black fixes --- src/relic/sga/core/cli.py | 16 ++++++++++------ tests/test_cli.py | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/relic/sga/core/cli.py b/src/relic/sga/core/cli.py index 0f1ca42..a4b7423 100644 --- a/src/relic/sga/core/cli.py +++ b/src/relic/sga/core/cli.py @@ -11,7 +11,9 @@ class RelicSgaCli(CliPluginGroup): GROUP = "relic.cli.sga" - def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + def _create_parser( + self, command_group: Optional[_SubParsersAction] = None + ) -> ArgumentParser: if command_group is None: return ArgumentParser("sga") else: @@ -19,8 +21,9 @@ def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> A class RelicSgaUnpackCli(CliPlugin): - - def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + def _create_parser( + self, command_group: Optional[_SubParsersAction] = None + ) -> ArgumentParser: parser: ArgumentParser if command_group is None: parser = ArgumentParser("unpack") @@ -38,7 +41,7 @@ def command(self, ns: Namespace) -> Optional[int]: print(f"Unpacking `{infile}`") - def _callback(_1: FS, srcfile: str, _2: FS, _3: str): + def _callback(_1: FS, srcfile: str, _2: FS, _3: str) -> None: print(f"\t\tUnpacking File `{srcfile}`") fs.copy.copy_fs(f"sga://{infile}", f"osfs://{outdir}", on_copy=_callback) @@ -49,7 +52,9 @@ def _callback(_1: FS, srcfile: str, _2: FS, _3: str): class RelicSgaPackCli(CliPluginGroup): GROUP = "relic.cli.sga.pack" - def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> ArgumentParser: + def _create_parser( + self, command_group: Optional[_SubParsersAction] = None + ) -> ArgumentParser: parser: ArgumentParser if command_group is None: parser = ArgumentParser("pack") @@ -59,4 +64,3 @@ def _create_parser(self, command_group: Optional[_SubParsersAction] = None) -> A # pack further delegates to version plugins return parser - diff --git a/tests/test_cli.py b/tests/test_cli.py index 7c2305b..5bce432 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,5 +1,6 @@ import io import subprocess + # Local testing requires running `pip install -e "."` from contextlib import redirect_stdout from typing import Sequence @@ -19,6 +20,7 @@ def test_run(self, args: Sequence[str], output: str, exit_code: int): def test_run_with(self, args: Sequence[str], output: str, exit_code: int): from relic.core.cli import cli_root + with io.StringIO() as f: with redirect_stdout(f): status = cli_root.run_with(*args) @@ -34,7 +36,7 @@ def test_run_with(self, args: Sequence[str], output: str, exit_code: int): _SGA_UNPACK_HELP = ["sga", "unpack", "-h"], """usage: relic sga unpack [-h]""", 0 _TESTS = [_SGA_HELP, _SGA_PACK_HELP, _SGA_UNPACK_HELP] -_TEST_IDS = [' '.join(_[0]) for _ in _TESTS] +_TEST_IDS = [" ".join(_[0]) for _ in _TESTS] @pytest.mark.parametrize(["args", "output", "exit_code"], _TESTS, ids=_TEST_IDS)