diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fa4e9e..36abf67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,11 +40,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.11", "3.12"] + python-version: ["3.8", "3.12"] runs-on: [ubuntu-latest, macos-latest, windows-latest] include: - - python-version: pypy-3.9 + - python-version: pypy-3.10 runs-on: ubuntu-latest steps: diff --git a/docs/index.md b/docs/index.md index 2337fab..7cac16f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,7 @@ dynamic_metadata ``` ```{eval-rst} -.. click:: scikit_build_core.cli.main:skbuild +.. click:: scikit_build_core.cli.__main__:skbuild :prog: skbuild ``` diff --git a/pyproject.toml b/pyproject.toml index f2dfb26..da0293b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ Discussions = "https://github.com/scikit-build/scikit-build-cli/discussions" Changelog = "https://github.com/scikit-build/scikit-build-cli/releases" [project.scripts] -skbuild = "scikit_build_cli.__main__:run_cli" +skbuild = "scikit_build_cli.__main__:skbuild" [project.entry-points."skbuild.commands"] build = "scikit_build_cli.commands.build:build" @@ -67,6 +67,8 @@ configure = "scikit_build_cli.commands.configure:configure" dynamic-metadata = "scikit_build_cli.commands.dynamic_metadata:dynamic_metadata" metadata = "scikit_build_cli.commands.metadata:metadata" install = "scikit_build_cli.commands.install:install" +new = "scikit_build_cli.commands.new:new" +init = "scikit_build_cli.commands.init:init" [tool.hatch] version.source = "vcs" diff --git a/src/scikit_build_cli/__init__.py b/src/scikit_build_cli/__init__.py index fa03fc1..da53058 100644 --- a/src/scikit_build_cli/__init__.py +++ b/src/scikit_build_cli/__init__.py @@ -2,8 +2,4 @@ from ._version import version as __version__ -# Load all subcommands -# Load and expose the main CLI interface -from .main import skbuild - -__all__: list[str] = ["skbuild", "__version__"] +__all__: list[str] = ["__version__"] diff --git a/src/scikit_build_cli/__main__.py b/src/scikit_build_cli/__main__.py index e7ff84e..1a734ae 100644 --- a/src/scikit_build_cli/__main__.py +++ b/src/scikit_build_cli/__main__.py @@ -1,14 +1,83 @@ from __future__ import annotations -from . import skbuild +import pathlib +from collections.abc import MutableMapping, Sequence +from importlib.metadata import EntryPoint +import click -def run_cli() -> None: +from . import __version__ +from ._compat.importlib import metadata + +__all__ = ["skbuild"] + + +def __dir__() -> list[str]: + return __all__ + + +class LazyGroup(click.Group): """ - Entry point to skbuild command. + Lazy loader for click commands. Based on Click's documentation, but uses + EntryPoints. """ - skbuild() + + def __init__( + self, + name: str | None = None, + commands: MutableMapping[str, click.Command] + | Sequence[click.Command] + | None = None, + *, + lazy_subcommands: Sequence[EntryPoint] = (), + **kwargs: object, + ): + super().__init__(name, commands, **kwargs) + self.lazy_subcommands = {v.name: v for v in lazy_subcommands} + + def list_commands(self, ctx: click.Context) -> list[str]: + return sorted([*super().list_commands(ctx), *self.lazy_subcommands]) + + def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: + if cmd_name in self.lazy_subcommands: + return self._lazy_load(cmd_name) + return super().get_command(ctx, cmd_name) + + def _lazy_load(self, cmd_name: str) -> click.Command: + ep = self.lazy_subcommands[cmd_name] + cmd_object = ep.load() + if not isinstance(cmd_object, click.Command): + msg = f"Lazy loading of {ep} failed by returning a non-command object" + raise ValueError(msg) + return cmd_object + + +# Add all plugin commands. +CMDS = list(metadata.entry_points(group="skbuild.commands")) + + +@click.group("skbuild", cls=LazyGroup, lazy_subcommands=CMDS) +@click.version_option(__version__) +@click.help_option("--help", "-h") +@click.option( + "--root", + "-r", + type=click.Path( + exists=True, + file_okay=False, + dir_okay=True, + writable=True, + path_type=pathlib.Path, + ), + help="Path to the Python project's root", +) +@click.pass_context +def skbuild(ctx: click.Context, root: pathlib.Path) -> None: # noqa: ARG001 + """ + scikit-build Main CLI interface + """ + # TODO: Add specific implementations if __name__ == "__main__": - run_cli() + skbuild() diff --git a/src/scikit_build_cli/commands/build.py b/src/scikit_build_cli/commands/build.py index d44bc00..11218e0 100644 --- a/src/scikit_build_cli/commands/build.py +++ b/src/scikit_build_cli/commands/build.py @@ -18,9 +18,10 @@ def __dir__() -> list[str]: @click.command() @_build_dir +@click.help_option("--help", "-h") @click.pass_context def build(ctx: click.Context, build_dir: Path) -> None: # noqa: ARG001 """ - Run cmake build step + Run CMake build step """ # TODO: Add specific implementations diff --git a/src/scikit_build_cli/commands/configure.py b/src/scikit_build_cli/commands/configure.py index d2b08e3..9dec85f 100644 --- a/src/scikit_build_cli/commands/configure.py +++ b/src/scikit_build_cli/commands/configure.py @@ -18,9 +18,10 @@ def __dir__() -> list[str]: @click.command() @_build_dir +@click.help_option("--help", "-h") @click.pass_context def configure(ctx: click.Context, build_dir: Path) -> None: # noqa: ARG001 """ - Run cmake configure step + Run CMake configure step """ # TODO: Add specific implementations diff --git a/src/scikit_build_cli/commands/dynamic_metadata.py b/src/scikit_build_cli/commands/dynamic_metadata.py index 7833a36..2b1d52a 100644 --- a/src/scikit_build_cli/commands/dynamic_metadata.py +++ b/src/scikit_build_cli/commands/dynamic_metadata.py @@ -10,6 +10,7 @@ def __dir__() -> list[str]: @click.command() +@click.help_option("--help", "-h") @click.pass_context def dynamic_metadata(ctx: click.Context) -> None: # noqa: ARG001 """ diff --git a/src/scikit_build_cli/commands/init.py b/src/scikit_build_cli/commands/init.py new file mode 100644 index 0000000..e4fbbbb --- /dev/null +++ b/src/scikit_build_cli/commands/init.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +import click + +__all__: list[str] = ["init"] + + +def __dir__() -> list[str]: + return __all__ + + +@click.command() +@click.help_option("--help", "-h") +@click.pass_context +def init(ctx: click.Context) -> None: # noqa: ARG001 + """ + Add scikit-build to an existing project + """ + # TODO: Add specific implementations diff --git a/src/scikit_build_cli/commands/install.py b/src/scikit_build_cli/commands/install.py index a3f4b59..2da4ddf 100644 --- a/src/scikit_build_cli/commands/install.py +++ b/src/scikit_build_cli/commands/install.py @@ -18,9 +18,10 @@ def __dir__() -> list[str]: @click.command() @_build_dir +@click.help_option("--help", "-h") @click.pass_context def install(ctx: click.Context, build_dir: Path) -> None: # noqa: ARG001 """ - Run cmake install step + Run CMake install step """ # TODO: Add specific implementations diff --git a/src/scikit_build_cli/commands/metadata.py b/src/scikit_build_cli/commands/metadata.py index cb645e6..a8b2055 100644 --- a/src/scikit_build_cli/commands/metadata.py +++ b/src/scikit_build_cli/commands/metadata.py @@ -10,6 +10,7 @@ def __dir__() -> list[str]: @click.command() +@click.help_option("--help", "-h") @click.pass_context def metadata(ctx: click.Context) -> None: # noqa: ARG001 """ diff --git a/src/scikit_build_cli/commands/new.py b/src/scikit_build_cli/commands/new.py new file mode 100644 index 0000000..0d148b5 --- /dev/null +++ b/src/scikit_build_cli/commands/new.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +import click + +__all__: list[str] = ["new"] + + +def __dir__() -> list[str]: + return __all__ + + +@click.command() +@click.help_option("--help", "-h") +@click.pass_context +def new(ctx: click.Context) -> None: # noqa: ARG001 + """ + Start a new project + """ + # TODO: Add specific implementations diff --git a/src/scikit_build_cli/main.py b/src/scikit_build_cli/main.py deleted file mode 100644 index 8e52462..0000000 --- a/src/scikit_build_cli/main.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -import pathlib -from collections.abc import MutableMapping, Sequence -from importlib.metadata import EntryPoint - -import click - -from . import __version__ -from ._compat.importlib import metadata - -__all__ = ["skbuild"] - - -def __dir__() -> list[str]: - return __all__ - - -class LazyGroup(click.Group): - """ - Lazy loader for click commands. Based on Click's documentation, but uses - EntryPoints. - """ - - def __init__( - self, - name: str | None = None, - commands: MutableMapping[str, click.Command] - | Sequence[click.Command] - | None = None, - *, - lazy_subcommands: Sequence[EntryPoint] = (), - **kwargs: object, - ): - super().__init__(name, commands, **kwargs) - self.lazy_subcommands = {v.name: v for v in lazy_subcommands} - - def list_commands(self, ctx: click.Context) -> list[str]: - return sorted([*super().list_commands(ctx), *self.lazy_subcommands]) - - def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: - if cmd_name in self.lazy_subcommands: - return self._lazy_load(cmd_name) - return super().get_command(ctx, cmd_name) - - def _lazy_load(self, cmd_name: str) -> click.Command: - ep = self.lazy_subcommands[cmd_name] - cmd_object = ep.load() - if not isinstance(cmd_object, click.Command): - msg = f"Lazy loading of {ep} failed by returning a non-command object" - raise ValueError(msg) - return cmd_object - - -# Add all plugin commands. -CMDS = list(metadata.entry_points(group="skbuild.commands")) - - -@click.group("skbuild", cls=LazyGroup, lazy_subcommands=CMDS) -@click.version_option(__version__) -@click.option( - "--root", - "-r", - type=click.Path( - exists=True, - file_okay=False, - dir_okay=True, - writable=True, - path_type=pathlib.Path, - ), - help="Path to the Python project's root", -) -@click.pass_context -def skbuild(ctx: click.Context, root: pathlib.Path) -> None: # noqa: ARG001 - """ - scikit-build Main CLI interface - """ - # TODO: Add specific implementations diff --git a/tests/test_package.py b/tests/test_package.py index 6177a93..c0a6161 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -2,8 +2,28 @@ import importlib.metadata -import scikit_build_cli as m +import pytest +from click.testing import CliRunner +import scikit_build_cli +import scikit_build_cli.__main__ -def test_version(): - assert importlib.metadata.version("scikit_build_cli") == m.__version__ + +def test_version() -> None: + assert ( + importlib.metadata.version("scikit_build_cli") == scikit_build_cli.__version__ + ) + + +@pytest.mark.parametrize("flag", ["--help", "-h"]) +def test_help_text(flag: str) -> None: + runner = CliRunner() + result = runner.invoke(scikit_build_cli.__main__.skbuild, [flag]) + assert result.exit_code == 0 + assert "Run CMake build step" in result.output + assert "Run CMake configure step" in result.output + assert "Get the generated dynamic metadata" in result.output + assert "Run CMake install step" in result.output + assert "Write out the project's metadata" in result.output + assert "Start a new project" in result.output + assert "Add scikit-build to an existing project" in result.output