From 709de3a23fef9e192c781d371d7db4004ade759d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:01:31 -0700 Subject: [PATCH 1/9] stdlib: add __slots__ --- stdlib/_collections_abc.pyi | 1 + stdlib/_threading_local.pyi | 2 ++ stdlib/asyncio/events.pyi | 2 ++ stdlib/asyncio/protocols.pyi | 5 +++++ stdlib/asyncio/transports.pyi | 9 ++++++++- stdlib/asyncio/trsock.pyi | 1 + stdlib/contextlib.pyi | 2 ++ stdlib/ctypes/_endian.pyi | 8 ++++++-- stdlib/dataclasses.pyi | 14 ++++++++++++++ stdlib/fractions.pyi | 1 + stdlib/hmac.pyi | 1 + stdlib/importlib/metadata/__init__.pyi | 1 + stdlib/inspect.pyi | 3 +++ stdlib/ipaddress.pyi | 6 ++++++ stdlib/multiprocessing/managers.pyi | 1 + stdlib/numbers.pyi | 5 +++++ stdlib/os/__init__.pyi | 1 + stdlib/pathlib/__init__.pyi | 16 ++++++++++++---- stdlib/pickletools.pyi | 3 +++ stdlib/socket.pyi | 1 + stdlib/statistics.pyi | 1 + stdlib/traceback.pyi | 1 + stdlib/tracemalloc.pyi | 5 +++++ stdlib/typing.pyi | 24 +++++++++++++++++++++++- stdlib/typing_extensions.pyi | 2 ++ stdlib/urllib/parse.pyi | 10 ++++++++-- stdlib/uuid.pyi | 1 + stdlib/weakref.pyi | 3 +++ stdlib/xml/dom/__init__.pyi | 1 + stdlib/xml/dom/expatbuilder.pyi | 5 +++++ stdlib/xml/dom/minicompat.pyi | 2 ++ stdlib/xml/dom/minidom.pyi | 26 ++++++++++++++++++++++++++ stdlib/xml/dom/xmlbuilder.pyi | 2 ++ stdlib/zipfile/__init__.pyi | 23 +++++++++++++++++++++++ 34 files changed, 179 insertions(+), 10 deletions(-) diff --git a/stdlib/_collections_abc.pyi b/stdlib/_collections_abc.pyi index b099bdd98f3c..c63606a13ca9 100644 --- a/stdlib/_collections_abc.pyi +++ b/stdlib/_collections_abc.pyi @@ -103,5 +103,6 @@ class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented if sys.version_info >= (3, 12): @runtime_checkable class Buffer(Protocol): + __slots__ = () @abstractmethod def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/stdlib/_threading_local.pyi b/stdlib/_threading_local.pyi index 07a825f0d816..5f6acaf840aa 100644 --- a/stdlib/_threading_local.pyi +++ b/stdlib/_threading_local.pyi @@ -7,6 +7,7 @@ __all__ = ["local"] _LocalDict: TypeAlias = dict[Any, Any] class _localimpl: + __slots__ = ("key", "dicts", "localargs", "locallock", "__weakref__") key: str dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]] # Keep localargs in sync with the *args, **kwargs annotation on local.__new__ @@ -16,6 +17,7 @@ class _localimpl: def create_dict(self) -> _LocalDict: ... class local: + __slots__ = ("_local__impl", "__dict__") def __new__(cls, /, *args: Any, **kw: Any) -> Self: ... def __getattribute__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... diff --git a/stdlib/asyncio/events.pyi b/stdlib/asyncio/events.pyi index a37f6f697b9a..c1d83d0f37b0 100644 --- a/stdlib/asyncio/events.pyi +++ b/stdlib/asyncio/events.pyi @@ -73,6 +73,7 @@ class _TaskFactory(Protocol): def __call__(self, loop: AbstractEventLoop, factory: _CoroutineLike[_T], /) -> Future[_T]: ... class Handle: + __slots__ = ("_callback", "_args", "_cancelled", "_loop", "_source_traceback", "_repr", "__weakref__", "_context") _cancelled: bool _args: Sequence[Any] def __init__( @@ -85,6 +86,7 @@ class Handle: def get_context(self) -> Context: ... class TimerHandle(Handle): + __slots__ = ["_scheduled", "_when"] def __init__( self, when: float, diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 5425336c49a8..179d611b73fc 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -6,21 +6,25 @@ from typing import Any __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") class BaseProtocol: + __slots__ = () def connection_made(self, transport: transports.BaseTransport) -> None: ... def connection_lost(self, exc: Exception | None) -> None: ... def pause_writing(self) -> None: ... def resume_writing(self) -> None: ... class Protocol(BaseProtocol): + __slots__ = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... class BufferedProtocol(BaseProtocol): + __slots__ = () def get_buffer(self, sizehint: int) -> ReadableBuffer: ... def buffer_updated(self, nbytes: int) -> None: ... def eof_received(self) -> bool | None: ... class DatagramProtocol(BaseProtocol): + __slots__ = () def connection_made(self, transport: transports.DatagramTransport) -> None: ... # type: ignore[override] # addr can be a tuple[int, int] for some unusual protocols like socket.AF_NETLINK. # Use tuple[str | Any, int] to not cause typechecking issues on most usual cases. @@ -30,6 +34,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): + __slots__ = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... diff --git a/stdlib/asyncio/transports.pyi b/stdlib/asyncio/transports.pyi index bce54897f18f..cc870d5e0b9a 100644 --- a/stdlib/asyncio/transports.pyi +++ b/stdlib/asyncio/transports.pyi @@ -8,6 +8,7 @@ from typing import Any __all__ = ("BaseTransport", "ReadTransport", "WriteTransport", "Transport", "DatagramTransport", "SubprocessTransport") class BaseTransport: + __slots__ = ("_extra",) def __init__(self, extra: Mapping[str, Any] | None = None) -> None: ... def get_extra_info(self, name: str, default: Any = None) -> Any: ... def is_closing(self) -> bool: ... @@ -16,11 +17,13 @@ class BaseTransport: def get_protocol(self) -> BaseProtocol: ... class ReadTransport(BaseTransport): + __slots__ = () def is_reading(self) -> bool: ... def pause_reading(self) -> None: ... def resume_reading(self) -> None: ... class WriteTransport(BaseTransport): + __slots__ = () def set_write_buffer_limits(self, high: int | None = None, low: int | None = None) -> None: ... def get_write_buffer_size(self) -> int: ... def get_write_buffer_limits(self) -> tuple[int, int]: ... @@ -32,13 +35,16 @@ class WriteTransport(BaseTransport): def can_write_eof(self) -> bool: ... def abort(self) -> None: ... -class Transport(ReadTransport, WriteTransport): ... +class Transport(ReadTransport, WriteTransport): + __slots__ = () class DatagramTransport(BaseTransport): + __slots__ = () def sendto(self, data: bytes | bytearray | memoryview, addr: _Address | None = None) -> None: ... def abort(self) -> None: ... class SubprocessTransport(BaseTransport): + __slots__ = () def get_pid(self) -> int: ... def get_returncode(self) -> int | None: ... def get_pipe_transport(self, fd: int) -> BaseTransport | None: ... @@ -47,4 +53,5 @@ class SubprocessTransport(BaseTransport): def kill(self) -> None: ... class _FlowControlMixin(Transport): + __slots__ = ("_loop", "_protocol_paused", "_high_water", "_low_water") def __init__(self, extra: Mapping[str, Any] | None = None, loop: AbstractEventLoop | None = None) -> None: ... diff --git a/stdlib/asyncio/trsock.pyi b/stdlib/asyncio/trsock.pyi index 4dacbbd49399..8f4f8f9ccb30 100644 --- a/stdlib/asyncio/trsock.pyi +++ b/stdlib/asyncio/trsock.pyi @@ -14,6 +14,7 @@ _WriteBuffer: TypeAlias = bytearray | memoryview _CMSG: TypeAlias = tuple[int, int, bytes] class TransportSocket: + __slots__ = ("_sock",) def __init__(self, sock: socket.socket) -> None: ... @property def family(self) -> int: ... diff --git a/stdlib/contextlib.pyi b/stdlib/contextlib.pyi index c616c1f5bf19..383a1b7f334b 100644 --- a/stdlib/contextlib.pyi +++ b/stdlib/contextlib.pyi @@ -47,6 +47,7 @@ _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) # allowlist for use as a Protocol. @runtime_checkable class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + __slots__ = () def __enter__(self) -> _T_co: ... @abstractmethod def __exit__( @@ -58,6 +59,7 @@ class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[m # allowlist for use as a Protocol. @runtime_checkable class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + __slots__ = () async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( diff --git a/stdlib/ctypes/_endian.pyi b/stdlib/ctypes/_endian.pyi index 144f5ba5dd40..97852f67aa6e 100644 --- a/stdlib/ctypes/_endian.pyi +++ b/stdlib/ctypes/_endian.pyi @@ -3,10 +3,14 @@ from ctypes import Structure, Union # At runtime, the native endianness is an alias for Structure, # while the other is a subclass with a metaclass added in. -class BigEndianStructure(Structure): ... +class BigEndianStructure(Structure): + __slots__ = () + class LittleEndianStructure(Structure): ... # Same thing for these: one is an alias of Union at runtime if sys.version_info >= (3, 11): - class BigEndianUnion(Union): ... + class BigEndianUnion(Union): + __slots__ = () + class LittleEndianUnion(Union): ... diff --git a/stdlib/dataclasses.pyi b/stdlib/dataclasses.pyi index fb9f5990b6f2..60c245904b20 100644 --- a/stdlib/dataclasses.pyi +++ b/stdlib/dataclasses.pyi @@ -170,6 +170,19 @@ class _DefaultFactory(Protocol[_T_co]): def __call__(self) -> _T_co: ... class Field(Generic[_T]): + __slots__ = ( + "name", + "type", + "default", + "default_factory", + "repr", + "hash", + "init", + "compare", + "metadata", + "kw_only", + "_field_type", + ) name: str type: Type[_T] | str | Any default: _T | Literal[_MISSING_TYPE.MISSING] @@ -355,6 +368,7 @@ def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstan class FrozenInstanceError(AttributeError): ... class InitVar(Generic[_T]): + __slots__ = ("type",) type: Type[_T] def __init__(self, type: Type[_T]) -> None: ... @overload diff --git a/stdlib/fractions.pyi b/stdlib/fractions.pyi index e81fbaf5dad7..ef4066aa65b5 100644 --- a/stdlib/fractions.pyi +++ b/stdlib/fractions.pyi @@ -14,6 +14,7 @@ class _ConvertibleToIntegerRatio(Protocol): def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ... class Fraction(Rational): + __slots__ = ("_numerator", "_denominator") @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload diff --git a/stdlib/hmac.pyi b/stdlib/hmac.pyi index 300ed9eb26d8..070c59b1c166 100644 --- a/stdlib/hmac.pyi +++ b/stdlib/hmac.pyi @@ -20,6 +20,7 @@ def new(key: bytes | bytearray, msg: ReadableBuffer | None, digestmod: _DigestMo def new(key: bytes | bytearray, *, digestmod: _DigestMod) -> HMAC: ... class HMAC: + __slots__ = ("_hmac", "_inner", "_outer", "block_size", "digest_size") digest_size: int block_size: int @property diff --git a/stdlib/importlib/metadata/__init__.pyi b/stdlib/importlib/metadata/__init__.pyi index d1315b2eb2f1..f9d3792565cb 100644 --- a/stdlib/importlib/metadata/__init__.pyi +++ b/stdlib/importlib/metadata/__init__.pyi @@ -97,6 +97,7 @@ class EntryPoint(_EntryPointBase): if sys.version_info >= (3, 12): class EntryPoints(tuple[EntryPoint, ...]): + __slots__ = () def __getitem__(self, name: str) -> EntryPoint: ... # type: ignore[override] def select( self, diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index f8ec6cad0160..1ff1c47caefa 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -336,6 +336,7 @@ class _void: ... class _empty: ... class Signature: + __slots__ = ("_return_annotation", "_parameters") def __init__( self, parameters: Sequence[Parameter] | None = None, *, return_annotation: Any = ..., __validate_parameters__: bool = True ) -> None: ... @@ -416,6 +417,7 @@ if sys.version_info >= (3, 12): def getasyncgenlocals(agen: AsyncGeneratorType[Any, Any]) -> dict[str, Any]: ... class Parameter: + __slots__ = ("_name", "_kind", "_default", "_annotation") def __init__(self, name: str, kind: _ParameterKind, *, default: Any = ..., annotation: Any = ...) -> None: ... empty = _empty @@ -447,6 +449,7 @@ class Parameter: def __hash__(self) -> int: ... class BoundArguments: + __slots__ = ("arguments", "_signature", "__weakref__") arguments: OrderedDict[str, Any] @property def args(self) -> tuple[Any, ...]: ... diff --git a/stdlib/ipaddress.pyi b/stdlib/ipaddress.pyi index 6d49eb8bd94a..d09804cb9342 100644 --- a/stdlib/ipaddress.pyi +++ b/stdlib/ipaddress.pyi @@ -22,6 +22,7 @@ def ip_interface( ) -> IPv4Interface | IPv6Interface: ... class _IPAddressBase: + __slots__ = () @property def compressed(self) -> str: ... @property @@ -33,6 +34,7 @@ class _IPAddressBase: def version(self) -> int: ... class _BaseAddress(_IPAddressBase): + __slots__ = () def __add__(self, other: int) -> Self: ... def __hash__(self) -> int: ... def __int__(self) -> int: ... @@ -105,6 +107,7 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): def hostmask(self) -> _A: ... class _BaseV4: + __slots__ = () if sys.version_info >= (3, 14): version: Final = 4 max_prefixlen: Final = 32 @@ -115,6 +118,7 @@ class _BaseV4: def max_prefixlen(self) -> Literal[32]: ... class IPv4Address(_BaseV4, _BaseAddress): + __slots__ = ("_ip", "__weakref__") def __init__(self, address: object) -> None: ... @property def is_global(self) -> bool: ... @@ -156,6 +160,7 @@ class IPv4Interface(IPv4Address): def with_prefixlen(self) -> str: ... class _BaseV6: + __slots__ = () if sys.version_info >= (3, 14): version: Final = 6 max_prefixlen: Final = 128 @@ -166,6 +171,7 @@ class _BaseV6: def max_prefixlen(self) -> Literal[128]: ... class IPv6Address(_BaseV6, _BaseAddress): + __slots__ = ("_ip", "_scope_id", "__weakref__") def __init__(self, address: object) -> None: ... @property def is_global(self) -> bool: ... diff --git a/stdlib/multiprocessing/managers.pyi b/stdlib/multiprocessing/managers.pyi index b0ccac41b925..5efe69a97377 100644 --- a/stdlib/multiprocessing/managers.pyi +++ b/stdlib/multiprocessing/managers.pyi @@ -38,6 +38,7 @@ class Namespace: _Namespace: TypeAlias = Namespace class Token: + __slots__ = ("typeid", "address", "id") typeid: str | bytes | None address: _Address | None id: str | bytes | int | None diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index b24591719cff..64fb16581e95 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -61,12 +61,14 @@ class _IntegralLike(_RealLike, Protocol): ################# class Number(metaclass=ABCMeta): + __slots__ = () @abstractmethod def __hash__(self) -> int: ... # See comment at the top of the file # for why some of these return types are purposefully vague class Complex(Number, _ComplexLike): + __slots__ = () @abstractmethod def __complex__(self) -> complex: ... def __bool__(self) -> bool: ... @@ -109,6 +111,7 @@ class Complex(Number, _ComplexLike): # See comment at the top of the file # for why some of these return types are purposefully vague class Real(Complex, _RealLike): + __slots__ = () @abstractmethod def __float__(self) -> float: ... @abstractmethod @@ -153,6 +156,7 @@ class Real(Complex, _RealLike): # See comment at the top of the file # for why some of these return types are purposefully vague class Rational(Real): + __slots__ = () @property @abstractmethod def numerator(self) -> _IntegralLike: ... @@ -164,6 +168,7 @@ class Rational(Real): # See comment at the top of the file # for why some of these return types are purposefully vague class Integral(Rational, _IntegralLike): + __slots__ = () @abstractmethod def __int__(self) -> int: ... def __index__(self) -> int: ... diff --git a/stdlib/os/__init__.pyi b/stdlib/os/__init__.pyi index 978b091cfcdc..7e54d8fb0a8c 100644 --- a/stdlib/os/__init__.pyi +++ b/stdlib/os/__init__.pyi @@ -862,6 +862,7 @@ In the future, this property will contain the last metadata change time.""" # on the allowlist for use as a Protocol starting in 3.14. @runtime_checkable class PathLike(ABC, Protocol[AnyStr_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + __slots__ = () @abstractmethod def __fspath__(self) -> AnyStr_co: ... diff --git a/stdlib/pathlib/__init__.pyi b/stdlib/pathlib/__init__.pyi index 4858f8db1ed0..da661bdcf5aa 100644 --- a/stdlib/pathlib/__init__.pyi +++ b/stdlib/pathlib/__init__.pyi @@ -29,6 +29,7 @@ if sys.version_info >= (3, 13): __all__ += ["UnsupportedOperation"] class PurePath(PathLike[str]): + __slots__ = ("_raw_paths", "_drv", "_root", "_tail_cached", "_str", "_str_normcase_cached", "_parts_normcase_cached", "_hash") if sys.version_info >= (3, 13): parser: ClassVar[types.ModuleType] def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ... @@ -108,10 +109,14 @@ class PurePath(PathLike[str]): if sys.version_info >= (3, 12): def with_segments(self, *args: StrPath) -> Self: ... -class PurePosixPath(PurePath): ... -class PureWindowsPath(PurePath): ... +class PurePosixPath(PurePath): + __slots__ = () + +class PureWindowsPath(PurePath): + __slots__ = () class Path(PurePath): + __slots__ = () if sys.version_info >= (3, 12): def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor] else: @@ -310,8 +315,11 @@ class Path(PurePath): self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ... ) -> Iterator[tuple[Self, list[str], list[str]]]: ... -class PosixPath(Path, PurePosixPath): ... -class WindowsPath(Path, PureWindowsPath): ... +class PosixPath(Path, PurePosixPath): + __slots__ = () + +class WindowsPath(Path, PureWindowsPath): + __slots__ = () if sys.version_info >= (3, 13): class UnsupportedOperation(NotImplementedError): ... diff --git a/stdlib/pickletools.pyi b/stdlib/pickletools.pyi index 23e9cbaa2c20..8bbfaba31b67 100644 --- a/stdlib/pickletools.pyi +++ b/stdlib/pickletools.pyi @@ -15,6 +15,7 @@ TAKEN_FROM_ARGUMENT4U: Final = -4 TAKEN_FROM_ARGUMENT8U: Final = -5 class ArgumentDescriptor: + __slots__ = ("name", "n", "reader", "doc") name: str n: int reader: _Reader @@ -118,6 +119,7 @@ def read_long4(f: IO[bytes]) -> int: ... long4: ArgumentDescriptor class StackObject: + __slots__ = ("name", "obtype", "doc") name: str obtype: type[Any] | tuple[type[Any], ...] doc: str @@ -143,6 +145,7 @@ markobject: StackObject stackslice: StackObject class OpcodeInfo: + __slots__ = ("name", "code", "arg", "stack_before", "stack_after", "proto", "doc") name: str code: str arg: ArgumentDescriptor | None diff --git a/stdlib/socket.pyi b/stdlib/socket.pyi index f1fdc9097e4a..b10b3560b91f 100644 --- a/stdlib/socket.pyi +++ b/stdlib/socket.pyi @@ -1300,6 +1300,7 @@ class _SendableFile(Protocol): # def fileno(self) -> int: ... class socket(_socket.socket): + __slots__ = ["__weakref__", "_io_refs", "_closed"] def __init__( self, family: AddressFamily | int = -1, type: SocketKind | int = -1, proto: int = -1, fileno: int | None = None ) -> None: ... diff --git a/stdlib/statistics.pyi b/stdlib/statistics.pyi index 6d7d3fbb4956..ba9e5f1b6b71 100644 --- a/stdlib/statistics.pyi +++ b/stdlib/statistics.pyi @@ -79,6 +79,7 @@ def stdev(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: . def variance(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: ... class NormalDist: + __slots__ = {"_mu": "Arithmetic mean of a normal distribution", "_sigma": "Standard deviation of a normal distribution"} def __init__(self, mu: float = 0.0, sigma: float = 1.0) -> None: ... @property def mean(self) -> float: ... diff --git a/stdlib/traceback.pyi b/stdlib/traceback.pyi index 4553dbd08384..e3c3b1fdc3c7 100644 --- a/stdlib/traceback.pyi +++ b/stdlib/traceback.pyi @@ -245,6 +245,7 @@ class TracebackException: def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ... class FrameSummary: + __slots__ = ("filename", "lineno", "end_lineno", "colno", "end_colno", "name", "_lines", "_lines_dedented", "locals", "_code") if sys.version_info >= (3, 11): def __init__( self, diff --git a/stdlib/tracemalloc.pyi b/stdlib/tracemalloc.pyi index 05d98ae127d8..31d8f7445639 100644 --- a/stdlib/tracemalloc.pyi +++ b/stdlib/tracemalloc.pyi @@ -32,6 +32,7 @@ class Filter(BaseFilter): ) -> None: ... class Statistic: + __slots__ = ("traceback", "size", "count") count: int size: int traceback: Traceback @@ -40,6 +41,7 @@ class Statistic: def __hash__(self) -> int: ... class StatisticDiff: + __slots__ = ("traceback", "size", "size_diff", "count", "count_diff") count: int count_diff: int size: int @@ -52,6 +54,7 @@ class StatisticDiff: _FrameTuple: TypeAlias = tuple[str, int] class Frame: + __slots__ = ("_frame",) @property def filename(self) -> str: ... @property @@ -72,6 +75,7 @@ class Frame: _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]] class Trace: + __slots__ = ("_trace",) @property def domain(self) -> int: ... @property @@ -83,6 +87,7 @@ class Trace: def __hash__(self) -> int: ... class Traceback(Sequence[Frame]): + __slots__ = ("_frames", "_total_nframe") @property def total_nframe(self) -> int | None: ... def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ... diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 7d048a13c288..2e2eb68a83bc 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -146,7 +146,9 @@ if sys.version_info >= (3, 13): # from _typeshed import AnnotationForm class Any: ... -class _Final: ... + +class _Final: + __slots__ = ("__weakref__",) def final(f: _T) -> _T: ... @final @@ -229,6 +231,7 @@ _promote = object() # N.B. Keep this definition in sync with typing_extensions._SpecialForm @final class _SpecialForm(_Final): + __slots__ = ("_name", "__doc__", "_getitem") def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -449,36 +452,43 @@ class _ProtocolMeta(ABCMeta): def runtime_checkable(cls: _TC) -> _TC: ... @runtime_checkable class SupportsInt(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __int__(self) -> int: ... @runtime_checkable class SupportsFloat(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __float__(self) -> float: ... @runtime_checkable class SupportsComplex(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __complex__(self) -> complex: ... @runtime_checkable class SupportsBytes(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __bytes__(self) -> bytes: ... @runtime_checkable class SupportsIndex(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __index__(self) -> int: ... @runtime_checkable class SupportsAbs(Protocol[_T_co]): + __slots__ = () @abstractmethod def __abs__(self) -> _T_co: ... @runtime_checkable class SupportsRound(Protocol[_T_co]): + __slots__ = () @overload @abstractmethod def __round__(self) -> int: ... @@ -820,6 +830,7 @@ class IO(Generic[AnyStr]): # At runtime these are all abstract properties, # but making them abstract in the stub is hugely disruptive, for not much gain. # See #8726 + __slots__ = () @property def mode(self) -> str: ... # Usually str, but may be bytes if a bytes path was passed to open(). See #10737. @@ -878,11 +889,13 @@ class IO(Generic[AnyStr]): ) -> None: ... class BinaryIO(IO[bytes]): + __slots__ = () @abstractmethod def __enter__(self) -> BinaryIO: ... class TextIO(IO[str]): # See comment regarding the @properties in the `IO` class + __slots__ = () @property def buffer(self) -> BinaryIO: ... @property @@ -1045,6 +1058,15 @@ if sys.version_info >= (3, 14): else: @final class ForwardRef(_Final): + __slots__ = ( + "__forward_arg__", + "__forward_code__", + "__forward_evaluated__", + "__forward_value__", + "__forward_is_argument__", + "__forward_is_class__", + "__forward_module__", + ) __forward_arg__: str __forward_code__: CodeType __forward_evaluated__: bool diff --git a/stdlib/typing_extensions.pyi b/stdlib/typing_extensions.pyi index 4b217e748be6..4cc353dd5c1d 100644 --- a/stdlib/typing_extensions.pyi +++ b/stdlib/typing_extensions.pyi @@ -450,11 +450,13 @@ if sys.version_info >= (3, 14): else: @runtime_checkable class Reader(Protocol[_T_co]): + __slots__ = () @abc.abstractmethod def read(self, size: int = ..., /) -> _T_co: ... @runtime_checkable class Writer(Protocol[_T_contra]): + __slots__ = () @abc.abstractmethod def write(self, data: _T_contra, /) -> int: ... diff --git a/stdlib/urllib/parse.pyi b/stdlib/urllib/parse.pyi index a33a32ec262c..364892ecdf69 100644 --- a/stdlib/urllib/parse.pyi +++ b/stdlib/urllib/parse.pyi @@ -39,12 +39,15 @@ if sys.version_info < (3, 11): MAX_CACHE_SIZE: Final[int] class _ResultMixinStr: + __slots__ = () def encode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinBytes: ... class _ResultMixinBytes: + __slots__ = () def decode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinStr: ... class _NetlocResultMixinBase(Generic[AnyStr]): + __slots__ = () @property def username(self) -> AnyStr | None: ... @property @@ -55,8 +58,11 @@ class _NetlocResultMixinBase(Generic[AnyStr]): def port(self) -> int | None: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): ... -class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): ... +class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): + __slots__ = () + +class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): + __slots__ = () class _DefragResultBase(NamedTuple, Generic[AnyStr]): url: AnyStr diff --git a/stdlib/uuid.pyi b/stdlib/uuid.pyi index 0aa2f76d40cc..303fb10eaf53 100644 --- a/stdlib/uuid.pyi +++ b/stdlib/uuid.pyi @@ -12,6 +12,7 @@ class SafeUUID(Enum): unknown = None class UUID: + __slots__ = ("int", "is_safe", "__weakref__") def __init__( self, hex: str | None = None, diff --git a/stdlib/weakref.pyi b/stdlib/weakref.pyi index 334fab7e7468..90206492cab9 100644 --- a/stdlib/weakref.pyi +++ b/stdlib/weakref.pyi @@ -65,6 +65,7 @@ ref = ReferenceType # everything below here is implemented in weakref.py class WeakMethod(ref[_CallableT]): + __slots__ = ("_func_ref", "_meth_type", "_alive", "__weakref__") def __new__(cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None) -> Self: ... def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... @@ -130,6 +131,7 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... class KeyedRef(ref[_T], Generic[_KT, _T]): + __slots__ = ("key",) key: _KT def __new__(type, ob: _T, callback: Callable[[Self], Any], key: _KT) -> Self: ... def __init__(self, ob: _T, callback: Callable[[Self], Any], key: _KT) -> None: ... @@ -185,6 +187,7 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... class finalize(Generic[_P, _T]): + __slots__ = () def __init__(self, obj: _T, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def __call__(self, _: Any = None) -> Any | None: ... def detach(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ... diff --git a/stdlib/xml/dom/__init__.pyi b/stdlib/xml/dom/__init__.pyi index d9615f9aacfe..9b3f101dde9b 100644 --- a/stdlib/xml/dom/__init__.pyi +++ b/stdlib/xml/dom/__init__.pyi @@ -3,6 +3,7 @@ from typing import Any, Final, Literal from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation class Node: + __slots__ = () ELEMENT_NODE: Literal[1] ATTRIBUTE_NODE: Literal[2] TEXT_NODE: Literal[3] diff --git a/stdlib/xml/dom/expatbuilder.pyi b/stdlib/xml/dom/expatbuilder.pyi index 228ad07e15ad..37e275ecde3e 100644 --- a/stdlib/xml/dom/expatbuilder.pyi +++ b/stdlib/xml/dom/expatbuilder.pyi @@ -17,6 +17,7 @@ FILTER_INTERRUPT = DOMBuilderFilter.FILTER_INTERRUPT theDOMImplementation: DOMImplementation class ElementInfo: + __slots__ = ("_attr_info", "_model", "tagName") tagName: str def __init__(self, tagName: str, model: _Model | None = None) -> None: ... def getAttributeType(self, aname: str) -> TypeInfo: ... @@ -66,19 +67,23 @@ class ExpatBuilder: def xml_decl_handler(self, version: str, encoding: str | None, standalone: int) -> None: ... class FilterVisibilityController: + __slots__ = ("filter",) filter: DOMBuilderFilter def __init__(self, filter: DOMBuilderFilter) -> None: ... def startContainer(self, node: Node) -> int: ... def acceptNode(self, node: Node) -> int: ... class FilterCrutch: + __slots__ = ("_builder", "_level", "_old_start", "_old_end") def __init__(self, builder: ExpatBuilder) -> None: ... class Rejecter(FilterCrutch): + __slots__ = () def start_element_handler(self, *args: Any) -> None: ... def end_element_handler(self, *args: Any) -> None: ... class Skipper(FilterCrutch): + __slots__ = () def start_element_handler(self, *args: Any) -> None: ... def end_element_handler(self, *args: Any) -> None: ... diff --git a/stdlib/xml/dom/minicompat.pyi b/stdlib/xml/dom/minicompat.pyi index 162f60254a58..6fcaee019dc2 100644 --- a/stdlib/xml/dom/minicompat.pyi +++ b/stdlib/xml/dom/minicompat.pyi @@ -8,11 +8,13 @@ _T = TypeVar("_T") StringTypes: tuple[type[str]] class NodeList(list[_T]): + __slots__ = () @property def length(self) -> int: ... def item(self, index: int) -> _T | None: ... class EmptyNodeList(tuple[()]): + __slots__ = () @property def length(self) -> Literal[0]: ... def item(self, index: int) -> None: ... diff --git a/stdlib/xml/dom/minidom.pyi b/stdlib/xml/dom/minidom.pyi index b9da9f3558ff..e0431417aa3c 100644 --- a/stdlib/xml/dom/minidom.pyi +++ b/stdlib/xml/dom/minidom.pyi @@ -188,6 +188,7 @@ _AttrChildrenVar = TypeVar("_AttrChildrenVar", bound=_AttrChildren) _AttrChildrenPlusFragment = TypeVar("_AttrChildrenPlusFragment", bound=_AttrChildren | DocumentFragment) class Attr(Node): + __slots__ = ("_name", "_value", "namespaceURI", "_prefix", "childNodes", "_localName", "ownerDocument", "ownerElement") nodeType: ClassVar[Literal[2]] nodeName: str # same as Attr.name nodeValue: str # same as Attr.value @@ -231,6 +232,7 @@ class Attr(Node): # In the DOM, this interface isn't specific to Attr, but our implementation is # because that's the only place we use it. class NamedNodeMap: + __slots__ = ("_attrs", "_attrsNS", "_ownerElement") def __init__(self, attrs: dict[str, Attr], attrsNS: dict[_NSName, Attr], ownerElement: Element) -> None: ... @property def length(self) -> int: ... @@ -262,6 +264,7 @@ class NamedNodeMap: AttributeList = NamedNodeMap class TypeInfo: + __slots__ = ("namespace", "name") namespace: str | None name: str | None def __init__(self, namespace: Incomplete | None, name: str | None) -> None: ... @@ -270,6 +273,20 @@ _ElementChildrenVar = TypeVar("_ElementChildrenVar", bound=_ElementChildren) _ElementChildrenPlusFragment = TypeVar("_ElementChildrenPlusFragment", bound=_ElementChildren | DocumentFragment) class Element(Node): + __slots__ = ( + "ownerDocument", + "parentNode", + "tagName", + "nodeName", + "prefix", + "namespaceURI", + "_localName", + "childNodes", + "_attrs", + "_attrsNS", + "nextSibling", + "previousSibling", + ) nodeType: ClassVar[Literal[1]] nodeName: str # same as Element.tagName nodeValue: None @@ -331,6 +348,7 @@ class Element(Node): def removeChild(self, oldChild: _ElementChildrenVar) -> _ElementChildrenVar: ... # type: ignore[override] class Childless: + __slots__ = () attributes: None childNodes: EmptyNodeList @property @@ -347,6 +365,7 @@ class Childless: def replaceChild(self, newChild: _NodesThatAreChildren | DocumentFragment, oldChild: _NodesThatAreChildren) -> NoReturn: ... class ProcessingInstruction(Childless, Node): + __slots__ = ("target", "data") nodeType: ClassVar[Literal[7]] nodeName: str # same as ProcessingInstruction.target nodeValue: str # same as ProcessingInstruction.data @@ -373,6 +392,7 @@ class ProcessingInstruction(Childless, Node): def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CharacterData(Childless, Node): + __slots__ = ("_data", "ownerDocument", "parentNode", "previousSibling", "nextSibling") nodeValue: str attributes: None @@ -397,6 +417,7 @@ class CharacterData(Childless, Node): def replaceData(self, offset: int, count: int, arg: str) -> None: ... class Text(CharacterData): + __slots__ = () nodeType: ClassVar[Literal[3]] nodeName: Literal["#text"] nodeValue: str # same as CharacterData.data, the content of the text node @@ -448,6 +469,7 @@ class Comment(CharacterData): def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CDATASection(Text): + __slots__ = () nodeType: ClassVar[Literal[4]] # type: ignore[assignment] nodeName: Literal["#cdata-section"] # type: ignore[assignment] nodeValue: str # same as CharacterData.data, the content of the CDATA Section @@ -460,6 +482,7 @@ class CDATASection(Text): def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class ReadOnlySequentialNamedNodeMap(Generic[_N]): + __slots__ = ("_seq",) def __init__(self, seq: Sequence[_N] = ()) -> None: ... def __len__(self) -> int: ... def getNamedItem(self, name: str) -> _N | None: ... @@ -474,6 +497,7 @@ class ReadOnlySequentialNamedNodeMap(Generic[_N]): def length(self) -> int: ... class Identified: + __slots__ = ("publicId", "systemId") publicId: str | None systemId: str | None @@ -565,6 +589,7 @@ class DOMImplementation(DOMImplementationLS): def getInterface(self, feature: str) -> Self | None: ... class ElementInfo: + __slots__ = ("tagName",) tagName: str def __init__(self, name: str) -> None: ... def getAttributeType(self, aname: str) -> TypeInfo: ... @@ -577,6 +602,7 @@ class ElementInfo: _DocumentChildrenPlusFragment = TypeVar("_DocumentChildrenPlusFragment", bound=_DocumentChildren | DocumentFragment) class Document(Node, DocumentLS): + __slots__ = ("_elem_info", "doctype", "_id_search_stack", "childNodes", "_id_cache") nodeType: ClassVar[Literal[9]] nodeName: Literal["#document"] nodeValue: None diff --git a/stdlib/xml/dom/xmlbuilder.pyi b/stdlib/xml/dom/xmlbuilder.pyi index 6fb18bbc4eda..8c0f2a3c7c26 100644 --- a/stdlib/xml/dom/xmlbuilder.pyi +++ b/stdlib/xml/dom/xmlbuilder.pyi @@ -44,9 +44,11 @@ class DOMBuilder: def parseWithContext(self, input: DOMInputSource, cnode: Node, action: Literal[1, 2, 3, 4]) -> NoReturn: ... class DOMEntityResolver: + __slots__ = ("_opener",) def resolveEntity(self, publicId: str | None, systemId: str) -> DOMInputSource: ... class DOMInputSource: + __slots__ = ("byteStream", "characterStream", "stringData", "encoding", "publicId", "systemId", "baseURI") byteStream: SupportsRead[bytes] | None characterStream: SupportsRead[str] | None stringData: str | None diff --git a/stdlib/zipfile/__init__.pyi b/stdlib/zipfile/__init__.pyi index 73e3a92fd0e2..3927f7636c96 100644 --- a/stdlib/zipfile/__init__.pyi +++ b/stdlib/zipfile/__init__.pyi @@ -272,6 +272,29 @@ class PyZipFile(ZipFile): def writepy(self, pathname: str, basename: str = "", filterfunc: Callable[[str], bool] | None = None) -> None: ... class ZipInfo: + __slots__ = ( + "orig_filename", + "filename", + "date_time", + "compress_type", + "compress_level", + "comment", + "extra", + "create_system", + "create_version", + "extract_version", + "reserved", + "flag_bits", + "volume", + "internal_attr", + "external_attr", + "header_offset", + "CRC", + "compress_size", + "file_size", + "_raw_time", + "_end_offset", + ) filename: str date_time: _DateTuple compress_type: int From 2beeb66fd887fa8ca6fe4c0c8a056b8f8c2cf2b4 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:07:33 -0700 Subject: [PATCH 2/9] back out a change (mypy bug?) --- stdlib/asyncio/protocols.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 179d611b73fc..72d803a7fc30 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -34,7 +34,6 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): - __slots__ = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... From d3e854160e0c47a8faa6c59990420e81e173f4c6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:16:23 -0700 Subject: [PATCH 3/9] workaround --- stdlib/asyncio/protocols.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 72d803a7fc30..37b0d39fc241 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -13,7 +13,8 @@ class BaseProtocol: def resume_writing(self) -> None: ... class Protocol(BaseProtocol): - __slots__ = () + # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class' + __slots__: tuple[()] = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... From 9173e2b9b52723e182dc63d9c47285cad1c4f196 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:18:18 -0700 Subject: [PATCH 4/9] this one too --- stdlib/asyncio/protocols.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 37b0d39fc241..2c52ad4be410 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -35,6 +35,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): + __slots__: tuple[()] = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... From fab7a7dd87ad944cc0447b6d22fad2f2fd90a9a8 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:26:38 -0700 Subject: [PATCH 5/9] some version changes --- stdlib/dataclasses.pyi | 44 ++++++++++++++++------- stdlib/pathlib/__init__.pyi | 33 +++++++++++++++-- stdlib/traceback.pyi | 18 +++++++++- stdlib/zipfile/__init__.pyi | 70 +++++++++++++++++++++++++------------ 4 files changed, 126 insertions(+), 39 deletions(-) diff --git a/stdlib/dataclasses.pyi b/stdlib/dataclasses.pyi index 60c245904b20..3a1c8cb5d62d 100644 --- a/stdlib/dataclasses.pyi +++ b/stdlib/dataclasses.pyi @@ -170,19 +170,37 @@ class _DefaultFactory(Protocol[_T_co]): def __call__(self) -> _T_co: ... class Field(Generic[_T]): - __slots__ = ( - "name", - "type", - "default", - "default_factory", - "repr", - "hash", - "init", - "compare", - "metadata", - "kw_only", - "_field_type", - ) + if sys.version_info >= (3, 14): + __slots__ = ( + "name", + "type", + "default", + "default_factory", + "repr", + "hash", + "init", + "compare", + "metadata", + "kw_only", + "doc", + "_field_type", + ) + elif sys.version_info >= (3, 10): + __slots__ = ( + "name", + "type", + "default", + "default_factory", + "repr", + "hash", + "init", + "compare", + "metadata", + "kw_only", + "_field_type", + ) + else: + __slots__ = ("name", "type", "default", "default_factory", "repr", "hash", "init", "compare", "metadata", "_field_type") name: str type: Type[_T] | str | Any default: _T | Literal[_MISSING_TYPE.MISSING] diff --git a/stdlib/pathlib/__init__.pyi b/stdlib/pathlib/__init__.pyi index da661bdcf5aa..8f3b9cacbeed 100644 --- a/stdlib/pathlib/__init__.pyi +++ b/stdlib/pathlib/__init__.pyi @@ -29,7 +29,31 @@ if sys.version_info >= (3, 13): __all__ += ["UnsupportedOperation"] class PurePath(PathLike[str]): - __slots__ = ("_raw_paths", "_drv", "_root", "_tail_cached", "_str", "_str_normcase_cached", "_parts_normcase_cached", "_hash") + if sys.version_info >= (3, 13): + __slots__ = ( + "_raw_paths", + "_drv", + "_root", + "_tail_cached", + "_str", + "_str_normcase_cached", + "_parts_normcase_cached", + "_hash", + ) + elif sys.version_info >= (3, 11): + __slots__ = ( + "_raw_paths", + "_drv", + "_root", + "_tail_cached", + "_str", + "_str_normcase_cached", + "_parts_normcase_cached", + "_lines_cached", + "_hash", + ) + else: + __slots__ = ("_drv", "_root", "_parts", "_str", "_hash", "_pparts", "_cached_cparts") if sys.version_info >= (3, 13): parser: ClassVar[types.ModuleType] def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ... @@ -116,7 +140,12 @@ class PureWindowsPath(PurePath): __slots__ = () class Path(PurePath): - __slots__ = () + if sys.version_info >= (3, 14): + __slots__ = ("_info",) + elif sys.version_info >= (3, 10): + __slots__ = () + else: + __slots__ = ("_accessor",) if sys.version_info >= (3, 12): def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor] else: diff --git a/stdlib/traceback.pyi b/stdlib/traceback.pyi index e3c3b1fdc3c7..03ccaf94aa55 100644 --- a/stdlib/traceback.pyi +++ b/stdlib/traceback.pyi @@ -245,7 +245,23 @@ class TracebackException: def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ... class FrameSummary: - __slots__ = ("filename", "lineno", "end_lineno", "colno", "end_colno", "name", "_lines", "_lines_dedented", "locals", "_code") + if sys.version_info >= (3, 13): + __slots__ = ( + "filename", + "lineno", + "end_lineno", + "colno", + "end_colno", + "name", + "_lines", + "_lines_dedented", + "locals", + "_code", + ) + elif sys.version_info >= (3, 11): + __slots__ = ("filename", "lineno", "end_lineno", "colno", "end_colno", "name", "_line", "locals") + else: + __slots__ = ("filename", "lineno", "name", "_line", "locals") if sys.version_info >= (3, 11): def __init__( self, diff --git a/stdlib/zipfile/__init__.pyi b/stdlib/zipfile/__init__.pyi index 3927f7636c96..eaaee1abb9d7 100644 --- a/stdlib/zipfile/__init__.pyi +++ b/stdlib/zipfile/__init__.pyi @@ -272,29 +272,53 @@ class PyZipFile(ZipFile): def writepy(self, pathname: str, basename: str = "", filterfunc: Callable[[str], bool] | None = None) -> None: ... class ZipInfo: - __slots__ = ( - "orig_filename", - "filename", - "date_time", - "compress_type", - "compress_level", - "comment", - "extra", - "create_system", - "create_version", - "extract_version", - "reserved", - "flag_bits", - "volume", - "internal_attr", - "external_attr", - "header_offset", - "CRC", - "compress_size", - "file_size", - "_raw_time", - "_end_offset", - ) + if sys.version_info >= (3, 11): + __slots__ = ( + "orig_filename", + "filename", + "date_time", + "compress_type", + "compress_level", + "comment", + "extra", + "create_system", + "create_version", + "extract_version", + "reserved", + "flag_bits", + "volume", + "internal_attr", + "external_attr", + "header_offset", + "CRC", + "compress_size", + "file_size", + "_raw_time", + "_end_offset", + ) + else: + __slots__ = ( + "orig_filename", + "filename", + "date_time", + "compress_type", + "_compresslevel", + "comment", + "extra", + "create_system", + "create_version", + "extract_version", + "reserved", + "flag_bits", + "volume", + "internal_attr", + "external_attr", + "header_offset", + "CRC", + "compress_size", + "file_size", + "_raw_time", + ) filename: str date_time: _DateTuple compress_type: int From 15cdab1053bada9e116bbdac0b6605e80c6569a2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 20 Aug 2025 20:34:00 -0700 Subject: [PATCH 6/9] . --- .../stubtest_allowlists/darwin-py310.txt | 1 + .../stubtest_allowlists/darwin-py39.txt | 1 + .../stubtest_allowlists/win32-py310.txt | 2 +- .../@tests/stubtest_allowlists/win32-py39.txt | 2 +- stdlib/pathlib/__init__.pyi | 2 +- stdlib/zipfile/__init__.pyi | 70 ++++++------------- 6 files changed, 28 insertions(+), 50 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/darwin-py310.txt b/stdlib/@tests/stubtest_allowlists/darwin-py310.txt index 4b6bd6e0eea7..569ec8b6d8dd 100644 --- a/stdlib/@tests/stubtest_allowlists/darwin-py310.txt +++ b/stdlib/@tests/stubtest_allowlists/darwin-py310.txt @@ -41,6 +41,7 @@ xml.etree.cElementTree.XMLPullParser.flush xml.parsers.expat.XMLParserType.GetReparseDeferralEnabled xml.parsers.expat.XMLParserType.SetReparseDeferralEnabled xml.sax.expatreader.ExpatParser.flush +zipfile.ZipInfo.__slots__ # ============================================================= diff --git a/stdlib/@tests/stubtest_allowlists/darwin-py39.txt b/stdlib/@tests/stubtest_allowlists/darwin-py39.txt index 8deeed14226e..beee69d9f0f7 100644 --- a/stdlib/@tests/stubtest_allowlists/darwin-py39.txt +++ b/stdlib/@tests/stubtest_allowlists/darwin-py39.txt @@ -50,6 +50,7 @@ xml.etree.cElementTree.XMLPullParser.flush xml.parsers.expat.XMLParserType.GetReparseDeferralEnabled xml.parsers.expat.XMLParserType.SetReparseDeferralEnabled xml.sax.expatreader.ExpatParser.flush +zipfile.ZipInfo.__slots__ # ============================================================= diff --git a/stdlib/@tests/stubtest_allowlists/win32-py310.txt b/stdlib/@tests/stubtest_allowlists/win32-py310.txt index 15005e65d04a..91ef658a05c9 100644 --- a/stdlib/@tests/stubtest_allowlists/win32-py310.txt +++ b/stdlib/@tests/stubtest_allowlists/win32-py310.txt @@ -42,7 +42,7 @@ xml.etree.cElementTree.XMLPullParser.flush xml.parsers.expat.XMLParserType.GetReparseDeferralEnabled xml.parsers.expat.XMLParserType.SetReparseDeferralEnabled xml.sax.expatreader.ExpatParser.flush - +zipfile.ZipInfo.__slots__ # ============================================================= # Allowlist entries that cannot or should not be fixed; <= 3.12 diff --git a/stdlib/@tests/stubtest_allowlists/win32-py39.txt b/stdlib/@tests/stubtest_allowlists/win32-py39.txt index 2d712bda1e01..e47f72cdd685 100644 --- a/stdlib/@tests/stubtest_allowlists/win32-py39.txt +++ b/stdlib/@tests/stubtest_allowlists/win32-py39.txt @@ -51,7 +51,7 @@ xml.etree.cElementTree.XMLPullParser.flush xml.parsers.expat.XMLParserType.GetReparseDeferralEnabled xml.parsers.expat.XMLParserType.SetReparseDeferralEnabled xml.sax.expatreader.ExpatParser.flush - +zipfile.ZipInfo.__slots__ # ============================================================= # Allowlist entries that cannot or should not be fixed; <= 3.12 diff --git a/stdlib/pathlib/__init__.pyi b/stdlib/pathlib/__init__.pyi index 8f3b9cacbeed..42518cc28932 100644 --- a/stdlib/pathlib/__init__.pyi +++ b/stdlib/pathlib/__init__.pyi @@ -40,7 +40,7 @@ class PurePath(PathLike[str]): "_parts_normcase_cached", "_hash", ) - elif sys.version_info >= (3, 11): + elif sys.version_info >= (3, 12): __slots__ = ( "_raw_paths", "_drv", diff --git a/stdlib/zipfile/__init__.pyi b/stdlib/zipfile/__init__.pyi index eaaee1abb9d7..3927f7636c96 100644 --- a/stdlib/zipfile/__init__.pyi +++ b/stdlib/zipfile/__init__.pyi @@ -272,53 +272,29 @@ class PyZipFile(ZipFile): def writepy(self, pathname: str, basename: str = "", filterfunc: Callable[[str], bool] | None = None) -> None: ... class ZipInfo: - if sys.version_info >= (3, 11): - __slots__ = ( - "orig_filename", - "filename", - "date_time", - "compress_type", - "compress_level", - "comment", - "extra", - "create_system", - "create_version", - "extract_version", - "reserved", - "flag_bits", - "volume", - "internal_attr", - "external_attr", - "header_offset", - "CRC", - "compress_size", - "file_size", - "_raw_time", - "_end_offset", - ) - else: - __slots__ = ( - "orig_filename", - "filename", - "date_time", - "compress_type", - "_compresslevel", - "comment", - "extra", - "create_system", - "create_version", - "extract_version", - "reserved", - "flag_bits", - "volume", - "internal_attr", - "external_attr", - "header_offset", - "CRC", - "compress_size", - "file_size", - "_raw_time", - ) + __slots__ = ( + "orig_filename", + "filename", + "date_time", + "compress_type", + "compress_level", + "comment", + "extra", + "create_system", + "create_version", + "extract_version", + "reserved", + "flag_bits", + "volume", + "internal_attr", + "external_attr", + "header_offset", + "CRC", + "compress_size", + "file_size", + "_raw_time", + "_end_offset", + ) filename: str date_time: _DateTuple compress_type: int From e84141036802573e77f6b230eb19f3b779ef2281 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 21 Aug 2025 06:50:27 -0700 Subject: [PATCH 7/9] Final --- stdlib/asyncio/protocols.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 2c52ad4be410..6c211001615e 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -1,6 +1,6 @@ from _typeshed import ReadableBuffer from asyncio import transports -from typing import Any +from typing import Any, Final # Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") @@ -14,7 +14,7 @@ class BaseProtocol: class Protocol(BaseProtocol): # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class' - __slots__: tuple[()] = () + __slots__: Final[tuple[()]] = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... @@ -35,7 +35,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): - __slots__: tuple[()] = () + __slots__: Final[tuple[()]] = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... From c285ca797604515429cedcd8db4345c56617480c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 21 Aug 2025 06:51:37 -0700 Subject: [PATCH 8/9] ClassVar instead --- stdlib/asyncio/protocols.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 6c211001615e..2f68390f4b8c 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -1,6 +1,6 @@ from _typeshed import ReadableBuffer from asyncio import transports -from typing import Any, Final +from typing import Any, ClassVar # Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") @@ -14,7 +14,7 @@ class BaseProtocol: class Protocol(BaseProtocol): # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class' - __slots__: Final[tuple[()]] = () + __slots__: ClassVar[tuple[()]] = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... @@ -35,7 +35,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): - __slots__: Final[tuple[()]] = () + __slots__: ClassVar[tuple[()]] = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... From 73d6f1f8e7a282c81ece6c2ffd63839d87813ba2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 21 Aug 2025 07:06:27 -0700 Subject: [PATCH 9/9] ClassVar is causing grief --- stdlib/asyncio/protocols.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/protocols.pyi b/stdlib/asyncio/protocols.pyi index 2f68390f4b8c..2c52ad4be410 100644 --- a/stdlib/asyncio/protocols.pyi +++ b/stdlib/asyncio/protocols.pyi @@ -1,6 +1,6 @@ from _typeshed import ReadableBuffer from asyncio import transports -from typing import Any, ClassVar +from typing import Any # Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") @@ -14,7 +14,7 @@ class BaseProtocol: class Protocol(BaseProtocol): # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class' - __slots__: ClassVar[tuple[()]] = () + __slots__: tuple[()] = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... @@ -35,7 +35,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): - __slots__: ClassVar[tuple[()]] = () + __slots__: tuple[()] = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ...