Package: src:pydantic
Version: 2.13.4-3
User: [email protected]
Usertags: python3.15
Tags: patch
Hi!
While rebuilding the python related packages against the Python 3.15rc1
version we found that pydantic fails to build from source [1].
We found that the necessary fixes have recently been applied in the
upstream repository [2].
I applied the upstream fix in the sandbox [3] to be able to build the
packages that depend on pydantic, please consider applying the patch to
support the upcoming 3.15 version.
Happy hacking,
[1]: https://debusine.debian.net/debian/r-python-python3.15/artifact/4439432/
[2]: https://github.com/pydantic/pydantic/pull/13587
[3]: https://debusine.debian.net/debian/r-python-python3.15/
--
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
commit b348cf0dce91e1cf1a3e918402e4bb8e4e6203b2
Author: Victorien <[email protected]>
Date: Thu Aug 6 13:34:53 2026 +0200
Add initial support for Python 3.15 (#13587)
Index: pydantic/pydantic/_internal/_model_construction.py
===================================================================
--- pydantic.orig/pydantic/_internal/_model_construction.py
+++ pydantic/pydantic/_internal/_model_construction.py
@@ -8,6 +8,7 @@ import typing
import warnings
import weakref
from abc import ABCMeta
+from collections.abc import MutableMapping
from functools import cache, partial, wraps
from types import FunctionType
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, cast
@@ -812,7 +813,7 @@ class _PydanticWeakRef:
return _PydanticWeakRef, (self(),)
-def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
+def build_lenient_weakvaluedict(d: MutableMapping[str, Any] | None) -> dict[str, Any] | None:
"""Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs.
We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values
Index: pydantic/pydantic/_internal/_typing_extra.py
===================================================================
--- pydantic.orig/pydantic/_internal/_typing_extra.py
+++ pydantic/pydantic/_internal/_typing_extra.py
@@ -7,6 +7,7 @@ import re
import sys
import types
import typing
+from collections.abc import MutableMapping
from functools import partial
from inspect import Signature, signature
from typing import TYPE_CHECKING, Any, Callable, cast
@@ -212,7 +213,7 @@ typing_base: Any = typing._Final # pyri
### Annotation evaluations functions:
-def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None:
+def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> MutableMapping[str, Any] | None:
"""Fetch the local namespace of the parent frame where this function is called.
Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace,
Index: pydantic/pydantic/types.py
===================================================================
--- pydantic.orig/pydantic/types.py
+++ pydantic/pydantic/types.py
@@ -5,6 +5,7 @@ from __future__ import annotations as _a
import base64
import dataclasses as _dataclasses
import re
+import sys
from collections.abc import Hashable, Iterator
from datetime import date, datetime
from decimal import Decimal
@@ -2434,6 +2435,9 @@ class Base64Encoder(EncoderProtocol):
return 'base64'
+_urlsafe_translation = bytes.maketrans(b'+/', b'-_')
+
+
class Base64UrlEncoder(EncoderProtocol):
"""URL-safe Base64 encoder."""
@@ -2448,7 +2452,15 @@ class Base64UrlEncoder(EncoderProtocol):
The decoded data.
"""
try:
- return base64.urlsafe_b64decode(data)
+ if sys.version_info >= (3, 15):
+ # In Python >= 3.15, `urlsafe_b64decode()` doesn't require padded input anymore.
+ # It also raises a `FutureWarning` if '+' or '/' is found in input (and translates it
+ # to '-' and '_'). We can't have this warning raised while validating, so we do the translation
+ # ourselves.
+ data = data.translate(_urlsafe_translation)
+ return base64.urlsafe_b64decode(data, padded=True)
+ else:
+ return base64.urlsafe_b64decode(data)
except ValueError as e:
raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)})
@@ -2799,11 +2811,8 @@ Base64UrlBytes = Annotated[bytes, Encode
"""A bytes type that is encoded and decoded using the URL-safe base64 encoder.
Note:
- Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
- functions.
-
- As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data
- (using `'+'` and `'/'`).
+ Under the hood, `Base64UrlBytes` uses the standard library [`base64.urlsafe_b64encode()`][base64.urlsafe_b64encode]
+ and [`base64.urlsafe_b64decode()`][base64.urlsafe_b64decode] functions.
```python
from pydantic import Base64UrlBytes, BaseModel
@@ -2821,10 +2830,8 @@ Base64UrlStr = Annotated[str, EncodedStr
"""A str type that is encoded and decoded using the URL-safe base64 encoder.
Note:
- Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
- functions.
-
- As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`).
+ Under the hood, `Base64UrlStr` uses the standard library [`base64.urlsafe_b64encode()`][base64.urlsafe_b64encode]
+ and [`base64.urlsafe_b64decode()`][base64.urlsafe_b64decode] functions.
```python
from pydantic import Base64UrlStr, BaseModel
Index: pydantic/pyproject.toml
===================================================================
--- pydantic.orig/pyproject.toml
+++ pydantic/pyproject.toml
@@ -34,6 +34,7 @@ classifiers = [
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
+ 'Programming Language :: Python :: 3.15',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Operating System :: OS Independent',
@@ -117,7 +118,8 @@ testing-extra = [
'devtools',
# used in docs tests
'sqlalchemy',
- 'pytest-memray; platform_python_implementation == "CPython" and platform_system != "Windows"',
+ 'pytest-memray; platform_python_implementation == "CPython" and platform_system != "Windows" and python_version < "3.15"',
+ 'memray; platform_python_implementation == "CPython" and platform_system != "Windows" and python_version < "3.15"',
]
typechecking = [
'mypy',
Index: pydantic/tests/test_generics.py
===================================================================
--- pydantic.orig/tests/test_generics.py
+++ pydantic/tests/test_generics.py
@@ -2295,6 +2295,7 @@ def test_parse_generic_json():
}
[email protected](sys.version_info >= (3, 15), reason="memray doesn't yet support Python 3.15")
def memray_limit_memory(limit):
if '--memray' in sys.argv:
return pytest.mark.limit_memory(limit)