Skip to content

Commit 1a33dc5

Browse files
committed
Enhance publish filtering to ensure exact version matches
- Updated the publisher to filter files based on exact version matches, preventing partial matches (e.g., 1.0.10 not matching 1.0.1). - Added tests to verify that only the correct version files are included during the publish process. - Minor adjustments in the BuildManager for clarity.
1 parent ab3e31e commit 1a33dc5

3 files changed

Lines changed: 46 additions & 7 deletions

File tree

src/python_package_folder/manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ def build():
677677
# Determine package name and version for filtering
678678
publish_package_name = None
679679
publish_version = version
680+
publish_package_name = None
680681
is_subfolder_build = self._is_subfolder_build()
681682

682683
if is_subfolder_build:

src/python_package_folder/publisher.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,21 @@ def publish(self, skip_existing: bool = False) -> None:
201201
# Handle .tar.gz files
202202
stem = stem[:-4] # Remove .tar
203203

204-
# Check if filename starts with any name variant followed by version
204+
# Check if filename matches any name variant with exact version
205205
matches = False
206206
for name_variant in name_variants:
207207
# Pattern: {name}-{version} or {name}-{version}-{tag}
208-
if stem.startswith(f"{name_variant}-{version_str}"):
209-
matches = True
210-
break
208+
# Use exact match: must start with name-version and next char (if any) must be - or end of string
209+
expected_prefix = f"{name_variant}-{version_str}"
210+
if stem.startswith(expected_prefix):
211+
# Ensure exact version match (not a longer version like 1.0.10 matching 1.0.1)
212+
# Check that after the version, we have either:
213+
# - End of string (for source dists: name-version)
214+
# - A hyphen followed by more characters (for wheels: name-version-tag)
215+
remaining = stem[len(expected_prefix) :]
216+
if not remaining or remaining.startswith("-"):
217+
matches = True
218+
break
211219

212220
if matches:
213221
dist_files.append(f)

tests/test_publisher.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ def test_dist_dir(tmp_path: Path) -> Path:
1919
# Create some distribution files
2020
(dist_dir / "package-1.0.0-py3-none-any.whl").write_text("fake wheel")
2121
(dist_dir / "package-1.0.0.tar.gz").write_text("fake source")
22+
(dist_dir / "package-1.0.1-py3-none-any.whl").write_text("fake wheel")
23+
(dist_dir / "package-1.0.1.tar.gz").write_text("fake source")
24+
(dist_dir / "package-1.0.10-py3-none-any.whl").write_text("fake wheel")
2225
(dist_dir / "other-package-2.0.0-py3-none-any.whl").write_text("fake wheel")
2326

2427
return dist_dir
@@ -125,6 +128,30 @@ def test_publish_filters_by_package_name(self, test_dist_dir: Path) -> None:
125128
assert all("package-1.0.0" in str(f) for f in file_args)
126129
assert not any("other-package" in str(f) for f in file_args)
127130

131+
def test_publish_filters_exact_version(self, test_dist_dir: Path) -> None:
132+
"""Test that publish filters files by exact version (not partial matches)."""
133+
publisher = Publisher(
134+
repository=Repository.PYPI,
135+
dist_dir=test_dist_dir,
136+
package_name="package",
137+
version="1.0.1",
138+
)
139+
140+
with patch("python_package_folder.publisher.subprocess.run") as mock_run:
141+
mock_run.return_value = MagicMock(returncode=0)
142+
143+
with patch.object(publisher, "_get_credentials", return_value=("user", "pass")):
144+
publisher.publish()
145+
146+
call_args = mock_run.call_args[0][0]
147+
file_args = [arg for arg in call_args if str(test_dist_dir) in str(arg)]
148+
149+
# Should only include 1.0.1 files, not 1.0.0 or 1.0.10
150+
assert all("1.0.1" in str(f) for f in file_args)
151+
assert not any("1.0.0" in str(f) for f in file_args)
152+
assert not any("1.0.10" in str(f) for f in file_args)
153+
assert len(file_args) == 2 # wheel and source dist
154+
128155
def test_publish_filters_by_version(self, test_dist_dir: Path) -> None:
129156
"""Test that publish filters files by version."""
130157
publisher = Publisher(
@@ -143,8 +170,11 @@ def test_publish_filters_by_version(self, test_dist_dir: Path) -> None:
143170
call_args = mock_run.call_args[0][0]
144171
file_args = [arg for arg in call_args if str(test_dist_dir) in str(arg)]
145172

146-
# Should only include 1.0.0 files, not 2.0.0
173+
# Should only include 1.0.0 files, not 1.0.1, 1.0.10, or 2.0.0
147174
assert all("1.0.0" in str(f) for f in file_args)
175+
assert not any("1.0.1" in str(f) for f in file_args)
176+
assert not any("1.0.10" in str(f) for f in file_args)
177+
assert not any("2.0.0" in str(f) for f in file_args)
148178

149179
def test_publish_no_filtering(self, test_dist_dir: Path) -> None:
150180
"""Test that publish includes all files when no filter specified."""
@@ -162,8 +192,8 @@ def test_publish_no_filtering(self, test_dist_dir: Path) -> None:
162192
call_args = mock_run.call_args[0][0]
163193
file_args = [arg for arg in call_args if str(test_dist_dir) in str(arg)]
164194

165-
# Should include all distribution files
166-
assert len(file_args) == 3
195+
# Should include all distribution files (6 files: 4 wheels + 2 source dists)
196+
assert len(file_args) == 6
167197

168198
def test_publish_raises_when_no_files(self, tmp_path: Path) -> None:
169199
"""Test that publish raises when no distribution files found."""

0 commit comments

Comments
 (0)