Skip to content

Commit 5f68a3c

Browse files
introduce run_hg and remove the need for module reloading for the hg command env var
1 parent 6d3524f commit 5f68a3c

File tree

6 files changed

+46
-50
lines changed

6 files changed

+46
-50
lines changed

src/setuptools_scm/_file_finders/hg.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,17 @@
77
from .. import _types as _t
88
from .._file_finders import is_toplevel_acceptable
99
from .._file_finders import scm_find_files
10-
from .._run_cmd import run as _run
10+
from ..hg import run_hg
1111
from ..integration import data_from_mime
1212
from .pathtools import norm_real
1313

1414
log = logging.getLogger(__name__)
1515

16-
HG_COMMAND = os.environ.get("SETUPTOOLS_SCM_HG_COMMAND", "hg")
17-
1816

1917
def _hg_toplevel(path: str) -> str | None:
2018
try:
21-
return _run(
22-
[HG_COMMAND, "root"],
19+
return run_hg(
20+
["root"],
2321
cwd=(path or "."),
2422
check=True,
2523
).parse_success(norm_real)
@@ -34,7 +32,7 @@ def _hg_toplevel(path: str) -> str | None:
3432
def _hg_ls_files_and_dirs(toplevel: str) -> tuple[set[str], set[str]]:
3533
hg_files: set[str] = set()
3634
hg_dirs = {toplevel}
37-
res = _run([HG_COMMAND, "files"], cwd=toplevel)
35+
res = run_hg(["files"], cwd=toplevel)
3836
if res.returncode:
3937
return set(), set()
4038
for name in res.stdout.splitlines():

src/setuptools_scm/hg.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pathlib import Path
88
from typing import TYPE_CHECKING
9+
from typing import Any
910

1011
from . import Configuration
1112
from ._version_cls import Version
@@ -19,18 +20,28 @@
1920
if TYPE_CHECKING:
2021
from . import _types as _t
2122

23+
from ._run_cmd import CompletedProcess
2224
from ._run_cmd import require_command as _require_command
2325
from ._run_cmd import run as _run
2426

2527
log = logging.getLogger(__name__)
2628

27-
HG_COMMAND = os.environ.get("SETUPTOOLS_SCM_HG_COMMAND", "hg")
29+
30+
def _get_hg_command() -> str:
31+
"""Get the hg command from environment, allowing runtime configuration."""
32+
return os.environ.get("SETUPTOOLS_SCM_HG_COMMAND", "hg")
33+
34+
35+
def run_hg(args: list[str], cwd: _t.PathT, **kwargs: Any) -> CompletedProcess:
36+
"""Run mercurial command with the configured hg executable."""
37+
cmd = [_get_hg_command(), *args]
38+
return _run(cmd, cwd=cwd, **kwargs)
2839

2940

3041
class HgWorkdir(Workdir):
3142
@classmethod
3243
def from_potential_worktree(cls, wd: _t.PathT) -> HgWorkdir | None:
33-
res = _run([HG_COMMAND, "root"], wd)
44+
res = run_hg(["root"], wd)
3445
if res.returncode:
3546
return None
3647
return cls(Path(res.stdout))
@@ -79,8 +90,8 @@ def _get_node_info(self) -> tuple[str, str, str] | None:
7990

8091
def _get_branch_info(self) -> tuple[str, bool, str]:
8192
"""Get branch name, dirty status, and dirty date."""
82-
branch, dirty_str, dirty_date = _run(
83-
[HG_COMMAND, "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"],
93+
branch, dirty_str, dirty_date = run_hg(
94+
["id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"],
8495
cwd=self.path,
8596
check=True,
8697
).stdout.split("\n")
@@ -181,9 +192,9 @@ def _get_distance_based_version(
181192
return None
182193

183194
def hg_log(self, revset: str, template: str) -> str:
184-
cmd = [HG_COMMAND, "log", "-r", revset, "-T", template]
185-
186-
return _run(cmd, cwd=self.path, check=True).stdout
195+
return run_hg(
196+
["log", "-r", revset, "-T", template], cwd=self.path, check=True
197+
).stdout
187198

188199
def get_latest_normalizable_tag(self) -> str | None:
189200
# Gets all tags containing a '.' (see #229) from oldest to newest
@@ -223,12 +234,12 @@ def get_dirty_tag_date(self) -> datetime.date | None:
223234
"""
224235
try:
225236
# Check if working directory is dirty first
226-
res = _run([HG_COMMAND, "id", "-T", "{dirty}"], cwd=self.path)
237+
res = run_hg(["id", "-T", "{dirty}"], cwd=self.path)
227238
if res.returncode != 0 or not bool(res.stdout):
228239
return None
229240

230241
# Get list of changed files using hg status
231-
status_res = _run([HG_COMMAND, "status", "-m", "-a", "-r"], cwd=self.path)
242+
status_res = run_hg(["status", "-m", "-a", "-r"], cwd=self.path)
232243
if status_res.returncode != 0:
233244
return None
234245

@@ -248,9 +259,10 @@ def get_dirty_tag_date(self) -> datetime.date | None:
248259

249260

250261
def parse(root: _t.PathT, config: Configuration) -> ScmVersion | None:
251-
_require_command(HG_COMMAND)
262+
hg_cmd = _get_hg_command()
263+
_require_command(hg_cmd)
252264
if os.path.exists(os.path.join(root, ".hg/git")):
253-
res = _run([HG_COMMAND, "path"], root)
265+
res = run_hg(["path"], root)
254266
if not res.returncode:
255267
for line in res.stdout.split("\n"):
256268
if line.startswith("default ="):

src/setuptools_scm/hg_git.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99

1010
from . import _types as _t
1111
from ._run_cmd import CompletedProcess as _CompletedProcess
12-
from ._run_cmd import run as _run
1312
from .git import GitWorkdir
14-
from .hg import HG_COMMAND
1513
from .hg import HgWorkdir
14+
from .hg import run_hg
1615
from .scm_workdir import get_latest_file_mtime
1716

1817
log = logging.getLogger(__name__)
@@ -27,25 +26,25 @@
2726
class GitWorkdirHgClient(GitWorkdir, HgWorkdir):
2827
@classmethod
2928
def from_potential_worktree(cls, wd: _t.PathT) -> GitWorkdirHgClient | None:
30-
res = _run([HG_COMMAND, "root"], cwd=wd).parse_success(parse=Path)
29+
res = run_hg(["root"], cwd=wd).parse_success(parse=Path)
3130
if res is None:
3231
return None
3332
return cls(res)
3433

3534
def is_dirty(self) -> bool:
36-
res = _run([HG_COMMAND, "id", "-T", "{dirty}"], cwd=self.path, check=True)
35+
res = run_hg(["id", "-T", "{dirty}"], cwd=self.path, check=True)
3736
return bool(res.stdout)
3837

3938
def get_branch(self) -> str | None:
40-
res = _run([HG_COMMAND, "id", "-T", "{bookmarks}"], cwd=self.path)
39+
res = run_hg(["id", "-T", "{bookmarks}"], cwd=self.path)
4140
if res.returncode:
4241
log.info("branch err %s", res)
4342
return None
4443
return res.stdout
4544

4645
def get_head_date(self) -> date | None:
47-
return _run(
48-
[HG_COMMAND, "log", "-r", ".", "-T", "{shortdate(date)}"], cwd=self.path
46+
return run_hg(
47+
["log", "-r", ".", "-T", "{shortdate(date)}"], cwd=self.path
4948
).parse_success(parse=date.fromisoformat, error_msg="head date err")
5049

5150
def get_dirty_tag_date(self) -> date | None:
@@ -59,7 +58,7 @@ def get_dirty_tag_date(self) -> date | None:
5958

6059
try:
6160
# Get list of changed files using hg status
62-
status_res = _run([HG_COMMAND, "status", "-m", "-a", "-r"], cwd=self.path)
61+
status_res = run_hg(["status", "-m", "-a", "-r"], cwd=self.path)
6362
if status_res.returncode != 0:
6463
return None
6564

@@ -84,7 +83,7 @@ def fetch_shallow(self) -> None:
8483
pass
8584

8685
def get_hg_node(self) -> str | None:
87-
res = _run([HG_COMMAND, "log", "-r", ".", "-T", "{node}"], cwd=self.path)
86+
res = run_hg(["log", "-r", ".", "-T", "{node}"], cwd=self.path)
8887
if res.returncode:
8988
return None
9089
else:
@@ -108,7 +107,7 @@ def node(self) -> str | None:
108107

109108
if git_node is None:
110109
# trying again after hg -> git
111-
_run([HG_COMMAND, "gexport"], cwd=self.path)
110+
run_hg(["gexport"], cwd=self.path)
112111
git_node = self._hg2git(hg_node)
113112

114113
if git_node is None:
@@ -123,7 +122,7 @@ def node(self) -> str | None:
123122
return git_node[:7]
124123

125124
def count_all_nodes(self) -> int:
126-
res = _run([HG_COMMAND, "log", "-r", "ancestors(.)", "-T", "."], cwd=self.path)
125+
res = run_hg(["log", "-r", "ancestors(.)", "-T", "."], cwd=self.path)
127126
return len(res.stdout)
128127

129128
def default_describe(self) -> _CompletedProcess:
@@ -133,9 +132,8 @@ def default_describe(self) -> _CompletedProcess:
133132
`git describe --dirty --tags --long --match *[0-9]*`
134133
135134
"""
136-
res = _run(
135+
res = run_hg(
137136
[
138-
HG_COMMAND,
139137
"log",
140138
"-r",
141139
"(reverse(ancestors(.)) and tag(r're:v?[0-9].*'))",
@@ -163,7 +161,7 @@ def default_describe(self) -> _CompletedProcess:
163161
logging.warning("tag not found hg=%s git=%s", hg_tags, git_tags)
164162
return _FAKE_GIT_DESCRIBE_ERROR
165163

166-
res = _run([HG_COMMAND, "log", "-r", f"'{tag}'::.", "-T", "."], cwd=self.path)
164+
res = run_hg(["log", "-r", f"'{tag}'::.", "-T", "."], cwd=self.path)
167165
if res.returncode:
168166
return _FAKE_GIT_DESCRIBE_ERROR
169167
distance = len(res.stdout) - 1

testing/test_file_finder.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import importlib
43
import os
54
import sys
65

@@ -9,7 +8,6 @@
98
import pytest
109

1110
from setuptools_scm._file_finders import find_files
12-
from setuptools_scm._file_finders import hg
1311

1412
from .wd_wrapper import WorkDir
1513

@@ -275,6 +273,5 @@ def test_hg_command_from_env(
275273
with monkeypatch.context() as m:
276274
m.setenv("SETUPTOOLS_SCM_HG_COMMAND", hg_exe)
277275
m.setenv("PATH", str(hg_wd.cwd / "not-existing"))
278-
request.addfinalizer(lambda: importlib.reload(hg))
279-
importlib.reload(hg)
276+
# No module reloading needed - runtime configuration works immediately
280277
assert set(find_files()) == {"file"}

testing/test_hg_git.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
from __future__ import annotations
22

3-
import importlib
4-
53
import pytest
64

75
from setuptools_scm import Configuration
8-
from setuptools_scm import hg
9-
from setuptools_scm import hg_git
106
from setuptools_scm._run_cmd import CommandNotFoundError
117
from setuptools_scm._run_cmd import has_command
128
from setuptools_scm._run_cmd import run
@@ -113,9 +109,6 @@ def test_hg_command_from_env(
113109
with monkeypatch.context() as m:
114110
m.setenv("SETUPTOOLS_SCM_HG_COMMAND", hg_exe)
115111
m.setenv("PATH", str(wd.cwd / "not-existing"))
116-
request.addfinalizer(lambda: importlib.reload(hg))
117-
request.addfinalizer(lambda: importlib.reload(hg_git))
118-
importlib.reload(hg)
119-
importlib.reload(hg_git)
112+
# No module reloading needed - runtime configuration works immediately
120113
wd.write("pyproject.toml", "[tool.setuptools_scm]")
121114
assert wd.get_version().startswith("0.1.dev0+")

testing/test_mercurial.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import importlib
43
import os
54

65
from pathlib import Path
@@ -10,7 +9,6 @@
109
import setuptools_scm._file_finders
1110

1211
from setuptools_scm import Configuration
13-
from setuptools_scm import hg
1412
from setuptools_scm._run_cmd import CommandNotFoundError
1513
from setuptools_scm._run_cmd import has_command
1614
from setuptools_scm.hg import archival_to_version
@@ -78,19 +76,19 @@ def test_hg_command_from_env(
7876
with monkeypatch.context() as m:
7977
m.setenv("SETUPTOOLS_SCM_HG_COMMAND", hg_exe)
8078
m.setenv("PATH", str(wd.cwd / "not-existing"))
81-
request.addfinalizer(lambda: importlib.reload(hg))
82-
importlib.reload(hg)
8379
wd.write("pyproject.toml", "[tool.setuptools_scm]")
84-
assert wd.get_version() == "0.0"
80+
# Need to commit something first for versioning to work
81+
wd.commit_testfile()
82+
version = wd.get_version()
83+
assert version.startswith("0.1.dev1+")
8584

8685

8786
def test_hg_command_from_env_is_invalid(
8887
wd: WorkDir, monkeypatch: pytest.MonkeyPatch, request: pytest.FixtureRequest
8988
) -> None:
9089
with monkeypatch.context() as m:
9190
m.setenv("SETUPTOOLS_SCM_HG_COMMAND", str(wd.cwd / "not-existing"))
92-
request.addfinalizer(lambda: importlib.reload(hg))
93-
importlib.reload(hg)
91+
# No module reloading needed - runtime configuration works immediately
9492
config = Configuration()
9593
wd.write("pyproject.toml", "[tool.setuptools_scm]")
9694
with pytest.raises(CommandNotFoundError, match=r"hg"):

0 commit comments

Comments
 (0)