diff options
-rw-r--r-- | src/setuptools_scm/_cli.py | 21 | ||||
-rw-r--r-- | src/setuptools_scm/config.py | 35 | ||||
-rw-r--r-- | testing/conftest.py | 26 | ||||
-rw-r--r-- | testing/test_cli.py | 50 | ||||
-rw-r--r-- | testing/test_git.py | 7 |
5 files changed, 115 insertions, 24 deletions
diff --git a/src/setuptools_scm/_cli.py b/src/setuptools_scm/_cli.py index 88dd6d0..4883c4f 100644 --- a/src/setuptools_scm/_cli.py +++ b/src/setuptools_scm/_cli.py @@ -10,15 +10,15 @@ from setuptools_scm.discover import walk_potential_roots from setuptools_scm.integration import find_files -def main() -> None: - opts = _get_cli_opts() - root = opts.root or "." +def main(args: list[str] | None = None) -> None: + opts = _get_cli_opts(args) + inferred_root: str = opts.root or "." - pyproject = opts.config or _find_pyproject(root) + pyproject = opts.config or _find_pyproject(inferred_root) try: - root = opts.root or os.path.relpath(os.path.dirname(pyproject)) - config = Configuration.from_file(pyproject, root=root) + + config = Configuration.from_file(pyproject, root=opts.root) except (LookupError, FileNotFoundError) as ex: # no pyproject.toml OR no [tool.setuptools_scm] print( @@ -27,10 +27,11 @@ def main() -> None: f" Reason: {ex}.", file=sys.stderr, ) - config = Configuration(root=root) + config = Configuration(inferred_root) version = _get_version(config) - assert version is not None + if version is None: + raise SystemExit("ERROR: no version found for", opts) if opts.strip_dev: version = version.partition(".dev")[0] print(version) @@ -40,7 +41,7 @@ def main() -> None: print(fname) -def _get_cli_opts() -> argparse.Namespace: +def _get_cli_opts(args: list[str] | None) -> argparse.Namespace: prog = "python -m setuptools_scm" desc = "Print project version according to SCM metadata" parser = argparse.ArgumentParser(prog, description=desc) @@ -68,7 +69,7 @@ def _get_cli_opts() -> argparse.Namespace: # We avoid `metavar` to prevent printing repetitive information desc = "List files managed by the SCM" sub.add_parser("ls", help=desc[0].lower() + desc[1:], description=desc) - return parser.parse_args() + return parser.parse_args(args) def _find_pyproject(parent: str) -> str: diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index f73b905..fee652c 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -24,6 +24,7 @@ if TYPE_CHECKING: DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" DEFAULT_VERSION_SCHEME = "guess-next-dev" DEFAULT_LOCAL_SCHEME = "node-and-date" +_ROOT = "root" def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]: @@ -213,6 +214,7 @@ class Configuration: with open(name, encoding="UTF-8") as strm: data = strm.read() + defn = _load_toml(data) try: section = defn.get("tool", {})["setuptools_scm"] @@ -220,6 +222,21 @@ class Configuration: raise LookupError( f"{name} does not contain a tool.setuptools_scm section" ) from e + + project = defn.get("project", {}) + dist_name = cls._cleanup_from_file_args_data( + project, dist_name, kwargs, section + ) + return cls(dist_name=dist_name, relative_to=name, **section, **kwargs) + + @staticmethod + def _cleanup_from_file_args_data( + project: dict[str, Any], + dist_name: str | None, + kwargs: dict[str, Any], + section: dict[str, Any], + ) -> str | None: + """drops problematic details and figures the distribution name""" if "dist_name" in section: if dist_name is None: dist_name = section.pop("dist_name") @@ -227,13 +244,21 @@ class Configuration: assert dist_name == section["dist_name"] del section["dist_name"] if dist_name is None: - if "project" in defn: - # minimal pep 621 support for figuring the pretend keys - dist_name = defn["project"].get("name") + # minimal pep 621 support for figuring the pretend keys + dist_name = project.get("name") if dist_name is None: dist_name = _read_dist_name_from_setup_cfg() - - return cls(dist_name=dist_name, **section, **kwargs) + if _ROOT in kwargs: + if kwargs[_ROOT] is None: + kwargs.pop(_ROOT, None) + elif _ROOT in section: + if section[_ROOT] != kwargs[_ROOT]: + warnings.warn( + f"root {section[_ROOT]} is overridden" + f" by the cli arg {kwargs[_ROOT]}" + ) + section.pop("root", None) + return dist_name def _read_dist_name_from_setup_cfg() -> str | None: diff --git a/testing/conftest.py b/testing/conftest.py index c881042..d29b5dd 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -3,10 +3,10 @@ from __future__ import annotations import os from pathlib import Path from typing import Any -from typing import Generator import pytest +import setuptools_scm.utils from .wd_wrapper import WorkDir @@ -39,13 +39,25 @@ def pytest_addoption(parser: Any) -> None: ) -@pytest.fixture(autouse=True) -def debug_mode() -> Generator[None, None, None]: - from setuptools_scm import utils +class DebugMode: + def __init__(self, monkeypatch: pytest.MonkeyPatch): + self.__monkeypatch = monkeypatch + self.__module = setuptools_scm.utils + + __monkeypatch: pytest.MonkeyPatch + + def enable(self) -> None: + self.__monkeypatch.setattr(self.__module, "DEBUG", True) - utils.DEBUG = True - yield - utils.DEBUG = False + def disable(self) -> None: + self.__monkeypatch.setattr(self.__module, "DEBUG", False) + + +@pytest.fixture(autouse=True) +def debug_mode(monkeypatch: pytest.MonkeyPatch) -> DebugMode: + debug_mode = DebugMode(monkeypatch) + debug_mode.enable() + return debug_mode @pytest.fixture diff --git a/testing/test_cli.py b/testing/test_cli.py new file mode 100644 index 0000000..0198111 --- /dev/null +++ b/testing/test_cli.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import io +from contextlib import redirect_stdout + +import pytest + +from .conftest import DebugMode +from .test_git import wd as wd_fixture # NOQA evil fixture reuse +from .wd_wrapper import WorkDir +from setuptools_scm._cli import main + + +PYPROJECT_TOML = "pyproject.toml" +PYPROJECT_SIMPLE = "[tool.setuptools_scm]" +PYPROJECT_ROOT = '[tool.setuptools_scm]\nroot=".."' + + +def get_output(args: list[str]) -> str: + + with redirect_stdout(io.StringIO()) as out: + main(args) + return out.getvalue() + + +def test_cli_find_pyproject( + wd: WorkDir, monkeypatch: pytest.MonkeyPatch, debug_mode: DebugMode +) -> None: + debug_mode.disable() + wd.commit_testfile() + wd.write(PYPROJECT_TOML, PYPROJECT_SIMPLE) + monkeypatch.chdir(wd.cwd) + + out = get_output([]) + assert out.startswith("0.1.dev1+") + + with pytest.raises(SystemExit, match="no version found for"): + get_output(["--root=.."]) + + wd.write(PYPROJECT_TOML, PYPROJECT_ROOT) + with pytest.raises(SystemExit, match="no version found for"): + print(get_output(["-c", PYPROJECT_TOML])) + + with pytest.raises(SystemExit, match="no version found for"): + + get_output(["-c", PYPROJECT_TOML, "--root=.."]) + + with pytest.warns(UserWarning, match="root .. is overridden by the cli arg ."): + out = get_output(["-c", PYPROJECT_TOML, "--root=."]) + assert out.startswith("0.1.dev1+") diff --git a/testing/test_git.py b/testing/test_git.py index 661a5ee..105a9e0 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -15,6 +15,7 @@ from unittest.mock import patch import pytest +from .conftest import DebugMode from .wd_wrapper import WorkDir from setuptools_scm import Configuration from setuptools_scm import format_version @@ -31,14 +32,16 @@ pytestmark = pytest.mark.skipif( ) -@pytest.fixture -def wd(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> WorkDir: +@pytest.fixture(name="wd") +def wd(wd: WorkDir, monkeypatch: pytest.MonkeyPatch, debug_mode: DebugMode) -> WorkDir: + debug_mode.disable() monkeypatch.delenv("HOME", raising=False) wd("git init") wd("git config user.email test@example.com") wd('git config user.name "a test"') wd.add_command = "git add ." wd.commit_command = "git commit -m test-{reason}" + debug_mode.enable() return wd |