Skip to content

Pin secure test dependency floors and drop EOL Python 3.9/3.8#2045

Merged
rchiodo merged 4 commits into
microsoft:mainfrom
rchiodo:rchiodo-test-dep-versions-per-python
Jun 24, 2026
Merged

Pin secure test dependency floors and drop EOL Python 3.9/3.8#2045
rchiodo merged 4 commits into
microsoft:mainfrom
rchiodo:rchiodo-test-dep-versions-per-python

Conversation

@rchiodo

@rchiodo rchiodo commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Supersedes #2043. Adds the secure minimum-version floors for test dependencies that Component Governance flags as vulnerable, and drops Python 3.9/3.8 (both EOL) so those floors are installable.

The pinned versions in #2043pytest>=9.0.3, requests>=2.33.0, urllib3>=2.7.0 — all require Python >=3.10, so on the 3.9 CI legs pip install -r tests/requirements.txt failed with No matching distribution found for pytest>=9.0.3 (every 9.x release is Requires-Python >=3.10). Since Python 3.9 (and 3.8) are past end-of-life, the right fix is to stop testing/claiming support for them rather than weaken the security floors.

CVEs addressed

CVE Package Severity Vulnerable Fixed in
CVE-2026-44431 / CVE-2026-44432 urllib3 High 2.6.0 – 2.6.x 2.7.0
CVE-2026-25645 requests Medium < 2.33.0 2.33.0
CVE-2025-71176 pytest Medium <= 9.0.2 9.0.3

Changes

  • tests/requirements.txt: pytest>=9.0.3, requests>=2.33.0, urllib3>=2.7.0 (explicit floor since it's pulled in transitively by requests).
  • azure-pipelines/pipelines.yaml: remove py39 from the Linux/macOS/Windows test matrices, remove the py39_32 (32-bit) leg, and bump the Lint stage to Python 3.10.
  • tox.ini: drop py38/py39 from envlist and the per-env commands.
  • setup.py: python_requires=">=3.10" and remove the 3.8/3.9 classifiers.

Notes

  • 32-bit Windows testing is dropped (it only existed on the removed 3.9 leg).
  • The vendored components under src/debugpy/_vendored/pydevd/ are out of scope here.

pytest>=9.0.3, requests>=2.33.0 and urllib3>=2.7.0 all require Python >=3.10, so they have no installable candidate on Python 3.9. Since Python 3.9 and 3.8 are EOL, drop them from the test matrix and bump the minimum supported version to 3.10, and add the CVE-driven dependency floors (supersedes microsoft#2043).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rchiodo rchiodo requested a review from a team as a code owner June 24, 2026 19:25
@heejaechang

Copy link
Copy Markdown
Contributor

GitHub cannot anchor PR review comments to unchanged lines in the diff. Falling back to a general PR comment for azure-pipelines/pipelines.yaml:L219.

Removing py39_32 eliminates the only x86 CI leg, but setup.py still packages win32 32-bit wheels and native pydevd attach binaries. That leaves a shipped 32-bit target with zero 32-bit signal, so architecture-specific regressions (pointer width, ctypes struct layout, calling convention) can ship undetected. Dropping the EOL interpreter and dropping the sole x86 leg are independent decisions: either re-add one x86 leg on a supported interpreter (e.g. py310_32: { python.version: 3.10, architecture: x86 }) or explicitly confirm in the PR that 32-bit Windows wheels are being discontinued in lockstep.

@heejaechang heejaechang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo

rchiodo commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 804b463 — restored a 32-bit Windows CI leg on a supported interpreter (`py310_32: { python.version: 3.10, architecture: x86 }`) in the `Test_Windows` matrix.

Comment thread azure-pipelines/pipelines.yaml
Comment thread tests/requirements.txt
@heejaechang

Copy link
Copy Markdown
Contributor

Approving — the security floors and EOL-interpreter removal are correct. Two follow-ups worth checking before merge: (1) verify the dep set actually installs/collects on the re-added win32 CPython 3.10 leg, and (2) the PR description says "32-bit Windows testing is dropped," but the diff actually re-adds it as py310_32 (architecture: x86) — reconcile the description with the diff.

@heejaechang

Copy link
Copy Markdown
Contributor

Change looks sound. Main residual risk is install-time fragility on the newly-added 32-bit 3.10 leg and the major pytest 8→9 bump with unpinned plugins — worth a quick smoke-install/collect before merge, but not blocking.

@heejaechang heejaechang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

The previously re-added py310_32 (32-bit Windows) leg runs
`pip install -r tests/requirements.txt` on win32 CPython 3.10. gevent
publishes no cp310-win32 wheel (only win_amd64), so pip would fall back
to a source build of gevent/greenlet/libev/c-ares and risk failing the
leg at install -- the same install-failure class this PR removes. numpy
and Cython do ship cp310-win32 wheels, but there is no reliable PEP 508
marker to drop only gevent for a 32-bit interpreter on 64-bit Windows,
so the leg is removed to keep CI green and consistent with the PR intent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
architecture: x86
py310:
python.version: 3.10
py311:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 azure-pipelines/pipelines.yaml:211
This revision removes py39 and py39_32 and adds nothing in their place, so the Windows matrix is now py310py314 with zero 32-bit legs — yet setup.py:is_applicable still packages win32 artifacts (return plat in ("win32", "win-amd64")). debugpy continues to ship a win32 wheel surface with no x86 CI signal. Either restore a single py310_32 (architecture: x86) leg — and smoke-install tests/requirements.txt on win32 cp310 to confirm the unpinned C-extension deps resolve — or remove win32 from setup.py:is_applicable so packaging and CI agree. Do not merge with a shipped-but-untested architecture.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this is a real tension, but I don't think either literal option is the right change to make inside this PR, so I'm flagging it for a maintainer call rather than forcing it here. Reasoning, with evidence:

Option A — restore a py310_32 (x86) leg: This re-introduces the exact failure class this PR exists to remove. As established in the (now-resolved) thread above, gevent ships no cp310-win32 wheel (only ...-cp310-cp310-win_amd64.whl), so pip install -r tests/requirements.txt on win32 cp310 falls back to a source build of gevent (greenlet/libev/c-ares) and risks failing the leg at install. There's no PEP 508 marker that reliably distinguishes a 32-bit interpreter on 64-bit Windows (platform_machine reflects the OS, not the interpreter), so gevent can't be cleanly gated off only the x86 leg without either still source-building or silently dropping gevent coverage. Adding the leg back is therefore a coverage/risk tradeoff that you flagged yourself — a maintainer decision, not something I should force in autonomously.

Option B — remove win32 from setup.py:is_applicable: This doesn't actually stop debugpy from shipping a win32 wheel — is_applicable only filters which vendored pydevd native binaries (.exe/.dll/.pdb/.pyd) get copied into the wheel. Dropping win32 there would still build a win32 wheel, just a broken one missing the attach-to-process binaries. Genuinely retiring the win32 wheel surface means changing the build/packaging matrix and would break existing 32-bit Windows users — clearly out of scope for "drop EOL 3.9/3.8 + pin secure dep floors."

Note this PR didn't newly create the gap: the only prior 32-bit leg was py39_32, removed here solely because Python 3.9 is EOL. The win32 packaging surface is unchanged.

My recommendation: keep win32 packaging as-is in this PR and track "restore an x86 test signal (wheel-only deps, gevent excluded on x86)" or "retire the win32 wheel" as a separate, deliberate follow-up. Happy to implement whichever direction you choose — leaving this thread open for your call.

Comment thread setup.py
@heejaechang

Copy link
Copy Markdown
Contributor

Solid security-floor + EOL-cleanup change overall. One blocking item: reconcile dropping the only 32-bit Windows test leg with still shipping win32 wheels (restore a 32-bit leg on a supported Python, or stop packaging win32). Also align the remaining py38 static-analysis declarations with the new >=3.10 floor.

Bump [tool.pyright] pythonVersion and [tool.ruff] target-version from
3.8/py38 to 3.10/py310 (and update the comment) so the Lint stage and
pyright validate against the supported floor this PR converges on.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
architecture: x86
py310:
python.version: 3.10
py311:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This revision removes both py39 and py39_32 — the base branch's only architecture: x86 leg — leaving the Windows matrix at py310py314 under the global architecture: x64. Meanwhile setup.py (is_applicable) still ships win32 wheels (plat in ("win32", "win-amd64")), so debugpy continues to package an x86 surface with zero x86 CI signal: a win32-only regression in the attach binaries could ship undetected. Consider either restoring one py310 architecture: x86 leg (at minimum smoke-installing tests/requirements.txt on win32 cp310), or narrowing setup.py:is_applicable to drop win32 so packaging and CI coverage agree.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this is the same x86-CI vs. win32-packaging tension raised (and discussed in depth) in the thread above, so I'll consolidate here and keep it open for a maintainer call rather than force a change in this PR.

Both options you list have been evaluated, with evidence:

  • Restore a py310 x86 leg. I actually tried this in this PR (804b4639 "ci: restore x86 Windows leg on Python 3.10") and had to revert it (d2c3ac40 "ci: drop x86 Windows leg (gevent has no cp310-win32 wheel)"). gevent publishes no cp310-win32 wheel, so pip install -r tests/requirements.txt on win32 cp310 falls back to a source build (greenlet/libev/c-ares) and risks failing the leg at install — the exact failure class this PR exists to remove. There's also no PEP 508 marker that reliably distinguishes a 32-bit interpreter on 64-bit Windows (platform_machine reflects the OS, not the interpreter), so gevent can't be cleanly excluded on just the x86 leg.

  • Drop win32 from setup.py:is_applicable. is_applicable only filters which vendored pydevd native binaries get copied into the wheel; it does not stop a win32 wheel from being built. Removing win32 there would still ship a win32 wheel — just a broken one missing the attach-to-process binaries. Genuinely retiring the win32 wheel surface is a packaging-matrix change that would break existing 32-bit users, which is out of scope for "drop EOL 3.9/3.8 + pin secure dep floors."

For context, this PR didn't create the gap: the only prior x86 leg was py39_32, removed here solely because Python 3.9 is EOL; the win32 packaging surface is unchanged.

Recommendation: keep win32 packaging as-is in this PR (which you've approved) and track "restore an x86 test signal with wheel-only deps (gevent excluded on x86)" or "retire the win32 wheel surface" as a separate, deliberate follow-up. Happy to implement whichever direction you choose — leaving this open for your call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this is the same x86-CI vs. win32-packaging tension raised (and discussed in depth) in the thread above, so I'll consolidate here and keep it open for a maintainer call rather than force a change in this PR.

Both options you list have been evaluated, with evidence:

  • Restore a py310 x86 leg. I actually tried this in this PR (804b4639 "ci: restore x86 Windows leg on Python 3.10") and had to revert it (d2c3ac40 "ci: drop x86 Windows leg (gevent has no cp310-win32 wheel)"). gevent publishes no cp310-win32 wheel, so pip install -r tests/requirements.txt on win32 cp310 falls back to a source build (greenlet/libev/c-ares) and risks failing the leg at install — the exact failure class this PR exists to remove. There's also no PEP 508 marker that reliably distinguishes a 32-bit interpreter on 64-bit Windows (platform_machine reflects the OS, not the interpreter), so gevent can't be cleanly excluded on just the x86 leg.

  • Drop win32 from setup.py:is_applicable. is_applicable only filters which vendored pydevd native binaries get copied into the wheel; it does not stop a win32 wheel from being built. Removing win32 there would still ship a win32 wheel — just a broken one missing the attach-to-process binaries. Genuinely retiring the win32 wheel surface is a packaging-matrix change that would break existing 32-bit users, which is out of scope for "drop EOL 3.9/3.8 + pin secure dep floors."

For context, this PR didn't create the gap: the only prior x86 leg was py39_32, removed here solely because Python 3.9 is EOL; the win32 packaging surface is unchanged.

Recommendation: keep win32 packaging as-is in this PR (which you've approved) and track "restore an x86 test signal with wheel-only deps (gevent excluded on x86)" or "retire the win32 wheel surface" as a separate, deliberate follow-up. Happy to implement whichever direction you choose — leaving this open for your call.

@heejaechang

Copy link
Copy Markdown
Contributor

Security floors and the 3.8/3.9 removal look consistent across setup.py, pyproject.toml, tox.ini, and the CI matrices. Two non-blocking things worth a look before merge: (1) dropping the only x86 leg while still shipping win32 wheels leaves that surface without CI signal, and (2) the pytest 8→9 major bump with floor-less plugins — a quick pip install -r tests/requirements.txt + pytest --collect-only on 3.10 would confirm the matrix still resolves.

@heejaechang heejaechang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo rchiodo merged commit cd699b4 into microsoft:main Jun 24, 2026
21 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants