Skip to content

Commit 9d2dd02

Browse files
committed
Stop updating own package
1 parent b402067 commit 9d2dd02

2 files changed

Lines changed: 66 additions & 13 deletions

File tree

spec0_action/__init__.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,21 @@ def _version_from_filename(filename: str) -> Version | None:
8585
return None
8686

8787

88-
def update_pyproject_dependencies(dependencies: list, schedule: Dict[str, str]):
88+
def update_pyproject_dependencies(
89+
dependencies: list, schedule: Dict[str, str], own_name: str | None = None
90+
):
8991
# Iterate by idx because we want to update it inplace
9092
for i in range(len(dependencies)):
9193
dep_str = dependencies[i]
9294
if not isinstance(dep_str, str):
9395
continue
9496
pkg, extras, spec, env = parse_pep_dependency(dep_str)
9597
schedule_key = canonicalize_name(pkg)
96-
if isinstance(spec, Url) or schedule_key not in schedule:
98+
if (
99+
isinstance(spec, Url)
100+
or schedule_key == own_name
101+
or schedule_key not in schedule
102+
):
97103
continue
98104
new_lower_bound = Version(schedule[schedule_key])
99105
try:
@@ -126,11 +132,13 @@ def iter_pep_dependency_lists(pyproject_data: dict, project_data: dict):
126132
yield dependencies
127133

128134

129-
def update_dependency_table(dep_table: dict, new_versions: dict):
135+
def update_dependency_table(
136+
dep_table: dict, new_versions: dict, own_name: str | None = None
137+
):
130138
for pkg, pkg_data in dep_table.items():
131139
schedule_key = canonicalize_name(pkg)
132-
# Don't do anything for pkgs that aren't in our schedule
133-
if schedule_key not in new_versions:
140+
# Don't do anything for the package itself or pkgs that aren't in our schedule
141+
if schedule_key == own_name or schedule_key not in new_versions:
134142
continue
135143
# Like pkg = ">x.y.z,<a"
136144
if isinstance(pkg_data, str):
@@ -159,18 +167,24 @@ def update_dependency_table(dep_table: dict, new_versions: dict):
159167
pkg_data["version"] = repr_spec_set(spec)
160168

161169

162-
def update_pixi_dependencies(pixi_tables: dict, schedule: Dict[str, str]):
170+
def update_pixi_dependencies(
171+
pixi_tables: dict, schedule: Dict[str, str], own_name: str | None = None
172+
):
163173
if "pypi-dependencies" in pixi_tables:
164-
update_dependency_table(pixi_tables["pypi-dependencies"], schedule)
174+
update_dependency_table(pixi_tables["pypi-dependencies"], schedule, own_name)
165175
if "dependencies" in pixi_tables:
166-
update_dependency_table(pixi_tables["dependencies"], schedule)
176+
update_dependency_table(pixi_tables["dependencies"], schedule, own_name)
167177

168178
if "feature" in pixi_tables:
169179
for _, feature_data in pixi_tables["feature"].items():
170180
if "dependencies" in feature_data:
171-
update_dependency_table(feature_data["dependencies"], schedule)
181+
update_dependency_table(
182+
feature_data["dependencies"], schedule, own_name
183+
)
172184
if "pypi-dependencies" in feature_data:
173-
update_dependency_table(feature_data["pypi-dependencies"], schedule)
185+
update_dependency_table(
186+
feature_data["pypi-dependencies"], schedule, own_name
187+
)
174188

175189

176190
def update_pyproject_toml(
@@ -198,6 +212,13 @@ def update_pyproject_toml(
198212
project_data = pyproject_data.get("project", {})
199213
if not isinstance(project_data, dict):
200214
project_data = {}
215+
# Self-references like "pkg[extras]" are used to share extras between
216+
# dependency groups, their version is always the local one so never pin it.
217+
own_name = project_data.get("name")
218+
if isinstance(own_name, str):
219+
own_name = canonicalize_name(own_name)
220+
else:
221+
own_name = None
201222
if "python" in new_version and isinstance(project_data, dict):
202223
current_requires_python = project_data.get("requires-python")
203224
if current_requires_python:
@@ -213,18 +234,22 @@ def update_pyproject_toml(
213234
project_data["requires-python"] = repr_spec_set(python_spec)
214235

215236
for dependencies in iter_pep_dependency_lists(pyproject_data, project_data):
216-
update_pyproject_dependencies(dependencies, new_version)
237+
update_pyproject_dependencies(dependencies, new_version, own_name)
217238

218239
if "tool" in pyproject_data and "pixi" in pyproject_data["tool"]:
219240
pixi_data = pyproject_data["tool"]["pixi"]
220-
update_pixi_dependencies(pixi_data, new_version)
241+
update_pixi_dependencies(pixi_data, new_version, own_name)
221242
if update_all is not None:
222243
for deps in iter_pep_dependency_lists(pyproject_data, project_data):
223244
for i, dep_str in enumerate(deps):
224245
if not isinstance(dep_str, str):
225246
continue
226247
pkg, extras, spec, env = parse_pep_dependency(dep_str)
227-
if canonicalize_name(pkg) in new_version or isinstance(spec, Url):
248+
if (
249+
canonicalize_name(pkg) in new_version
250+
or canonicalize_name(pkg) == own_name
251+
or isinstance(spec, Url)
252+
):
228253
continue
229254
min_ver = _get_oldest_version_in_window(pkg, update_all)
230255
if min_ver is None:

tests/test_update_pyproject_toml.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,34 @@ def test_update_all_updates_optional_dependency_groups_and_unbounded(
116116
]
117117

118118

119+
def test_self_referencing_extras_are_left_alone(patch_datetime_now):
120+
pyproject = _minimal_pyproject("requests>=2.0.0")
121+
pyproject["project"]["name"] = "My_Package"
122+
pyproject["dependency-groups"] = {
123+
"tests": ["my-package[plotting,tests-only]"],
124+
}
125+
schedule = read_schedule("tests/test_data/test_schedule.json")
126+
with patch.object(
127+
spec0_action, "_get_oldest_version_in_window", return_value=Version("2.2.2")
128+
) as mock_pypi:
129+
update_pyproject_toml(pyproject, schedule, update_all=2.0)
130+
131+
assert pyproject["dependency-groups"]["tests"] == ["my-package[plotting,tests-only]"]
132+
for call_args in mock_pypi.call_args_list:
133+
assert call_args[0][0] != "my-package"
134+
135+
136+
def test_self_reference_skipped_even_when_in_schedule(patch_datetime_now):
137+
# A project named like a schedule package must not have its self-reference pinned
138+
pyproject = _minimal_pyproject("numpy[test]")
139+
pyproject["project"]["name"] = "numpy"
140+
schedule = read_schedule("tests/test_data/test_schedule.json")
141+
142+
update_pyproject_toml(pyproject, schedule)
143+
144+
assert pyproject["project"]["dependencies"] == ["numpy[test]"]
145+
146+
119147
def test_requires_python_preserves_existing_restrictions(patch_datetime_now):
120148
pyproject = _minimal_pyproject()
121149
pyproject["project"]["requires-python"] = ">=3.9,<3.14,!=3.13.*"

0 commit comments

Comments
 (0)