diff --git a/poetry.lock b/poetry.lock index 351e76cf..f3f15b36 100644 --- a/poetry.lock +++ b/poetry.lock @@ -193,7 +193,7 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.1" +version = "8.0.3" description = "Composable command line interface toolkit" category = "dev" optional = false @@ -1206,7 +1206,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "5549883a46cb4aa2d993a219229ca89255dce3f6b10fbc5b86cc86c1f15893f8" +content-hash = "b85bebf7a796b0b37e647fd7289589ea03a147c155839dfd1bcd561ead1fde92" [metadata.files] aiodns = [ @@ -1409,8 +1409,8 @@ charset-normalizer = [ {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"}, ] click = [ - {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, - {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] codecov = [ {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, diff --git a/pyproject.toml b/pyproject.toml index 5281ba95..245c8fad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,8 @@ pytest-xdist = { version = "^2.3.0", extras = ["psutil"] } mkdocs = ">=1.1.2,<2.0.0" mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" +# Scripts +click = "^8.0.3" [build-system] requires = ["poetry-core>=1.0.0"] @@ -88,5 +90,6 @@ flake8 = { cmd = "python -m flake8", help = "Lints code with flake8" } lint = { cmd = "pre-commit run --all-files", help = "Checks all files for CI errors" } precommit = { cmd = "pre-commit install --install-hooks", help = "Installs the precommit hook" } report = { cmd = "coverage report", help = "Show coverage report from previously run tests." } +scripts = { cmd = 'python -m scripts', help = 'Run the scripts wrapper cli.' } test = { cmd = "pytest -n auto --dist loadfile", help = "Runs tests and save results to a coverage report" } test_mocks = { cmd = 'pytest tests/test_mocks.py', help = 'Runs the tests on the mock files. They are excluded from the main test suite.' } diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/__main__.py b/scripts/__main__.py new file mode 100644 index 00000000..9e09447a --- /dev/null +++ b/scripts/__main__.py @@ -0,0 +1,68 @@ +""" +Script wrapper for scripts. + +This allows scripts to be invoked through the scripts module. + +The exposed interface is just running the internal files. +Whatever interface they have, is what is shown. +""" + +import functools +import runpy +import sys + +import click + + +# key: alias +# value: tuple of module name, help description +commands: "dict[str, tuple[str, str | None]]" = { + "export_req": ("scripts.export_requirements", "Export requirements to requirements.txt"), +} + + +def run_script(module_name: str, *args, **kwargs) -> None: + """ + Run the provided module, with the provided args and kwargs. + + The provided defaults are what makes the environment nearly the same as if module was invoked directly. + """ + kwargs.setdefault("run_name", "__main__") + kwargs.setdefault("alter_sys", True) + runpy.run_module(module_name, **kwargs) + + +@click.group() +@click.help_option("-h", "--help") +def cli() -> None: + """ + Custom scripts which help modmail development. + + All custom scripts should be listed below as a command, with a description. + In addition, some built in modules may be listed below as well. + If a custom script is not shown below please open an issue. + """ + pass + + +def main(cmd: str = None) -> None: + """Add the commands and run the cli.""" + if cmd is None: + cmd = [] + for k, v in commands.items(): + func = functools.partial(run_script, v[0]) + cli.add_command(click.Command(k, help=v[1], callback=func)) + cli.main(cmd, standalone_mode=False) + + +if __name__ == "__main__": + try: + cmd = [sys.argv[1]] + sys.argv.pop(1) # pop the first arg out of sys.argv since its being used to get the name + except IndexError: + cmd = [] + try: + main(cmd) + except click.ClickException as e: + e.show() + sys.exit()