Skip to content

Add Python 3.15 builtins updates#15724

Merged
JelleZijlstra merged 4 commits into
python:mainfrom
JelleZijlstra:codex/python315-builtins
May 8, 2026
Merged

Add Python 3.15 builtins updates#15724
JelleZijlstra merged 4 commits into
python:mainfrom
JelleZijlstra:codex/python315-builtins

Conversation

@JelleZijlstra
Copy link
Copy Markdown
Member

@JelleZijlstra JelleZijlstra commented May 8, 2026

Summary

  • Add Python 3.15 builtins updates for frozendict, sentinel, lazy imports, generic slice, and bytearray.take_bytes.
  • Update directly related frozendict support in collections/opcode.
  • Remove corresponding 3.15 stubtest allowlist entries.

Relevant report entries

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

pandas (https://github.com/pandas-dev/pandas)
- pandas/core/computation/ops.py:328: error: Need type annotation for "_binary_ops_dict" (hint: "_binary_ops_dict: dict[<type>, <type>] = ...")  [var-annotated]

@JelleZijlstra JelleZijlstra marked this pull request as ready for review May 8, 2026 18:10
@JelleZijlstra JelleZijlstra merged commit 38143a2 into python:main May 8, 2026
56 checks passed
@JelleZijlstra JelleZijlstra deleted the codex/python315-builtins branch May 8, 2026 19:30
Comment thread stdlib/builtins.pyi
Comment on lines +606 to +619
if sys.version_info >= (3, 15):
@staticmethod
@overload
def maketrans(
x: (
dict[int, _T]
| dict[str, _T]
| dict[str | int, _T]
| frozendict[int, _T]
| frozendict[str, _T]
| frozendict[str | int, _T]
),
/,
) -> dict[int, _T]: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it seems like the runtime error message if you provide a non-(frozen)dict Mapping is now inaccurate:

>>> str.maketrans(types.MappingProxyType({42: 'foo'}))
Traceback (most recent call last):
  File "<python-input-13>", line 1, in <module>
    str.maketrans(types.MappingProxyType({42: 'foo'}))
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: if you give only one argument to maketrans it must be a dict
>>> # au contraire...
>>> str.maketrans(frozendict({42: 'foo'}))
{42: 'foo'}

Comment thread stdlib/builtins.pyi
Comment on lines +1273 to +1288
@overload
def __new__(cls, /) -> frozendict[Any, Any]: ...
@overload
def __new__(cls: type[frozendict[str, _VT]], /, **kwargs: _VT) -> frozendict[str, _VT]: ...
@overload
def __new__(cls, map: SupportsKeysAndGetItem[_KT, _VT], /) -> frozendict[_KT, _VT]: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT
) -> frozendict[str, _VT]: ...
@overload
def __new__(cls, iterable: Iterable[tuple[_KT, _VT]], /) -> frozendict[_KT, _VT]: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT
) -> frozendict[str, _VT]: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An instance of the subclass is returned if you subclass frozendict:

>>> class Foo(frozendict): ...
...
>>> Foo()
Foo()
>>> type(_)
<class '__main__.Foo'>

But these __new__ overloads mean that ty and pyrefly will infer Foo() as resolving to frozendict[Any, Any], which is unfortunate. (Those were the only type checkers I checked other than mypy, which infers Foo for Foo().)

I wondered if you could do this, but mypy has lots of (I think legitimate) complaints about that, so I don't think it's really an option:

Suggested change
@overload
def __new__(cls, /) -> frozendict[Any, Any]: ...
@overload
def __new__(cls: type[frozendict[str, _VT]], /, **kwargs: _VT) -> frozendict[str, _VT]: ...
@overload
def __new__(cls, map: SupportsKeysAndGetItem[_KT, _VT], /) -> frozendict[_KT, _VT]: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT
) -> frozendict[str, _VT]: ...
@overload
def __new__(cls, iterable: Iterable[tuple[_KT, _VT]], /) -> frozendict[_KT, _VT]: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT
) -> frozendict[str, _VT]: ...
@overload
def __new__(cls, /) -> Self: ...
@overload
def __new__(cls: type[frozendict[str, _VT]], /, **kwargs: _VT) -> Self: ...
@overload
def __new__(cls, map: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT
) -> Self: ...
@overload
def __new__(cls, iterable: Iterable[tuple[_KT, _VT]], /) -> Self: ...
@overload
def __new__(
cls: type[frozendict[str, _VT]], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT
) -> Self: ...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Another option is to lie and implement this in __init__ instead, matching __dict__, but that would be inaccurate to the runtime since frozendict constructs itself purely in __new__.

Comment thread stdlib/builtins.pyi
@final
class sentinel:
__name__: str
__module__: str
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These are both read-only at runtime -- couldn't we make these @propertys to reflect that?

>>> X = sentinel("X")
>>> X.__name__ = 'Y'
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    X.__name__ = 'Y'
    ^^^^^^^^^^
AttributeError: readonly attribute
>>> X.__module__ = 'foo'
Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    X.__module__ = 'foo'
    ^^^^^^^^^^^^
AttributeError: readonly attribute

Though I know we're thinking of changing that for __module__.

Comment thread stdlib/builtins.pyi
Comment on lines +2062 to +2064
def __new__(cls, name: str, /) -> Self: ...
def __copy__(self, /) -> Self: ...
def __deepcopy__(self, memo: Any, /) -> Self: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Self is accurate here, but since sentinel is @final I feel like we might as well just return sentinel directly? It saves a bit of work for type checkers

Suggested change
def __new__(cls, name: str, /) -> Self: ...
def __copy__(self, /) -> Self: ...
def __deepcopy__(self, memo: Any, /) -> Self: ...
def __new__(cls, name: str, /) -> sentinel: ...
def __copy__(self, /) -> sentinel: ...
def __deepcopy__(self, memo: Any, /) -> sentinel: ...

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.

3 participants