Add support for Python 3.14 - #7240
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7240 +/- ##
==========================================
- Coverage 79.74% 79.74% -0.00%
==========================================
Files 566 566
Lines 43882 43895 +13
==========================================
+ Hits 34990 34998 +8
- Misses 8892 8897 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
btw: my previous attempt is at #7074 |
asyncssh 2.19.0 uses a custom `Record` class (in `misc.py`) that breaks on Python 3.14. This causes `AttributeError` on any object built on `Record`, including `SFTPLimits` (missing `max_packet_len`) and `SSHCompletedProcess` (missing `exit_status`, `stdout`, etc.), effectively breaking all `core.ssh_async` transport operations. The fix (migrating from `Record` to Python dataclasses) landed in asyncssh 2.20.0, as tracked in ronf/asyncssh#732. We pin to ~=2.21.0 rather than ~=2.22.0 because asyncssh 2.22.0 dropped support for Python 3.9, which this project still supports.
b4ca165 to
6405e1e
Compare
The orm/test_fields.py test relies on the inbuilt `repr` function of python to compare the orm fields. With python 3.14 the formatting of `repr` changes. To avoid maintaining two sets of referenece files with different formatting we change the reference files to the formatting of 3.14 and run the tests only for 3.14 and above.
…aiidateam#7240) In Python 3.14, `url2pathname` was rewritten to be a full file-URL parser per RFC 8089 (python/cpython#126838). The new implementation prepends `file:` to the input and re-parses it with `urlsplit`, then validates that the URL authority resolves to localhost. This breaks our usage because we pass `urlparse(uri).path` — a pre-parsed path component — not a full URL. The new `url2pathname` misinterprets this input, and raises: URLError: file:// scheme is supported only on localhost Since we already validate that the scheme is `file://` before extracting the path, we replace the stdlib import with a local `url2pathname` shim that preserves the simple Python <= 3.13 behavior.
6405e1e to
e007df4
Compare
asyncssh 2.19.0 uses a custom `Record` class (in `misc.py`) that breaks on Python 3.14. This causes `AttributeError` on any object built on `Record`, including `SFTPLimits` (missing `max_packet_len`) and `SSHCompletedProcess` (missing `exit_status`, `stdout`, etc.), effectively breaking all `core.ssh_async` transport operations. The fix (migrating from `Record` to Python dataclasses) landed in asyncssh 2.20.0, as tracked in ronf/asyncssh#732. We pin to ~=2.21.0 rather than ~=2.22.0 because asyncssh 2.22.0 dropped support for Python 3.9, which this project still supports.
The orm/test_fields.py test relies on the inbuilt `repr` function of python to compare the orm fields. With python 3.14 the formatting of `repr` changes. To avoid maintaining two sets of referenece files with different formatting we change the reference files to the formatting of 3.14 and run the tests only for 3.14 and above.
e007df4 to
a062fb4
Compare
…aiidateam#7240) In Python 3.14, `url2pathname` was rewritten to be a full file-URL parser per RFC 8089 (python/cpython#126838). The new implementation prepends `file:` to the input and re-parses it with `urlsplit`, then validates that the URL authority resolves to localhost. This breaks our usage because we pass `urlparse(uri).path` — a pre-parsed path component — not a full URL. The new `url2pathname` misinterprets this input, and raises: URLError: file:// scheme is supported only on localhost Since we already validate that the scheme is `file://` before extracting the path, we replace the stdlib import with a local `url2pathname` shim that preserves the simple Python <= 3.13 behavior.
|
Test coverage complains because I touch a deprecated method in |
| 'alembic~=1.8', | ||
| 'archive-path~=0.4.2', | ||
| "asyncssh~=2.19.0", | ||
| "asyncssh~=2.21.0", |
There was a problem hiding this comment.
I'd go for 2.22.0
where they have fix an internal race condition
There was a problem hiding this comment.
ok I see. 2.22.0 has dropped 3.9..
| # generated with. Rather than maintaining two sets of reference files, we skip on | ||
| # Python versions that don't match. Once 3.14 becomes the baseline, regenerate the | ||
| # reference files and update this marker. | ||
| skip_below_py314 = pytest.mark.skipif(sys.version_info < (3, 14), reason='typing repr fixtures require Python 3.14+') |
There was a problem hiding this comment.
Would be good to get @edan-bainglass eyes on these changes.
There was a problem hiding this comment.
Yeah, I'd have to test this. The fields system constructs from the model system. Pydantic field typing changes between Python versions if I recall correctly. At some point, one couldn't use int | None without an additional dependency, instead having to use Optional[int]. Anyhow, the commit message of the relevant commit here mentions that repr changed in 3.14. But I think this would be the same result if we uniformly update all field annotations to the newer style, i.e. use pipes.
There was a problem hiding this comment.
Okay then we can try to drop the skipif once we we drop 3.9. I note this into the PR that drops 3.9
|
|
| # https://github.com/python/cpython/blob/1a2b0fb3e5eac4e767e6bbb0b2c3cedaedafc07b/Lib/urllib/request.py#L1664-L1679 | ||
| # Only minor changes were applied to conform with mypy | ||
| if os.name == 'nt': | ||
| from nturl2path import url2pathname |
There was a problem hiding this comment.
This still has the same behavior as before?
There was a problem hiding this comment.
So this is a 1to1 copy from as from py3.13, where this function seemed to still have worked in profile code. The if case is probably not needed because it is for Window NT but i just kept it to be sure to have the same behavior.
Honestly, I'd do that in a follow-up PR, to keep things here clean. Current state looks very good as is, so I'd say this is ready to be merged. Also, just as a reminder, these typing changes, we can easily do via EDIT: Started working on this, out of curiosity ^^ |
GeigerJ2
left a comment
There was a problem hiding this comment.
Great work, very clean! Thanks, @agoscinski 🫶
Wondering why 3.13 psql tests are pending? Maybe we can re-trigger manually? Once we know that everything passes, I'd say this can be merged.
Because of the branch protection rules. We replaced the CI workflow with 3.14, the 3.13 workflow does not exist anymore but because they are in the branch protection rules they still appear here as not being run. After this PR is merged with have to switch the branch protection rule from the CI 3.13 to 3.14. |
asyncssh 2.19.0 uses a custom `Record` class (in `misc.py`) that breaks on Python 3.14. This causes `AttributeError` on any object built on `Record`, including `SFTPLimits` (missing `max_packet_len`) and `SSHCompletedProcess` (missing `exit_status`, `stdout`, etc.), effectively breaking all `core.ssh_async` transport operations. The fix (migrating from `Record` to Python dataclasses) landed in asyncssh 2.20.0, as tracked in ronf/asyncssh#732. We pin to ~=2.21.0 rather than ~=2.22.0 because asyncssh 2.22.0 dropped support for Python 3.9, which this project still supports.
The orm/test_fields.py test relies on the inbuilt `repr` function of python to compare the orm fields. With python 3.14 the formatting of `repr` changes. To avoid maintaining two sets of referenece files with different formatting we change the reference files to the formatting of 3.14 and run the tests only for 3.14 and above.
…aiidateam#7240) In Python 3.14, `url2pathname` was rewritten to be a full file-URL parser per RFC 8089 (python/cpython#126838). The new implementation prepends `file:` to the input and re-parses it with `urlsplit`, then validates that the URL authority resolves to localhost. This breaks our usage because we pass `urlparse(uri).path` — a pre-parsed path component — not a full URL. The new `url2pathname` misinterprets this input, and raises: URLError: file:// scheme is supported only on localhost Since we already validate that the scheme is `file://` before extracting the path, we replace the stdlib import with a local `url2pathname` shim that preserves the simple Python <= 3.13 behavior.
a062fb4 to
f1c8337
Compare
| # Python versions that don't match. Once 3.14 becomes the baseline, regenerate the | ||
| # reference files and update this marker. |
There was a problem hiding this comment.
| # Python versions that don't match. Once 3.14 becomes the baseline, regenerate the | |
| # reference files and update this marker. | |
| # Python versions that don't match. |
|
btw: Would be good to verify that circus works well with Python 3.14 |
|
@danielhollas I dont have any rights for circus, I ran the tests on my own fork now agoscinski/circus#3 I needed to add some minor fixes. My understanding is that existing test errors will not affect aiida-core. You can check the fixes in the PR by yourself if you want. One test error is the change of In principle one can fix these issues upstream but the only required change in the source code of circus will not affect us and I would like to remove circus as dependency latest in the next minor release. |
|
@danielhollas I will remove the py3.14 classifier in the pyproject.toml for now until you give me the go. But will merge the current changes to main, so we can start another prerelease. For the actual release we can then decide if we want to classify it as officially supporting py3.14. |
asyncssh 2.19.0 uses a custom `Record` class (in `misc.py`) that breaks on Python 3.14. This causes `AttributeError` on any object built on `Record`, including `SFTPLimits` (missing `max_packet_len`) and `SSHCompletedProcess` (missing `exit_status`, `stdout`, etc.), effectively breaking all `core.ssh_async` transport operations. The fix (migrating from `Record` to Python dataclasses) landed in asyncssh 2.20.0, as tracked in ronf/asyncssh#732. We pin to ~=2.21.0 rather than ~=2.22.0 because asyncssh 2.22.0 dropped support for Python 3.9, which this project still supports.
The orm/test_fields.py test relies on the inbuilt `repr` function of python to compare the orm fields. With python 3.14 the formatting of `repr` changes. To avoid maintaining two sets of referenece files with different formatting we change the reference files to the formatting of 3.14 and run the tests only for 3.14 and above.
…aiidateam#7240) In Python 3.14, `url2pathname` was rewritten to be a full file-URL parser per RFC 8089 (python/cpython#126838). The new implementation prepends `file:` to the input and re-parses it with `urlsplit`, then validates that the URL authority resolves to localhost. This breaks our usage because we pass `urlparse(uri).path` — a pre-parsed path component — not a full URL. The new `url2pathname` misinterprets this input, and raises: URLError: file:// scheme is supported only on localhost Since we already validate that the scheme is `file://` before extracting the path, we replace the stdlib import with a local `url2pathname` shim that preserves the simple Python <= 3.13 behavior.
f1c8337 to
3e6e2a3
Compare
asyncssh 2.19.0 uses a custom `Record` class (in `misc.py`) that breaks on Python 3.14. This causes `AttributeError` on any object built on `Record`, including `SFTPLimits` (missing `max_packet_len`) and `SSHCompletedProcess` (missing `exit_status`, `stdout`, etc.), effectively breaking all `core.ssh_async` transport operations. The fix (migrating from `Record` to Python dataclasses) landed in asyncssh 2.20.0, as tracked in ronf/asyncssh#732. We pin to ~=2.21.0 rather than ~=2.22.0 because asyncssh 2.22.0 dropped support for Python 3.9, which this project still supports.
The orm/test_fields.py test relies on the inbuilt `repr` function of python to compare the orm fields. With python 3.14 the formatting of `repr` changes. To avoid maintaining two sets of referenece files with different formatting we change the reference files to the formatting of 3.14 and run the tests only for 3.14 and above.
danielhollas
left a comment
There was a problem hiding this comment.
@agoscinski thanks, this PR looks good to me, seems fine to add the marker hopefully after some real-world testing with python 3.14 with the new rc release.
Split from PR #7233.
Each required change is explained in the commit message. I tried to not introduce changes in the behavior of the functionalities but to continue to use the old behavior for py3.14. For example I copied the old
url2pathnamefunction from python that was changed in py3.14.I marked the PR number in the commit message so we can identify that all commits are related without doing a merge commit.
After this PR is merged we need to update the branch protection rules to switch from 3.13 to 3.14.