Skip to content

Commit 67c6b23

Browse files
committed
Filter target_platforms by resolution-markers in _parse_uv_lock
Prevent wheel repository override collisions across incompatible Python versions in uv.lock by properly evaluating resolution-markers against target platforms.
1 parent 472d5d9 commit 67c6b23

3 files changed

Lines changed: 89 additions & 28 deletions

File tree

.agents/skills/buildkite-retry-job/scripts/retry_buildkite_jobs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ def main():
6868

6969
for job in failed_jobs:
7070
job_id = job.get("id")
71-
job_name = job.get("name", "Unknown")
71+
job_name = job.get("name") or "Unknown"
7272

7373
if (
7474
args.job_name
7575
and args.job_name.lower() not in job_name.lower()
76-
and args.job_name.lower() not in job.get("step_key", "").lower()
76+
and args.job_name.lower() not in (job.get("step_key") or "").lower()
7777
):
7878
continue
7979

python/private/pypi/hub_builder.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ def _create_whl_repos(
493493
),
494494
logger = logger,
495495
),
496-
uv_lock = pip_attr.uv_lock,
496+
uv_lock = getattr(pip_attr, "uv_lock", None),
497497
platforms = platforms,
498498
extra_pip_args = pip_attr.extra_pip_args,
499499
get_index_urls = self._get_index_urls.get(pip_attr.python_version),

python/private/pypi/parse_requirements.bzl

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def parse_requirements(
8686
return _parse_uv_lock(
8787
uv_lock = uv_lock,
8888
all_platforms = _get_all_platforms(requirements_by_platform) if requirements_by_platform else sorted(platforms.keys()),
89+
platforms = platforms,
8990
extra_pip_args = extra_pip_args,
9091
logger = logger,
9192
)
@@ -108,12 +109,13 @@ def _get_all_platforms(requirements_by_platform):
108109
all_platforms[p] = None
109110
return sorted(all_platforms)
110111

111-
def _parse_uv_lock(uv_lock, all_platforms, logger, extra_pip_args = None):
112+
def _parse_uv_lock(uv_lock, all_platforms, platforms, logger, extra_pip_args = None):
112113
"""Parse decoded uv.lock TOML dict and build the same return structs as parse_requirements.
113114
114115
Args:
115116
uv_lock: {type}`dict` A decoded dictionary representing the uv.lock contents.
116117
all_platforms: {type}`list[str]` The list of all platform names.
118+
platforms: {type}`dict` The target platform descriptions.
117119
logger: {type}`struct` A logger for diagnostic messages.
118120
extra_pip_args: {type}`list[str] | None` Extra pip arguments to pass through.
119121
@@ -122,6 +124,22 @@ def _parse_uv_lock(uv_lock, all_platforms, logger, extra_pip_args = None):
122124
"""
123125
uv_packages = {}
124126
for pkg in uv_lock.get("package", []):
127+
valid_platforms = []
128+
if all_platforms:
129+
markers = pkg.get("resolution-markers", [])
130+
for plat_name in all_platforms:
131+
plat_env = platforms.get(plat_name)
132+
if not markers or not plat_env:
133+
valid_platforms.append(plat_name)
134+
else:
135+
for m in markers:
136+
if evaluate(m, env = plat_env.env):
137+
valid_platforms.append(plat_name)
138+
break
139+
140+
if not valid_platforms:
141+
continue
142+
125143
name = pkg["name"]
126144
version = pkg["version"]
127145
norm_name = normalize_name(name)
@@ -140,40 +158,79 @@ def _parse_uv_lock(uv_lock, all_platforms, logger, extra_pip_args = None):
140158
if extra not in entry["extras"]:
141159
entry["extras"][extra] = None
142160

143-
seen = {}
161+
wheels = []
144162
for wheel in pkg.get("wheels", []):
145163
sha256 = wheel.get("hash", "").removeprefix("sha256:")
146164
url = wheel["url"]
147165
_, _, filename = url.rpartition("/")
148-
key = (filename, sha256)
149-
if key not in seen:
150-
seen[key] = None
166+
wheels.append(struct(
167+
filename = filename,
168+
sha256 = sha256,
169+
url = url,
170+
target_platforms = [],
171+
))
172+
173+
matched_by_any = {}
174+
if all_platforms:
175+
for plat_name in valid_platforms:
176+
plat_env = platforms.get(plat_name)
177+
if not plat_env or not getattr(plat_env, "whl_platform_tags", None):
178+
for w in wheels:
179+
if plat_name not in w.target_platforms:
180+
w.target_platforms.append(plat_name)
181+
matched_by_any[plat_name] = None
182+
elif wheels:
183+
selected = select_whl(
184+
whls = wheels,
185+
python_version = plat_env.env["python_full_version"],
186+
implementation_name = plat_env.env.get("implementation_name", "cpython"),
187+
whl_abi_tags = plat_env.whl_abi_tags,
188+
whl_platform_tags = plat_env.whl_platform_tags,
189+
logger = logger,
190+
)
191+
if selected:
192+
if plat_name not in selected.target_platforms:
193+
selected.target_platforms.append(plat_name)
194+
matched_by_any[plat_name] = None
195+
196+
seen = {}
197+
for w in wheels:
198+
if not all_platforms or w.target_platforms:
199+
key = (w.filename, w.sha256)
200+
if key not in seen:
201+
seen[key] = None
202+
entry["src_entries"].append(struct(
203+
version = version,
204+
sha256 = w.sha256,
205+
url = w.url,
206+
filename = w.filename,
207+
# NOTE @aignas 2026-05-17: we don't know if it is yanked because we are not
208+
# checking the SimpleAPI, maybe we should?
209+
yanked = None,
210+
target_platforms = w.target_platforms,
211+
))
212+
213+
fallback_platforms = [p for p in valid_platforms if p not in matched_by_any] if all_platforms else []
214+
sdist = pkg.get("sdist", None)
215+
if sdist:
216+
if not all_platforms or fallback_platforms:
217+
sha256 = sdist.get("hash", "").removeprefix("sha256:")
218+
url = sdist["url"]
219+
_, _, filename = url.rpartition("/")
151220
entry["src_entries"].append(struct(
152221
version = version,
153222
sha256 = sha256,
154223
url = url,
155224
filename = filename,
156-
# NOTE @aignas 2026-05-17: we don't know if it is yanked because we are not
157-
# checking the SimpleAPI, maybe we should?
158225
yanked = None,
226+
target_platforms = fallback_platforms,
159227
))
160-
161-
sdist = pkg.get("sdist", None)
162-
if sdist:
163-
sha256 = sdist.get("hash", "").removeprefix("sha256:")
164-
url = sdist["url"]
165-
_, _, filename = url.rpartition("/")
166-
entry["src_entries"].append(struct(
167-
version = version,
168-
sha256 = sha256,
169-
url = url,
170-
filename = filename,
171-
yanked = None,
172-
))
173228
elif pkg.get("source", {}).get("git"):
174-
_add_vcs_entry(entry, version, pkg["source"])
229+
if not all_platforms or fallback_platforms:
230+
_add_vcs_entry(entry, version, pkg["source"], fallback_platforms)
175231
elif pkg.get("source", {}).get("url"):
176-
_add_direct_url_entry(entry, version, pkg["source"])
232+
if not all_platforms or fallback_platforms:
233+
_add_direct_url_entry(entry, version, pkg["source"], fallback_platforms)
177234

178235
ret = []
179236
for norm_name, info in sorted(uv_packages.items()):
@@ -192,7 +249,7 @@ def _parse_uv_lock(uv_lock, all_platforms, logger, extra_pip_args = None):
192249
distribution = info["distribution"],
193250
extra_pip_args = extra_pip_args or [],
194251
requirement_line = requirement_line,
195-
target_platforms = list(all_platforms),
252+
target_platforms = src_entry.target_platforms,
196253
filename = src_entry.filename,
197254
sha256 = src_entry.sha256,
198255
url = src_entry.url,
@@ -214,13 +271,14 @@ def _parse_uv_lock(uv_lock, all_platforms, logger, extra_pip_args = None):
214271
logger.debug(lambda: "Parsed {} packages from uv.lock".format(len(ret)))
215272
return ret
216273

217-
def _add_vcs_entry(entry, version, source):
274+
def _add_vcs_entry(entry, version, source, valid_platforms):
218275
"""Add a VCS entry from a uv.lock source.
219276
220277
Args:
221278
entry: {type}`dict` The package entry being built.
222279
version: {type}`str` The package version.
223280
source: {type}`dict` The source dict from uv.lock (e.g. {"git": url}).
281+
valid_platforms: {type}`list[str]` Valid target platforms.
224282
"""
225283
url = source["git"]
226284
_, _, filename = url.rpartition("/")
@@ -230,15 +288,17 @@ def _add_vcs_entry(entry, version, source):
230288
url = url,
231289
filename = filename,
232290
yanked = None,
291+
target_platforms = valid_platforms,
233292
))
234293

235-
def _add_direct_url_entry(entry, version, source):
294+
def _add_direct_url_entry(entry, version, source, valid_platforms):
236295
"""Add a direct URL entry from a uv.lock source.
237296
238297
Args:
239298
entry: {type}`dict` The package entry being built.
240299
version: {type}`str` The package version.
241300
source: {type}`dict` The source dict from uv.lock (e.g. {"url": url}).
301+
valid_platforms: {type}`list[str]` Valid target platforms.
242302
"""
243303
url = source["url"]
244304
_, _, filename = url.rpartition("/")
@@ -248,6 +308,7 @@ def _add_direct_url_entry(entry, version, source):
248308
url = url,
249309
filename = filename,
250310
yanked = None,
311+
target_platforms = valid_platforms,
251312
))
252313

253314
def _parse_requirements_from_req_files(

0 commit comments

Comments
 (0)