Visitar URL original
Find wppm from the checkout, not from luck · stonebig/winpython@dab14ef · GitHub
Skip to content

Commit dab14ef

Browse files
stonebigclaude
andcommitted
Find wppm from the checkout, not from luck
changelog_files.py imported wppm and CI answered ModuleNotFoundError. Running a script puts the script's own directory on sys.path -- here .github/scripts -- and not the checkout root, so the import fell through to whatever else was reachable. On a developer machine that is the installed wppm and everything looks fine; a runner installs none, so the step died. The workflow job would have died the same way, not just the tests. The path is now derived from __file__ and inserted first, so the wppm imported is the one being released. The tests missed it for the same reason CI caught it, so they now run the script by absolute path from an unrelated directory, with a decoy wppm on PYTHONPATH. The insert puts the checkout ahead of that decoy; without it the decoy wins and the test fails, on any machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MBk5k7WdpPvx4SUFyEk3U3
1 parent 95e806e commit dab14ef

2 files changed

Lines changed: 50 additions & 22 deletions

File tree

‎.github/scripts/changelog_files.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,21 @@
1313
only a checkout of the repository has that to compare against. Ordering is
1414
`wppm.diff`'s job; this decides what to hand it.
1515
16-
Run from the checkout root, so `from wppm import diff` finds the wppm being
17-
released rather than an installed one.
1816
"""
1917
import re
2018
import shutil
2119
import sys
2220
from pathlib import Path
2321

24-
from wppm import diff
25-
from wppm.diff import version # packaging, or pip's vendored copy
22+
# Running a script puts the script's own directory on sys.path, not the
23+
# checkout root, so wppm has to be found deliberately: this file is
24+
# .github/scripts/changelog_files.py, hence two levels up. Without it the
25+
# import finds whatever wppm happens to be installed, or -- as on a CI runner,
26+
# which installs none -- nothing at all.
27+
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
28+
29+
from wppm import diff # noqa: E402 the path above has to be set first
30+
from wppm.diff import version # noqa: E402 packaging, or pip's vendored copy
2631

2732
# WinPythonslim-64bit-3.15.0.5b1.md -- the flavor may be empty, and the version
2833
# may carry a release level, which is why the parser decides and not the regex

‎tests/test_changelog_files.py‎

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
only the repository has.
1212
"""
1313
import importlib.util
14+
import os
1415
import shutil
1516
import subprocess
1617
import sys
@@ -87,6 +88,19 @@ def test_directories_are_skipped(self, changelog_files, tmp_path):
8788
]
8889

8990

91+
def run_script(*args, cwd, env=None):
92+
"""The script by absolute path, from an unrelated directory.
93+
94+
It must not need to be run from the checkout: the workflow's own step and
95+
these tests both invoke it as a path, which puts .github/scripts on
96+
sys.path rather than the repository root.
97+
"""
98+
return subprocess.run(
99+
[sys.executable, str(SCRIPT), *map(str, args)],
100+
cwd=str(cwd), capture_output=True, text=True, env=env,
101+
)
102+
103+
90104
@needs_script
91105
class TestEndToEnd:
92106
"""Run the script the way the workflow runs it."""
@@ -111,12 +125,30 @@ def cycle(self, tmp_path):
111125
(source / "hashes_3.15.0.5slimb1.md").write_text("x", encoding="utf-8")
112126
return source, changelogs
113127

114-
def test_files_the_three_and_writes_the_history(self, cycle):
128+
def test_wppm_comes_from_the_checkout(self, cycle, tmp_path):
129+
"""A decoy wppm on PYTHONPATH must lose to the one being released.
130+
131+
Running a script puts the script's directory on sys.path, not the
132+
checkout root, so without a deliberate insert the import falls through
133+
to whatever else is reachable. On a machine with wppm installed that
134+
looks fine -- which is how it once slipped past a green local run --
135+
and on a CI runner, which installs none, it is ModuleNotFoundError.
136+
The decoy makes the difference visible either way.
137+
"""
115138
source, changelogs = cycle
116-
proc = subprocess.run(
117-
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
118-
cwd=REPO, capture_output=True, text=True,
139+
decoy = tmp_path / "decoy"
140+
(decoy / "wppm").mkdir(parents=True)
141+
(decoy / "wppm" / "__init__.py").write_text(
142+
"raise RuntimeError('decoy wppm imported')", encoding="utf-8"
119143
)
144+
env = {**os.environ, "PYTHONPATH": str(decoy)}
145+
proc = run_script(source, changelogs, cwd=tmp_path, env=env)
146+
assert proc.returncode == 0, proc.stderr
147+
assert "decoy" not in proc.stderr
148+
149+
def test_files_the_three_and_writes_the_history(self, cycle, tmp_path):
150+
source, changelogs = cycle
151+
proc = run_script(source, changelogs, cwd=tmp_path)
120152
assert proc.returncode == 0, proc.stderr
121153
landed = sorted(p.name for p in changelogs.iterdir())
122154
assert landed == [
@@ -127,12 +159,10 @@ def test_files_the_three_and_writes_the_history(self, cycle):
127159
"requir.64-3_15_0_5slimb1.txt",
128160
]
129161

130-
def test_the_history_names_the_release_it_compares_against(self, cycle):
162+
def test_the_history_names_the_release_it_compares_against(self, cycle, tmp_path):
131163
source, changelogs = cycle
132-
subprocess.run(
133-
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
134-
cwd=REPO, capture_output=True, text=True, check=True,
135-
)
164+
proc = run_script(source, changelogs, cwd=tmp_path)
165+
assert proc.returncode == 0, proc.stderr
136166
history = (changelogs / "WinPythonslim-64bit-3.15.0.5b1_History.md").read_text(
137167
encoding="utf-8"
138168
)
@@ -144,18 +174,11 @@ def test_an_empty_metadata_directory_is_an_error(self, tmp_path):
144174
source, changelogs = tmp_path / "src", tmp_path / "changelogs"
145175
source.mkdir()
146176
changelogs.mkdir()
147-
proc = subprocess.run(
148-
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
149-
cwd=REPO, capture_output=True, text=True,
150-
)
177+
proc = run_script(source, changelogs, cwd=tmp_path)
151178
assert proc.returncode != 0
152179
assert "held no changelog" in proc.stdout + proc.stderr
153180

154181
def test_a_missing_directory_is_an_error(self, tmp_path):
155-
proc = subprocess.run(
156-
[sys.executable, ".github/scripts/changelog_files.py",
157-
str(tmp_path / "nope"), str(tmp_path)],
158-
cwd=REPO, capture_output=True, text=True,
159-
)
182+
proc = run_script(tmp_path / "nope", tmp_path, cwd=tmp_path)
160183
assert proc.returncode != 0
161184
assert "no such directory" in proc.stdout + proc.stderr

0 commit comments

Comments
 (0)