Skip to content

Commit 21e58d4

Browse files
Merge pull request #731 from RonnyPfannschmidt/cli-setup-root-option
fix #691 - support root in pyproject.toml even for cli
2 parents 6990979 + 1d920c7 commit 21e58d4

File tree

5 files changed

+115
-24
lines changed

5 files changed

+115
-24
lines changed

src/setuptools_scm/_cli.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
from setuptools_scm.integration import find_files
1111

1212

13-
def main() -> None:
14-
opts = _get_cli_opts()
15-
root = opts.root or "."
13+
def main(args: list[str] | None = None) -> None:
14+
opts = _get_cli_opts(args)
15+
inferred_root: str = opts.root or "."
1616

17-
pyproject = opts.config or _find_pyproject(root)
17+
pyproject = opts.config or _find_pyproject(inferred_root)
1818

1919
try:
20-
root = opts.root or os.path.relpath(os.path.dirname(pyproject))
21-
config = Configuration.from_file(pyproject, root=root)
20+
21+
config = Configuration.from_file(pyproject, root=opts.root)
2222
except (LookupError, FileNotFoundError) as ex:
2323
# no pyproject.toml OR no [tool.setuptools_scm]
2424
print(
@@ -27,10 +27,11 @@ def main() -> None:
2727
f" Reason: {ex}.",
2828
file=sys.stderr,
2929
)
30-
config = Configuration(root=root)
30+
config = Configuration(inferred_root)
3131

3232
version = _get_version(config)
33-
assert version is not None
33+
if version is None:
34+
raise SystemExit("ERROR: no version found for", opts)
3435
if opts.strip_dev:
3536
version = version.partition(".dev")[0]
3637
print(version)
@@ -40,7 +41,7 @@ def main() -> None:
4041
print(fname)
4142

4243

43-
def _get_cli_opts() -> argparse.Namespace:
44+
def _get_cli_opts(args: list[str] | None) -> argparse.Namespace:
4445
prog = "python -m setuptools_scm"
4546
desc = "Print project version according to SCM metadata"
4647
parser = argparse.ArgumentParser(prog, description=desc)
@@ -68,7 +69,7 @@ def _get_cli_opts() -> argparse.Namespace:
6869
# We avoid `metavar` to prevent printing repetitive information
6970
desc = "List files managed by the SCM"
7071
sub.add_parser("ls", help=desc[0].lower() + desc[1:], description=desc)
71-
return parser.parse_args()
72+
return parser.parse_args(args)
7273

7374

7475
def _find_pyproject(parent: str) -> str:

src/setuptools_scm/config.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$"
2525
DEFAULT_VERSION_SCHEME = "guess-next-dev"
2626
DEFAULT_LOCAL_SCHEME = "node-and-date"
27+
_ROOT = "root"
2728

2829

2930
def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]:
@@ -213,27 +214,51 @@ def from_file(
213214

214215
with open(name, encoding="UTF-8") as strm:
215216
data = strm.read()
217+
216218
defn = _load_toml(data)
217219
try:
218220
section = defn.get("tool", {})["setuptools_scm"]
219221
except LookupError as e:
220222
raise LookupError(
221223
f"{name} does not contain a tool.setuptools_scm section"
222224
) from e
225+
226+
project = defn.get("project", {})
227+
dist_name = cls._cleanup_from_file_args_data(
228+
project, dist_name, kwargs, section
229+
)
230+
return cls(dist_name=dist_name, relative_to=name, **section, **kwargs)
231+
232+
@staticmethod
233+
def _cleanup_from_file_args_data(
234+
project: dict[str, Any],
235+
dist_name: str | None,
236+
kwargs: dict[str, Any],
237+
section: dict[str, Any],
238+
) -> str | None:
239+
"""drops problematic details and figures the distribution name"""
223240
if "dist_name" in section:
224241
if dist_name is None:
225242
dist_name = section.pop("dist_name")
226243
else:
227244
assert dist_name == section["dist_name"]
228245
del section["dist_name"]
229246
if dist_name is None:
230-
if "project" in defn:
231-
# minimal pep 621 support for figuring the pretend keys
232-
dist_name = defn["project"].get("name")
247+
# minimal pep 621 support for figuring the pretend keys
248+
dist_name = project.get("name")
233249
if dist_name is None:
234250
dist_name = _read_dist_name_from_setup_cfg()
235-
236-
return cls(dist_name=dist_name, **section, **kwargs)
251+
if _ROOT in kwargs:
252+
if kwargs[_ROOT] is None:
253+
kwargs.pop(_ROOT, None)
254+
elif _ROOT in section:
255+
if section[_ROOT] != kwargs[_ROOT]:
256+
warnings.warn(
257+
f"root {section[_ROOT]} is overridden"
258+
f" by the cli arg {kwargs[_ROOT]}"
259+
)
260+
section.pop("root", None)
261+
return dist_name
237262

238263

239264
def _read_dist_name_from_setup_cfg() -> str | None:

testing/conftest.py

+19-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import os
44
from pathlib import Path
55
from typing import Any
6-
from typing import Generator
76

87
import pytest
98

9+
import setuptools_scm.utils
1010
from .wd_wrapper import WorkDir
1111

1212

@@ -39,13 +39,25 @@ def pytest_addoption(parser: Any) -> None:
3939
)
4040

4141

42-
@pytest.fixture(autouse=True)
43-
def debug_mode() -> Generator[None, None, None]:
44-
from setuptools_scm import utils
42+
class DebugMode:
43+
def __init__(self, monkeypatch: pytest.MonkeyPatch):
44+
self.__monkeypatch = monkeypatch
45+
self.__module = setuptools_scm.utils
46+
47+
__monkeypatch: pytest.MonkeyPatch
48+
49+
def enable(self) -> None:
50+
self.__monkeypatch.setattr(self.__module, "DEBUG", True)
4551

46-
utils.DEBUG = True
47-
yield
48-
utils.DEBUG = False
52+
def disable(self) -> None:
53+
self.__monkeypatch.setattr(self.__module, "DEBUG", False)
54+
55+
56+
@pytest.fixture(autouse=True)
57+
def debug_mode(monkeypatch: pytest.MonkeyPatch) -> DebugMode:
58+
debug_mode = DebugMode(monkeypatch)
59+
debug_mode.enable()
60+
return debug_mode
4961

5062

5163
@pytest.fixture

testing/test_cli.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from __future__ import annotations
2+
3+
import io
4+
from contextlib import redirect_stdout
5+
6+
import pytest
7+
8+
from .conftest import DebugMode
9+
from .test_git import wd as wd_fixture # NOQA evil fixture reuse
10+
from .wd_wrapper import WorkDir
11+
from setuptools_scm._cli import main
12+
13+
14+
PYPROJECT_TOML = "pyproject.toml"
15+
PYPROJECT_SIMPLE = "[tool.setuptools_scm]"
16+
PYPROJECT_ROOT = '[tool.setuptools_scm]\nroot=".."'
17+
18+
19+
def get_output(args: list[str]) -> str:
20+
21+
with redirect_stdout(io.StringIO()) as out:
22+
main(args)
23+
return out.getvalue()
24+
25+
26+
def test_cli_find_pyproject(
27+
wd: WorkDir, monkeypatch: pytest.MonkeyPatch, debug_mode: DebugMode
28+
) -> None:
29+
debug_mode.disable()
30+
wd.commit_testfile()
31+
wd.write(PYPROJECT_TOML, PYPROJECT_SIMPLE)
32+
monkeypatch.chdir(wd.cwd)
33+
34+
out = get_output([])
35+
assert out.startswith("0.1.dev1+")
36+
37+
with pytest.raises(SystemExit, match="no version found for"):
38+
get_output(["--root=.."])
39+
40+
wd.write(PYPROJECT_TOML, PYPROJECT_ROOT)
41+
with pytest.raises(SystemExit, match="no version found for"):
42+
print(get_output(["-c", PYPROJECT_TOML]))
43+
44+
with pytest.raises(SystemExit, match="no version found for"):
45+
46+
get_output(["-c", PYPROJECT_TOML, "--root=.."])
47+
48+
with pytest.warns(UserWarning, match="root .. is overridden by the cli arg ."):
49+
out = get_output(["-c", PYPROJECT_TOML, "--root=."])
50+
assert out.startswith("0.1.dev1+")

testing/test_git.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import pytest
1717

18+
from .conftest import DebugMode
1819
from .wd_wrapper import WorkDir
1920
from setuptools_scm import Configuration
2021
from setuptools_scm import format_version
@@ -31,14 +32,16 @@
3132
)
3233

3334

34-
@pytest.fixture
35-
def wd(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> WorkDir:
35+
@pytest.fixture(name="wd")
36+
def wd(wd: WorkDir, monkeypatch: pytest.MonkeyPatch, debug_mode: DebugMode) -> WorkDir:
37+
debug_mode.disable()
3638
monkeypatch.delenv("HOME", raising=False)
3739
wd("git init")
3840
wd("git config user.email [email protected]")
3941
wd('git config user.name "a test"')
4042
wd.add_command = "git add ."
4143
wd.commit_command = "git commit -m test-{reason}"
44+
debug_mode.enable()
4245
return wd
4346

4447

0 commit comments

Comments
 (0)