From a8ecf8b0566ad57149aad97215c7e93060bed289 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 1 Mar 2023 21:24:21 -0800 Subject: [PATCH] Group meson commands in preparation for other build systems --- README.md | 74 +++++++----- devpy/__main__.py | 63 ++++++---- devpy/cmds/__init__.py | 11 +- devpy/cmds/_build.py | 69 ----------- devpy/cmds/_shell.py | 77 ------------ devpy/cmds/_test.py | 48 -------- devpy/cmds/build.py | 9 ++ devpy/cmds/meson.build | 5 +- devpy/cmds/meson.py | 232 +++++++++++++++++++++++++++++++++++++ devpy/cmds/util.py | 52 --------- devpy/meson.build | 4 +- devpy/tests/meson.build | 1 - devpy/tests/test_util.py | 24 ++-- example_pkg/.devpy/cmds.py | 17 ++- example_pkg/pyproject.toml | 21 +++- 15 files changed, 379 insertions(+), 328 deletions(-) delete mode 100644 devpy/cmds/_build.py delete mode 100644 devpy/cmds/_shell.py delete mode 100644 devpy/cmds/_test.py create mode 100644 devpy/cmds/build.py create mode 100644 devpy/cmds/meson.py diff --git a/README.md b/README.md index b5269b8..5fb2ee6 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,42 @@ As an example, see the `[tool.devpy]` section of [an example `pyproject.toml`](h The `[tool.devpy]` section should contain: ``` -package = 'pkg_importname' # used by pytest -commands = ['devpy.build', 'devpy.test'] +package = "pkg_importname" # name of your package +commands = [ + "devpy.cmds.meson.build", + "devpy.cmds.meson.test" +] +``` + +### Command sections + +Once you have several commands, it may be useful to organize them into sections. +In `pyproject.toml`, instead of specifying the commands as a list, use the following structure: + +```toml +[tool.devpy.commands] +"Build" = [ + "devpy.cmds.meson.build", + "devpy.cmds.meson.test" +] +"Environments" = [ + "devpy.cmds.meson.shell", + "devpy.cmds.meson.ipython", + "devpy.cmds.meson.python" +] +``` + +These commands will then be rendered as: + +``` +Build: + build ๐Ÿ”ง Build package with Meson/ninja and install + test ๐Ÿ”ง Run tests + +Environments: + shell ๐Ÿ’ป Launch shell with PYTHONPATH set + ipython ๐Ÿ’ป Launch IPython shell with PYTHONPATH set + python ๐Ÿ Launch Python shell with PYTHONPATH set ``` ## Running @@ -45,12 +79,22 @@ On Unix-like systems, you can also copy the [`dev.py` script](https://github.com ## Built-in commands +### [Meson](https://meson-python.readthedocs.io) + ``` build ๐Ÿ”ง Build package with Meson/ninja and install to `build-install` ipython ๐Ÿ’ป Launch IPython shell with PYTHONPATH set python ๐Ÿ Launch Python shell with PYTHONPATH set shell ๐Ÿ’ป Launch shell with PYTHONPATH set - test ๐Ÿ”ง Run tests + test ๐Ÿ”ง Run pytest +``` + +### [Build](https://pypa-build.readthedocs.io/en/stable/) (PEP 517 builder) + +`devpy` was started with Meson in mind, but we're working on expanding commands for PEP 517 `build`. + +``` + sdist ๐Ÿ“ฆ Build a source distribution in `dist/`. ``` ## ๐Ÿงช Custom commands @@ -82,30 +126,6 @@ def example(): print(config["tool.devpy"]) ``` -### Command sections - -Once you have several commands, it may be useful to organize them into sections. -In `pyproject.toml`, instead of specifying the commands as a list, use the following structure: - -```toml -[tool.devpy.commands] -"Build" = ["devpy.build_meson", "devpy.test"] -"Environments" = ["devpy.shell", "devpy.ipython", "devpy.python"] -``` - -These commands will then be rendered as: - -``` -Build: - build ๐Ÿ”ง Build package with Meson/ninja and install - test ๐Ÿ”ง Run tests - -Environments: - shell ๐Ÿ’ป Launch shell with PYTHONPATH set - ipython ๐Ÿ’ป Launch IPython shell with PYTHONPATH set - python ๐Ÿ Launch Python shell with PYTHONPATH set -``` - ## History The `dev.py` tool was [proposed for SciPy](https://github.com/scipy/scipy/issues/15489) by Ralf Gommers and [implemented](https://github.com/scipy/scipy/pull/15959) by Sayantika Banik, Eduardo Naufel Schettino, and Ralf Gommers (also see [Sayantika's blog post](https://labs.quansight.org/blog/the-evolution-of-the-scipy-developer-cli)). diff --git a/devpy/__main__.py b/devpy/__main__.py index 76683df..72991fe 100644 --- a/devpy/__main__.py +++ b/devpy/__main__.py @@ -48,12 +48,6 @@ def __getitem__(self, key): print("No configuration found in [pyproject.toml] for [tool.devpy]") sys.exit(1) - commands = { - f"devpy.{name}": getattr(cmds, name) - for name in dir(cmds) - if not name.startswith("_") - } - proj_name = project_config.get("name", config["package"]) @click.group(help=f"Developer tool for {proj_name}", cls=SectionedHelpGroup) @@ -69,31 +63,54 @@ def group(ctx): if isinstance(config_cmds, list): config_cmds = {"Commands": config_cmds} + # Backward compatibility workaround + # Originally, you could specify any of these commands as `devpy.cmd` + # and we'd fetch it from util + commands = { + "devpy.build": cmds.meson.build, + "devpy.test": cmds.meson.test, + "devpy.ipython": cmds.meson.ipython, + "devpy.python": cmds.meson.python, + "devpy.shell": cmds.meson.shell, + } + for section, cmds in config_cmds.items(): for cmd in cmds: if cmd not in commands: - try: - path, func = cmd.split(":") - spec = importlib.util.spec_from_file_location("custom_mod", path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) + # First, see if we can directly import the command + if not ":" in cmd: + path, func = cmd.rsplit(".", maxsplit=1) + try: + mod = importlib.import_module(path) + except ImportError: + print( + f"!! Could not import module `{path}` to load command `{cmd}`" + ) + continue + else: try: - cmd_func = getattr(mod, func) - except AttributeError: + path, func = cmd.split(":") + spec = importlib.util.spec_from_file_location( + "custom_mod", path + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + except FileNotFoundError: print( - f"!! Could not load command `{func}` from file `{path}`.\n" + f"!! Could not find file `{path}` to load custom command `{cmd}`.\n" ) continue - except FileNotFoundError: - print( - f"!! Could not find file `{path}` to load custom command `{cmd}`.\n" - ) + except Exception as e: + print( + f"!! Could not import file `{path}` to load custom command `{cmd}`.\n" + ) + raise e + + try: + cmd_func = getattr(mod, func) + except AttributeError: + print(f"!! Could not load command `{func}` from file `{path}`.\n") continue - except Exception as e: - print( - f"!! Could not import file `{path}` to load custom command `{cmd}`.\n" - ) - raise e commands[cmd] = cmd_func diff --git a/devpy/cmds/__init__.py b/devpy/cmds/__init__.py index e8845ce..299b5e5 100644 --- a/devpy/cmds/__init__.py +++ b/devpy/cmds/__init__.py @@ -1,3 +1,8 @@ -from ._build import build -from ._test import test -from ._shell import ipython, python, shell +from . import meson + +# Backward compatibility with older versions +build = meson.build +test = meson.test +ipython = meson.ipython +python = meson.python +shell = meson.shell diff --git a/devpy/cmds/_build.py b/devpy/cmds/_build.py deleted file mode 100644 index f597cf0..0000000 --- a/devpy/cmds/_build.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import sys -import shutil -import click -from .util import run, install_dir - - -@click.command("build") -@click.option("-j", "--jobs", help="Number of parallel tasks to launch", type=int) -@click.option("--clean", is_flag=True, help="Clean build directory before build") -@click.option( - "-v", "--verbose", is_flag=True, help="Print all build output, even installation" -) -@click.argument("meson_args", nargs=-1) -def build_meson(meson_args, jobs=None, clean=False, verbose=False): - """๐Ÿ”ง Build package with Meson/ninja and install - - MESON_ARGS are passed through e.g.: - - ./dev.py build -- -Dpkg_config_path=/lib64/pkgconfig - - The package is installed to BUILD_DIR-install - - By default builds for release, to be able to use a debugger set CFLAGS - appropriately. For example, for linux use - - CFLAGS="-O0 -g" ./dev.py build - """ - build_dir = os.path.abspath("build") - build_cmd = ["meson", "setup", build_dir, "--prefix=/usr"] + list(meson_args) - flags = [] - - if clean: - print(f"Removing `{build_dir}`") - if os.path.isdir(build_dir): - shutil.rmtree(build_dir) - print(f"Removing `{install_dir}`") - if os.path.isdir(install_dir): - shutil.rmtree(install_dir) - - if os.path.exists(build_dir): - flags += ["--reconfigure"] - - p = run(build_cmd + flags, sys_exit=False) - if p.returncode != 0 and "--reconfigure" in flags: - click.confirm( - f"\nMeson failed; perhaps due to an invalid build tree. OK to remove `{build_dir}` and try again?", - abort=True, - ) - shutil.rmtree(build_dir) - run(build_cmd) - - run(["meson", "compile", "-C", build_dir]) - run( - [ - "meson", - "install", - "--only-changed", - "-C", - build_dir, - "--destdir", - f"../{install_dir}", - ], - output=verbose, - ) - - -# Alias for backward compatibility -build = build_meson diff --git a/devpy/cmds/_shell.py b/devpy/cmds/_shell.py deleted file mode 100644 index c883705..0000000 --- a/devpy/cmds/_shell.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -import sys -import copy - -import click - -from .util import run, get_config, set_pythonpath - - -@click.command() -@click.argument("ipython_args", nargs=-1) -def ipython(ipython_args): - """๐Ÿ’ป Launch IPython shell with PYTHONPATH set - - IPYTHON_ARGS are passed through directly to IPython, e.g.: - - ./dev.py ipython -- -i myscript.py - """ - p = set_pythonpath() - print(f'๐Ÿ’ป Launching IPython with PYTHONPATH="{p}"') - run(["ipython", "--ignore-cwd"] + list(ipython_args), replace=True) - - -@click.command() -@click.argument("shell_args", nargs=-1) -def shell(shell_args=[]): - """๐Ÿ’ป Launch shell with PYTHONPATH set - - SHELL_ARGS are passed through directly to the shell, e.g.: - - ./dev.py shell -- -c 'echo $PYTHONPATH' - - Ensure that your shell init file (e.g., ~/.zshrc) does not override - the PYTHONPATH. - """ - p = set_pythonpath() - shell = os.environ.get("SHELL", "sh") - cmd = [shell] + list(shell_args) - print(f'๐Ÿ’ป Launching shell with PYTHONPATH="{p}"') - print(f"โš  Change directory to avoid importing source instead of built package") - print(f"โš  Ensure that your ~/.shellrc does not unset PYTHONPATH") - run(cmd, replace=True) - - -@click.command() -@click.option( - "--build-dir", default="build", help="Build directory; default is `$PWD/build`" -) -@click.argument("python_args", nargs=-1) -def python(python_args): - """๐Ÿ Launch Python shell with PYTHONPATH set - - PYTHON_ARGS are passed through directly to Python, e.g.: - - ./dev.py python -- -c 'import sys; print(sys.path)' - """ - p = set_pythonpath() - v = sys.version_info - if (v.major < 3) or (v.major == 3 and v.minor < 11): - print("We're sorry, but this feature only works on Python 3.11 and greater ๐Ÿ˜ข") - print() - print( - "Why? Because we need the '-P' flag so the interpreter doesn't muck with PYTHONPATH" - ) - print() - print("However! You can still launch your own interpreter:") - print() - print(f" PYTHONPATH='{p}' python") - print() - print("And then call:") - print() - print("import sys; del(sys.path[0])") - sys.exit(-1) - - print(f'๐Ÿ Launching Python with PYTHONPATH="{p}"') - - run(["/usr/bin/env", "python", "-P"] + list(python_args), replace=True) diff --git a/devpy/cmds/_test.py b/devpy/cmds/_test.py deleted file mode 100644 index 3d5a420..0000000 --- a/devpy/cmds/_test.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import sys -import click - -from .util import run, get_config, get_commands, set_pythonpath, get_site_packages - - -@click.command() -@click.argument("pytest_args", nargs=-1) -@click.pass_context -def test(ctx, pytest_args): - """๐Ÿ”ง Run tests - - PYTEST_ARGS are passed through directly to pytest, e.g.: - - ./dev.py test -- -v - - By default, runs the full test suite. To skip "slow" tests, run - - ./dev.py test -- -m "not slow" - """ - cfg = get_config() - - command_groups = get_commands() - commands = [cmd for section in command_groups for cmd in command_groups[section]] - build_cmd = next((cmd for cmd in commands if cmd.name == "build"), None) - if build_cmd: - click.secho( - f"Invoking `build` prior to running tests:", bold=True, fg="bright_green" - ) - ctx.invoke(build_cmd) - - if not pytest_args: - pytest_args = (cfg.get("tool.devpy.package", None),) - if pytest_args == (None,): - print( - "Please specify `package = packagename` under `tool.devpy` section of `pyproject.toml`" - ) - sys.exit(1) - - site_path = get_site_packages() - set_pythonpath() - - print(f'$ export PYTHONPATH="{site_path}"') - run( - [sys.executable, "-m", "pytest", f"--rootdir={site_path}"] + list(pytest_args), - cwd=site_path, - ) diff --git a/devpy/cmds/build.py b/devpy/cmds/build.py new file mode 100644 index 0000000..57862bc --- /dev/null +++ b/devpy/cmds/build.py @@ -0,0 +1,9 @@ +import click + +from .util import run + + +@click.command() +def sdist(): + """๐Ÿ“ฆ Build a source distribution in `dist/`.""" + run(["python", "-m", "build", ".", "--sdist"]) diff --git a/devpy/cmds/meson.build b/devpy/cmds/meson.build index 1de32a8..9f7631e 100644 --- a/devpy/cmds/meson.build +++ b/devpy/cmds/meson.build @@ -1,8 +1,7 @@ python_sources = [ '__init__.py', - '_build.py', - '_shell.py', - '_test.py', + 'build.py', + 'meson.py', 'util.py' ] diff --git a/devpy/cmds/meson.py b/devpy/cmds/meson.py new file mode 100644 index 0000000..ec83810 --- /dev/null +++ b/devpy/cmds/meson.py @@ -0,0 +1,232 @@ +import os +import sys +import shutil + +import click + +from .util import run, get_config, get_commands + + +install_dir = "build-install" + + +def set_pythonpath(): + site_packages = get_site_packages() + env = os.environ + + if "PYTHONPATH" in env: + env["PYTHONPATH"] = f"{site_packages}{os.pathsep}{env['PYTHONPATH']}" + else: + env["PYTHONPATH"] = site_packages + + return env["PYTHONPATH"] + + +def get_site_packages(): + candidate_paths = [] + for root, dirs, files in os.walk(install_dir): + for subdir in dirs: + if subdir == "site-packages" or subdir == "dist-packages": + candidate_paths.append(os.path.abspath(os.path.join(root, subdir))) + + X, Y = sys.version_info.major, sys.version_info.minor + + site_packages = None + if any(f"python{X}." in p for p in candidate_paths): + # We have a system that uses `python3.X/site-packages` or `python3.X/dist-packages` + site_packages = [p for p in candidate_paths if f"python{X}.{Y}" in p] + if len(site_packages) == 0: + raise FileNotFoundError( + f"No site-packages found in {install_dir} for Python {X}.{Y}" + ) + else: + site_packages = site_packages[0] + else: + # A naming scheme that does not encode the Python major/minor version is used, so return + # whatever site-packages path was found + if len(candidate_paths) > 1: + raise FileNotFoundError( + f"Multiple `site-packages` found in `{install_dir}`, but cannot use Python version to disambiguate" + ) + elif len(candidate_paths) == 1: + site_packages = candidate_paths[0] + + if site_packages is None: + raise FileNotFoundError( + f"No `site-packages` or `dist-packages` found under `{install_dir}`" + ) + + return site_packages + + +@click.command() +@click.option("-j", "--jobs", help="Number of parallel tasks to launch", type=int) +@click.option("--clean", is_flag=True, help="Clean build directory before build") +@click.option( + "-v", "--verbose", is_flag=True, help="Print all build output, even installation" +) +@click.argument("meson_args", nargs=-1) +def build(meson_args, jobs=None, clean=False, verbose=False): + """๐Ÿ”ง Build package with Meson/ninja and install + + MESON_ARGS are passed through e.g.: + + ./dev.py build -- -Dpkg_config_path=/lib64/pkgconfig + + The package is installed to BUILD_DIR-install + + By default builds for release, to be able to use a debugger set CFLAGS + appropriately. For example, for linux use + + CFLAGS="-O0 -g" ./dev.py build + """ + build_dir = os.path.abspath("build") + build_cmd = ["meson", "setup", build_dir, "--prefix=/usr"] + list(meson_args) + flags = [] + + if clean: + print(f"Removing `{build_dir}`") + if os.path.isdir(build_dir): + shutil.rmtree(build_dir) + print(f"Removing `{install_dir}`") + if os.path.isdir(install_dir): + shutil.rmtree(install_dir) + + if os.path.exists(build_dir): + flags += ["--reconfigure"] + + p = run(build_cmd + flags, sys_exit=False) + if p.returncode != 0 and "--reconfigure" in flags: + click.confirm( + f"\nMeson failed; perhaps due to an invalid build tree. OK to remove `{build_dir}` and try again?", + abort=True, + ) + shutil.rmtree(build_dir) + run(build_cmd) + + run(["meson", "compile", "-C", build_dir]) + run( + [ + "meson", + "install", + "--only-changed", + "-C", + build_dir, + "--destdir", + f"../{install_dir}", + ], + output=verbose, + ) + + +@click.command() +@click.argument("pytest_args", nargs=-1) +@click.pass_context +def test(ctx, pytest_args): + """๐Ÿ”ง Run tests + + PYTEST_ARGS are passed through directly to pytest, e.g.: + + ./dev.py test -- -v + + By default, runs the full test suite. To skip "slow" tests, run + + ./dev.py test -- -m "not slow" + """ + cfg = get_config() + + command_groups = get_commands() + commands = [cmd for section in command_groups for cmd in command_groups[section]] + build_cmd = next((cmd for cmd in commands if cmd.name == "build"), None) + if build_cmd: + click.secho( + f"Invoking `build` prior to running tests:", bold=True, fg="bright_green" + ) + ctx.invoke(build_cmd) + + if not pytest_args: + pytest_args = (cfg.get("tool.devpy.package", None),) + if pytest_args == (None,): + print( + "Please specify `package = packagename` under `tool.devpy` section of `pyproject.toml`" + ) + sys.exit(1) + + site_path = get_site_packages() + set_pythonpath() + + print(f'$ export PYTHONPATH="{site_path}"') + run( + [sys.executable, "-m", "pytest", f"--rootdir={site_path}"] + list(pytest_args), + cwd=site_path, + ) + + +@click.command() +@click.argument("ipython_args", nargs=-1) +def ipython(ipython_args): + """๐Ÿ’ป Launch IPython shell with PYTHONPATH set + + IPYTHON_ARGS are passed through directly to IPython, e.g.: + + ./dev.py ipython -- -i myscript.py + """ + p = set_pythonpath() + print(f'๐Ÿ’ป Launching IPython with PYTHONPATH="{p}"') + run(["ipython", "--ignore-cwd"] + list(ipython_args), replace=True) + + +@click.command() +@click.argument("shell_args", nargs=-1) +def shell(shell_args=[]): + """๐Ÿ’ป Launch shell with PYTHONPATH set + + SHELL_ARGS are passed through directly to the shell, e.g.: + + ./dev.py shell -- -c 'echo $PYTHONPATH' + + Ensure that your shell init file (e.g., ~/.zshrc) does not override + the PYTHONPATH. + """ + p = set_pythonpath() + shell = os.environ.get("SHELL", "sh") + cmd = [shell] + list(shell_args) + print(f'๐Ÿ’ป Launching shell with PYTHONPATH="{p}"') + print(f"โš  Change directory to avoid importing source instead of built package") + print(f"โš  Ensure that your ~/.shellrc does not unset PYTHONPATH") + run(cmd, replace=True) + + +@click.command() +@click.option( + "--build-dir", default="build", help="Build directory; default is `$PWD/build`" +) +@click.argument("python_args", nargs=-1) +def python(python_args): + """๐Ÿ Launch Python shell with PYTHONPATH set + + PYTHON_ARGS are passed through directly to Python, e.g.: + + ./dev.py python -- -c 'import sys; print(sys.path)' + """ + p = set_pythonpath() + v = sys.version_info + if (v.major < 3) or (v.major == 3 and v.minor < 11): + print("We're sorry, but this feature only works on Python 3.11 and greater ๐Ÿ˜ข") + print() + print( + "Why? Because we need the '-P' flag so the interpreter doesn't muck with PYTHONPATH" + ) + print() + print("However! You can still launch your own interpreter:") + print() + print(f" PYTHONPATH='{p}' python") + print() + print("And then call:") + print() + print("import sys; del(sys.path[0])") + sys.exit(-1) + + print(f'๐Ÿ Launching Python with PYTHONPATH="{p}"') + + run(["/usr/bin/env", "python", "-P"] + list(python_args), replace=True) diff --git a/devpy/cmds/util.py b/devpy/cmds/util.py index e413403..99aab39 100644 --- a/devpy/cmds/util.py +++ b/devpy/cmds/util.py @@ -8,9 +8,6 @@ import click -install_dir = "build-install" - - def run(cmd, cwd=None, replace=False, sys_exit=True, output=True, *args, **kwargs): if cwd: click.secho(f"$ cd {cwd}", bold=True, fg="bright_blue") @@ -42,52 +39,3 @@ def get_config(): def get_commands(): return click.get_current_context().meta["commands"] - - -def get_site_packages(): - candidate_paths = [] - for root, dirs, files in os.walk(install_dir): - for subdir in dirs: - if subdir == "site-packages" or subdir == "dist-packages": - candidate_paths.append(os.path.abspath(os.path.join(root, subdir))) - - X, Y = sys.version_info.major, sys.version_info.minor - - site_packages = None - if any(f"python{X}." in p for p in candidate_paths): - # We have a system that uses `python3.X/site-packages` or `python3.X/dist-packages` - site_packages = [p for p in candidate_paths if f"python{X}.{Y}" in p] - if len(site_packages) == 0: - raise FileNotFoundError( - f"No site-packages found in {install_dir} for Python {X}.{Y}" - ) - else: - site_packages = site_packages[0] - else: - # A naming scheme that does not encode the Python major/minor version is used, so return - # whatever site-packages path was found - if len(candidate_paths) > 1: - raise FileNotFoundError( - f"Multiple `site-packages` found in `{install_dir}`, but cannot use Python version to disambiguate" - ) - elif len(candidate_paths) == 1: - site_packages = candidate_paths[0] - - if site_packages is None: - raise FileNotFoundError( - f"No `site-packages` or `dist-packages` found under `{install_dir}`" - ) - - return site_packages - - -def set_pythonpath(): - site_packages = get_site_packages() - env = os.environ - - if "PYTHONPATH" in env: - env["PYTHONPATH"] = f"{site_packages}{os.pathsep}{env['PYTHONPATH']}" - else: - env["PYTHONPATH"] = site_packages - - return env["PYTHONPATH"] diff --git a/devpy/meson.build b/devpy/meson.build index 34784c4..fdf45c8 100644 --- a/devpy/meson.build +++ b/devpy/meson.build @@ -1,6 +1,8 @@ python_sources = [ '__init__.py', - '__main__.py' + '__main__.py', + 'color_format.py', + 'sectioned_help.py' ] py.install_sources( diff --git a/devpy/tests/meson.build b/devpy/tests/meson.build index b2de356..169157b 100644 --- a/devpy/tests/meson.build +++ b/devpy/tests/meson.build @@ -1,5 +1,4 @@ python_sources = [ - 'test_builtin_cmds.py', 'test_util.py' ] diff --git a/devpy/tests/test_util.py b/devpy/tests/test_util.py index d9b1400..b38619d 100644 --- a/devpy/tests/test_util.py +++ b/devpy/tests/test_util.py @@ -5,7 +5,7 @@ import pytest -from devpy import util +from devpy.cmds import meson def make_paths(root, paths): @@ -22,7 +22,7 @@ def test_path_discovery(monkeypatch): with tempfile.TemporaryDirectory() as d: with monkeypatch.context() as m: install_dir = pjoin(d, "build-install") - m.setattr(util, "install_dir", install_dir) + m.setattr(meson, "install_dir", install_dir) make_paths( install_dir, @@ -34,14 +34,14 @@ def test_path_discovery(monkeypatch): ) assert ( normpath(f"/usr/lib64/python{X}.{Y}/site-packages") - in util.get_site_packages() + in meson.get_site_packages() ) # Debian uses dist-packages with tempfile.TemporaryDirectory() as d: with monkeypatch.context() as m: install_dir = pjoin(d, "build-install") - m.setattr(util, "install_dir", install_dir) + m.setattr(meson, "install_dir", install_dir) make_paths( install_dir, @@ -51,7 +51,7 @@ def test_path_discovery(monkeypatch): ) assert ( normpath(f"/usr/lib64/python{X}.{Y}/dist-packages") - in util.get_site_packages() + in meson.get_site_packages() ) # If there is no version information in site-packages, @@ -59,19 +59,19 @@ def test_path_discovery(monkeypatch): with tempfile.TemporaryDirectory() as d: with monkeypatch.context() as m: install_dir = pjoin(d, "build-install") - m.setattr(util, "install_dir", install_dir) + m.setattr(meson, "install_dir", install_dir) make_paths(install_dir, ["/Python3/site-packages"]) - assert normpath("/Python3/site-packages") in util.get_site_packages() + assert normpath("/Python3/site-packages") in meson.get_site_packages() # Raise if no site-package directory present with tempfile.TemporaryDirectory() as d: with monkeypatch.context() as m: install_dir = pjoin(d, "build-install") - m.setattr(util, "install_dir", install_dir) + m.setattr(meson, "install_dir", install_dir) with pytest.raises(FileNotFoundError): - util.get_site_packages() + meson.get_site_packages() # If there are multiple site-package paths, but without version information, # refuse the temptation to guess @@ -81,13 +81,13 @@ def test_path_discovery(monkeypatch): install_dir, [f"/Python3/x/site-packages", f"/Python3/y/site-packages"] ) with pytest.raises(FileNotFoundError): - util.get_site_packages() + meson.get_site_packages() # Multiple site-package paths found, but none that matches our Python with tempfile.TemporaryDirectory() as d: with monkeypatch.context() as m: install_dir = pjoin(d, "build-install") - m.setattr(util, "install_dir", install_dir) + m.setattr(meson, "install_dir", install_dir) make_paths( install_dir, @@ -97,4 +97,4 @@ def test_path_discovery(monkeypatch): ], ) with pytest.raises(FileNotFoundError): - util.get_site_packages() + meson.get_site_packages() diff --git a/example_pkg/.devpy/cmds.py b/example_pkg/.devpy/cmds.py index f790c16..17300df 100644 --- a/example_pkg/.devpy/cmds.py +++ b/example_pkg/.devpy/cmds.py @@ -1,3 +1,4 @@ +import json import click from devpy import util @@ -10,14 +11,12 @@ def example(flag): Accepts arbitrary flags, and shows how to access `pyproject.toml` config. """ - print("Running example custom command") + click.secho("Running example custom command", bold=True, fg="bright_blue") + print() config = util.get_config() - print("Flag provided is:", flag) - print("Tool config is:") - print(config["tool.devpy"]) + commands = util.get_commands() + click.secho("Flag provided with --flag is: ", fg="yellow", nl=False) + print(flag or None) - -@click.command() -def sdist(): - """๐Ÿ“ฆ Build a source distribution in `dist/`.""" - util.run(["python", "-m", "build", ".", "--sdist"]) + click.secho("\nTool config is:", fg="yellow") + print(json.dumps(config["tool.devpy"], indent=2)) diff --git a/example_pkg/pyproject.toml b/example_pkg/pyproject.toml index c22f9e9..0824fdf 100644 --- a/example_pkg/pyproject.toml +++ b/example_pkg/pyproject.toml @@ -16,8 +16,23 @@ package = 'example_pkg' [tool.devpy.commands] # If you don't need sections, you can also provide a list of commands under [tool.devpy]: # -# commands = ["devpy.build", "devpy.test", "devpy.shell", "devpy.ipython", "devpy.python", "custom/cmds.py:example"] +# commands = [ +# "devpy.cmds.meson.build", +# "devpy.cmds.meson.test", +# "devpy.cmds.meson.shell", +# "devpy.cmds.meson.ipython", +# "devpy.cmds.meson.python", +# "custom/cmds.py:example" +# ] -"Build" = ["devpy.build", "devpy.test", ".devpy/cmds.py:sdist"] -"Environments" = ["devpy.shell", "devpy.ipython", "devpy.python"] +"Build" = [ + "devpy.cmds.meson.build", + "devpy.cmds.meson.test", + "devpy.cmds.build.sdist" +] +"Environments" = [ + "devpy.cmds.meson.shell", + "devpy.cmds.meson.ipython", + "devpy.cmds.meson.python" +] "Extensions" = [".devpy/cmds.py:example"]