build!: drop python 3.9 and 3.10, drop pyside2, require >=3.11 - #745
Merged
Conversation
BREAKING CHANGE: python 3.9 and 3.10 are no longer supported, and the PySide2 backend is dropped along with them (PySide2 5.15.2.1 declares `requires_python <3.11` and ships no wheel past cp310, so it cannot be installed or tested on the new floor). - requires-python = ">=3.11"; drop the 3.9/3.10 classifiers - drop the pyside2 extra, dependency-group, docs and CI jobs, along with the test-qt-legacy group and [tool.uv] conflicts block that existed only to keep PySide2 on pytest-qt 4.4 - CI: base matrix 3.10 -> 3.11, min-deps 3.9 -> 3.11, pyqt5 job 3.9 -> 3.11, test-dependents default 3.10 -> 3.11 - SLOTS is now unconditional, and the <3.11 weakref_slot skip is dead - ruff target-version py39 -> py311, and pin the dev ruff/mypy to the versions pre-commit already runs (they had drifted to ruff 0.15.12 against pre-commit's 0.16.4) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #745 +/- ##
==========================================
- Coverage 89.32% 89.31% -0.02%
==========================================
Files 40 40
Lines 4835 4849 +14
==========================================
+ Hits 4319 4331 +12
- Misses 516 518 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
larsoner
approved these changes
Aug 26, 2026
larsoner
left a comment
Contributor
There was a problem hiding this comment.
I dropped PySide2 in PyVistaQt as well, no regrets! 😄
tlambert03
added a commit
that referenced
this pull request
Aug 26, 2026
* fix: handle PEP 604 unions (`X | Y`) like `Union[X, Y]`
`get_origin(int | None)` is `types.UnionType`, not `typing.Union`, on
python < 3.14 -- so the four `get_origin(...) is Union` comparisons
silently took the non-union path for PEP 604 annotations.
Most visibly, the Optional wrapper was not stripped from a widget's
reported annotation:
@magicgui
def f(x: Optional[int] = None): ... # .annotation -> int
@magicgui
def f(x: int | None = None): ... # .annotation -> int | None
and `register_type(int | str, return_callback=...)` registered nothing
for the individual member types.
Adds `magicgui._util.is_union`, which accepts both spellings, and uses
it at all four sites.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* style: import Callable from collections.abc in the ipynb backend
UP035 under the new py311 target. #742 merged after #745 switched
target-version, so its CI ran against the old config and main is
currently failing ruff.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* style: enable UP045 for source now that PEP 604 unions work
The preceding fix makes `X | None` behave like `Optional[X]`, so the
pyupgrade rewrite is safe for src. Scoped deliberately:
- UP045 is enabled for src/ and docs/ only; tests/ keep it ignored,
since they exercise both spellings on purpose (see test_no_order).
- UP007 stays ignored: `Union` is still needed as a runtime *value* for
the public type aliases (PathLike, ChoicesType, AppRef, TableData,
WidgetRef) and for `Union[args]` construction. ruff offers no fix for
those 11 sites, so enabling it would just leave permanent errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Drops Python 3.9/3.10 and moves the floor to
>=3.11, plus the ruff updates that fall out of it.Not a choice so much as a consequence: PySide2
5.15.2.1declaresrequires_python: ... <3.11and ships no wheel pastcp310. It cannot be installed on the new floor, so its extra, dependency-group and CI jobs are all dead weight. Removing it also lets us delete thetest-qt-legacygroup and the[tool.uv] conflictsblock from #743, which existed only to hold PySide2 on pytest-qt 4.4.Changes
Version floor
requires-python = ">=3.11", 3.9/3.10 classifiers removed3.10→3.11, min-deps3.9→3.11, pyqt5 job3.9→3.11,test-dependentsdefault3.10→3.11(it would otherwise try to install magicgui on 3.10)SLOTSis unconditional now; the<3.11weakref_slotskip is dead codeRuff
target-versionpy39→py311ruff>=0.8.3resolved to 0.15.12 locally while pre-commit pinned 0.16.4 — different enough thatRUF036is preview in one and stable in the other, souv run ruffand CI disagreed about the codebase.Callable→collections.abc,typing_extensions→typingwhere 3.11 suffices,zip(..., strict=False)(explicit, behavior-preserving), one deadtype: ignoreTwo rules deliberately disabled
UP007/UP045(Optional[X]→X | None) are inignore, because for this codebase they are not a cosmetic change:magicgui compares
get_origin(...) is Unionin four places, so PEP 604 unions take a different path. Letting ruff apply these broke 7 tests intest_ui_field.py— it rewroteOptional[int]toint | NoneandUiFieldthen extracted a different annotation. Tests also deliberately exercise both spellings, so blanket-rewriting them would quietly cost coverage.TC003—collections.abcis added toflake8-type-checking.exempt-modules, alongside thetyping/typing_extensionsthat ruff exempts by default. These 16 findings only appeared becauseCallablemoved offtyping; magicgui resolves annotations at runtime, so those names must stay importable at runtime rather than move intoTYPE_CHECKING.Known pre-existing bug (not fixed here)
The above uncovered a real bug on
main, unrelated to this PR:PEP 604 unions aren't unwrapped by the four
get_origin(...) is Unionchecks on Python 3.11–3.13 (masked on 3.14, wheretypes.UnionTypebecametyping.Union). Worth its own PR; happy to write it.Testing
Full suite green on 3.11 / 3.13 / 3.14, across PyQt6, PyQt5, PySide6.
prek run -aclean.🤖 Generated with Claude Code