Add Python 3.15 builtins updates#15724
Conversation
This comment has been minimized.
This comment has been minimized.
|
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]
|
| 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]: ... |
There was a problem hiding this comment.
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'}| @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]: ... |
There was a problem hiding this comment.
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:
| @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: ... |
There was a problem hiding this comment.
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__.
| @final | ||
| class sentinel: | ||
| __name__: str | ||
| __module__: str |
There was a problem hiding this comment.
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 attributeThough I know we're thinking of changing that for __module__.
| def __new__(cls, name: str, /) -> Self: ... | ||
| def __copy__(self, /) -> Self: ... | ||
| def __deepcopy__(self, memo: Any, /) -> Self: ... |
There was a problem hiding this comment.
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
| 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: ... |
Summary
frozendict,sentinel, lazy imports, genericslice, andbytearray.take_bytes.frozendictsupport incollections/opcode.Relevant report entries
builtins.frozendictand allowed it instr.maketrans,eval, andexecglobals (CPython PR: PEP 814: Add built-in frozendict type cpython#141510; source: https://github.com/python/cpython/blob/main/Objects/dictobject.c)builtins.sentinel(CPython PR: Implement PEP 661 Sentinel Values cpython#148829; source: https://github.com/python/cpython/blob/main/Objects/sentinelobject.c)bytearray.take_bytes(CPython PR: Add.take_bytes([n])a zero-copy path frombytearraytobytescpython#139871; source: https://github.com/python/cpython/blob/main/Objects/bytearrayobject.c)slice(already present upstream; CPython PR: Make slice generic cpython#128335; source: https://github.com/python/cpython/blob/main/Objects/sliceobject.c)builtins.ImportCycleErrorandbuiltins.__lazy_import__for lazy imports (CPython source: https://github.com/python/cpython/blob/e81025e6d2e01e38c5a5b656af5739ac9df5ff55/Python/bltinmodule.c)opcode.opmaptofrozendictand addedpathlib.PurePath.__vfspath__()for Python 3.15 (CPython sources: https://github.com/python/cpython/blob/e81025e6d2e01e38c5a5b656af5739ac9df5ff55/Lib/opcode.py, https://github.com/python/cpython/blob/e81025e6d2e01e38c5a5b656af5739ac9df5ff55/Lib/pathlib/__init__.py)