[EngSys] Detect compiled dev requirements deterministically instead of regex-matching [tool.cibuildwheel]
维护者通常 1 天内回复
@danieljurek 已经在做这个了。
开始于 2026年7月30日。
评估
这个 Issue 还没有评估数据。
描述
Summary
PR #48371 added a Check for compiled dev requirements step to eng/pipelines/templates/jobs/live.tests.yml (line 191). It gates a set of cibuildwheel configuration steps on whether the service under test contains a package that needs them.
The gate works, but it is a heuristic: it recursively globs sdk/<ServiceDirectory> for pyproject.toml files and regex-matches ^\[tool\.cibuildwheel\].
$found += Get-ChildItem -Path $root -Filter pyproject.toml -Recurse -ErrorAction SilentlyContinue `
| Where-Object { (Get-Content $_.FullName -Raw) -match '(?m)^\[tool\.cibuildwheel\]' }
The problem
[tool.cibuildwheel] is not what actually triggers cibuildwheel. create_package branches on setup_parsed.ext_modules — see ci_tools/build.py:243 and :292. It never reads the [tool.cibuildwheel] section.
Worse, for the one package this currently matters for, the file being grepped isn't where the truth lives. azure-storage-extensions has no [tool.setuptools.ext-modules] in its pyproject.toml at all; ParsedSetup falls back to parsing the adjacent setup.py (parse_functions.py:713-722). The regex happens to match because that package also declares cibuildwheel config — the two are correlated today, not causally linked.
I scanned every package under sdk/ and confirmed the sets are currently identical:
has ext_modules : ['sdk/storage/azure-storage-extensions']
has [tool.cibuildwheel]: ['sdk/storage/azure-storage-extensions']
ext_modules but NO cibuildwheel section (missed by current check): []
So there is no active bug — this is latent. A package that grows an extension without adding a [tool.cibuildwheel] section (relying on cibuildwheel defaults) would silently fail to be detected, and live tests for that service would start failing at dev-requirement install exactly the way storage did — with a root cause that is considerably harder to spot the second time.
Secondary issue: the gate is scoped too broadly
The check answers "does this service directory contain a compiled package?" The condition that actually matters is "does any package under test in this job have a relative dev requirement that resolves to a package with ext_modules?" — which is what build_whl_for_req actually evaluates at runtime. A job targeting a single storage package that has no such dev requirement still gets the variables set.
Proposed solutions
Option A — Reuse package properties / ENABLE_EXTENSION_BUILD (not viable)
The natural instinct is to reuse what the build pipeline does. steps/resolve-build-platforms.yml sets ENABLE_EXTENSION_BUILD by reading the PackageInfo JSON folder.
Two independent blockers:
-
The folder doesn't exist in this job.
live.tests.ymlcallsresolve-package-targeting.ymlwithoutPackagePropertiesFolder, which defaults to''. NoPackageInfofolder is produced anywhere in the live test chain.jobs/ci.tests.ymldoes pass it;jobs/live.tests.ymldoes not. -
The schema has no field for this anyway.
eng/scripts/get_package_properties.pyemits onlyname,version,is_new_sdk, folder, anddependent_packages. There is nothing about extensions or compilation.
That is precisely why resolve-build-platforms.yml resorts to:
if ($packageProperties -contains "azure-storage-extensions") {
Adopting this pattern would trade a regex for a hardcoded package name — less deterministic, not more. Worth noting the build pipeline has the same latent problem, just expressed differently.
A variant would be to add an ext_modules / is_compiled field to the package properties schema and plumb PackagePropertiesFolder through live.tests.yml. That's a larger change touching cross-language tooling (Package-Properties.ps1 consumers), and it makes the live test job depend on a save-package-properties step it doesn't currently run.
Option B1 — Query ParsedSetup directly (minimal change)
Keep the current shape and scope; just fix the predicate. Ask the same parser create_package uses:
from ci_tools.parsing import ParsedSetup
ParsedSetup.from_path(package_dir).ext_modules
Walk the package directories under the service directory and check .ext_modules instead of regex-matching the file.
Pros: roughly a one-line semantic change; removes the proxy entirely; no parameter plumbing.
Cons: still service-directory-scoped rather than target-scoped; introduces a Python dependency into a step that is currently pure PowerShell (see the ordering constraint below).
Option B2 — Mirror build_whl_for_req exactly (most faithful)
Replicate what actually happens at runtime. For each package in $(TargetingString), read its dev_requirements.txt, filter with is_relative_install_path, and check .ext_modules on the resolved target:
for pkg in discover_targeted_packages(targeting_string, os.path.join("sdk", service_dir)):
for req in open(os.path.join(pkg, "dev_requirements.txt")):
if is_relative_install_path(req.strip(), pkg):
if ParsedSetup.from_path(os.path.abspath(os.path.join(pkg, req.strip()))).ext_modules:
# cibuildwheel will run
This is a line-for-line mirror of build_whl_for_req — the function that invokes cibuildwheel. It uses the same ParsedSetup, the same is_relative_install_path, and the same dev-requirements traversal, so it cannot drift from the behavior it is predicting.
I prototyped this locally against the real repo:
| Input | Result |
|---|---|
azure-storage-blob / storage |
true — found azure-storage-extensions, 1 ext module |
azure-keyvault-keys / keyvault |
false |
It also tightens the gate from service directory to packages actually under test, addressing the secondary issue above.
Pros: correct predicate, correct scope, structurally incapable of diverging from runtime behavior.
Cons: most code; same ordering constraint as B1; ~1s per package parse cost (ParsedSetup execs setup.py).
Ordering constraint (applies to both B1 and B2)
ci_tools is not importable where the step currently sits. In live.tests.yml the new steps run before the build-test.yml template, and everything needed arrives inside it:
| What | Where |
|---|---|
| Interpreter selection | steps/build-test.yml → use-python-version.yml |
| Virtualenv | steps/build-test.yml → use-venv.yml |
azure-sdk-tools install |
steps/build-test.yml → Prep Environment (pip install -r eng/ci_tools.txt) |
build-test.yml exposes a BeforeTestSteps parameter that runs after Prep Environment and before Run Tests — exactly the window required. All four cibuildwheel steps would move into that hook.
There is a latent bug in the way: live.tests.yml declares BeforeTestSteps as a parameter (line 20) but never forwards it to build-test.yml. Live-test callers that set it have their steps silently dropped today. jobs/ci.tests.yml forwards correctly. stages/archetype-sdk-tests.yml does pass the parameter into live.tests.yml, so this is reachable from real callers — stages/cosmos-sdk-client.yml is an existing consumer of BeforeTestSteps.
Wiring it through is ~3 lines and fixes that bug as a side effect. It should arguably be fixed regardless of what happens to this issue.
Option C — Narrow what needs gating at all
Orthogonal simplification, combinable with any of the above.
The CIBW_* environment variables are inert unless cibuildwheel actually runs. Setting CIBW_ARCHS, CIBW_SKIP, CIBW_TEST_SKIP, and CIBW_ENVIRONMENT_PASS_LINUX in a job that never invokes cibuildwheel has no effect whatsoever.
So the Configure cibuildwheel for dev requirement builds step could be made unconditional at zero risk. Only the two Windows NuGet steps genuinely need gating, because those cost wall-clock time and can fail.
That shrinks the surface the detection logic has to protect from four steps to two, and correspondingly reduces the cost of the detection being wrong: a false negative would then only mean a Windows job fetching CPython from a blocked endpoint (loud, obvious) rather than a silent misconfiguration.
Recommendation
[!NOTE]
The following recommendation was produced by an LLM agent (GitHub Copilot CLI) during the investigation that led to PR #48371. It reflects analysis of the code paths cited above and locally prototyped verification, but has not been reviewed by a human engineer. Treat it as a starting point rather than a decision.
Option B2 (mirror build_whl_for_req) via BeforeTestSteps, combined with Option C.
Rationale:
- C first, independently — make the
Configure cibuildwheelstep unconditional. It is free, reduces the gated surface to the two Windows NuGet steps, and lowers the blast radius of any detection mistake. - Fix the
BeforeTestStepsforwarding bug inlive.tests.ymlregardless. It's a genuine defect with a ~3-line fix, and it unblocks the ordering constraint. - Then B2, because it is the only option that cannot drift: it calls the same functions the runtime calls. B1 is a reasonable fallback if the parameter plumbing is unwanted — it is strictly better than the current regex for roughly one added line, and keeps every step where it is today.
Option A should be ruled out unless someone is separately motivated to add a compiled/extension field to the package properties schema, in which case resolve-build-platforms.yml's hardcoded azure-storage-extensions string would be worth fixing at the same time.
Priority: low. There is no active bug — the heuristic and the truth agree on every package in the repo today. This is about preventing a confusing failure mode later, and the cost of that failure mode is a service's live tests breaking with a non-obvious root cause.
Context
- 主要语言
- Python
- 星标
- 5.6k
- 派生
- 3.4k
- 平均合并
- 1 天 18 小时
- 30 天内合并 PR
- 214
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
Azure/azure-sdk-for-python 的其他 Issue
-
Evaluation Service Attention
难度 2/5 1-3 小时 新手友好度 84/100
Azure/azure-sdk-for-python#49190 · 1 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 90/100
Azure/azure-sdk-for-python#49183 · 1 个 reaction ·
维护者通常 1 天内回复
-
Evaluation Service Attention
难度 2/5 1-3 小时 新手友好度 75/100
Azure/azure-sdk-for-python#49153 · 1 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
Search Service Attention
难度 2/5 1-3 小时 新手友好度 74/100
Azure/azure-sdk-for-python#48555 · 1 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
Azure.Core customer-reported feature-request needs-team-attention
难度 2/5 1-3 小时 新手友好度 76/100
Azure/azure-sdk-for-python#47186 ·
维护者通常 1 天内回复
查看 Azure/azure-sdk-for-python 的全部 Issue
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 72/100
letsencrypt/cp-cps#353 ·
-
难度 2/5 1-3 小时 新手友好度 68/100
-
难度 2/5 1-3 小时 新手友好度 84/100
PedestrianDynamics/pyFDS-Evac#394 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
DOI-USGS/pywatershed#421 ·
-
难度 2/5 1-3 小时 新手友好度 78/100
python-pillow/Pillow#10087 · 1 条评论 ·
维护者通常 1 天内回复