Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
149b210
Add @disjoint_base decorator in the stdlib
JelleZijlstra Aug 20, 2025
612bb3b
3.14 stubtest
JelleZijlstra Aug 20, 2025
3f56c9b
stubtest fixes
JelleZijlstra Aug 20, 2025
b978f2f
.
JelleZijlstra Aug 20, 2025
76a237d
Merge branch 'main' into disjointbase
JelleZijlstra Aug 22, 2025
45eabe5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 22, 2025
1cedf1b
python -m stubdefaulter --only disjoint-base-with-slots --stdlib ../t…
JelleZijlstra Aug 22, 2025
a23c025
fixup
JelleZijlstra Aug 22, 2025
220281f
?
JelleZijlstra Aug 22, 2025
b76c640
add some missing slots
JelleZijlstra Aug 22, 2025
482fdd0
AttributeError and NameError were not disjoint bases in 3.9
JelleZijlstra Aug 22, 2025
950c2d7
fix EntryPoint
JelleZijlstra Aug 22, 2025
0073311
put one back
JelleZijlstra Aug 22, 2025
a536167
Merge remote-tracking branch 'upstream/main' into disjointbase
JelleZijlstra Aug 22, 2025
fcb6813
fixes
JelleZijlstra Aug 22, 2025
7d2b0f4
mine
JelleZijlstra Aug 22, 2025
dbfb2d2
Merge branch 'main' into disjointbase
JelleZijlstra Aug 22, 2025
42e87fb
Merge branch 'main' into disjointbase
JelleZijlstra Aug 23, 2025
9bb5a56
allowlist
JelleZijlstra Aug 22, 2025
7ae12e8
fix stragglers
JelleZijlstra Aug 24, 2025
aca4189
mypy pin
JelleZijlstra Aug 24, 2025
aa2cbd3
unused allowlists
JelleZijlstra Aug 24, 2025
622f17f
more stubtest fixes
JelleZijlstra Aug 24, 2025
e480d91
only pos-only in 3.13+
JelleZijlstra Aug 24, 2025
a17c86b
ignore the Random thing for now
JelleZijlstra Aug 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/stubtest_stdlib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@ jobs:
check-latest: true
- name: Install dependencies
run: pip install -r requirements-tests.txt
# Temporary to get @disjoint_base support; can remove once mypy 1.18 is released
- name: Install mypy from git
run: pip install git+https://github.com/python/mypy.git@116b92bae7b5dbf5e6bd36fd9b0c6804973e5554
- name: Run stubtest
run: python tests/stubtest_stdlib.py
20 changes: 2 additions & 18 deletions stdlib/@tests/stubtest_allowlists/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@

# Please keep sorted alphabetically

collections\.ChainMap\.fromkeys # https://github.com/python/mypy/issues/17023
http.client.HTTPConnection.response_class # the actual type at runtime is abc.ABCMeta
importlib.abc.Loader.exec_module # See Lib/importlib/_abc.py. Might be defined for backwards compatibility
importlib.abc.MetaPathFinder.find_spec # Not defined on the actual class, but expected to exist.
importlib.abc.PathEntryFinder.find_spec # Not defined on the actual class, but expected to exist.
tkinter.simpledialog.[A-Z_]+
tkinter.simpledialog.TclVersion
tkinter.simpledialog.TkVersion
tkinter.Text.count # stubtest somehow thinks that index1 parameter has a default value, but it doesn't in any of the overloads
builtins.tuple # should have @disjoint_base but hits pyright issue
tarfile.TarInfo.__slots__ # it's a big dictionary at runtime and the dictionary values are a bit long


# ===============================================================
Expand Down Expand Up @@ -201,7 +200,6 @@ _markupbase.ParserBase.parse_marked_section

_pydecimal.* # See comments in file
_typeshed.* # Utility types for typeshed, doesn't exist at runtime
argparse.ArgumentParser.__init__ # stubtest doesn't recognise the runtime default (a class) as being compatible with a callback protocol (the stub annotation)
argparse.Namespace.__getattr__ # The whole point of this class is its attributes are dynamic

# Runtime AST node runtime constructor behaviour is too loose.
Expand All @@ -218,8 +216,6 @@ argparse.Namespace.__setattr__ # should allow setting any attribute

ast.ImportFrom.level # None on the class, but never None on instances
ast.NodeVisitor.visit_\w+ # Methods are discovered dynamically, see #3796
_?asyncio.Future.__init__ # Usually initialized from c object
asyncio.futures.Future.__init__ # Usually initialized from c object

# Condition functions are exported in __init__
asyncio.Condition.acquire
Expand All @@ -230,7 +226,6 @@ asyncio.locks.Condition.locked
asyncio.locks.Condition.release

builtins.memoryview.__contains__ # C type that implements __getitem__
builtins.object.__init__ # default C signature is incorrect
builtins.reveal_locals # Builtins that type checkers pretends exist
builtins.reveal_type # Builtins that type checkers pretends exist
builtins.type.__dict__ # read-only but not actually a property; stubtest thinks it's a mutable attribute.
Expand Down Expand Up @@ -260,8 +255,6 @@ configparser.SectionProxy.getint # SectionProxy get functions are set in __init
contextlib.AbstractAsyncContextManager.__class_getitem__
contextlib.AbstractContextManager.__class_getitem__

_?contextvars.Context.__init__ # C signature is broader than what is actually accepted

copy.PyStringMap # defined only in Jython

# The Dialect properties are initialized as None in Dialect but their values are enforced in _Dialect
Expand Down Expand Up @@ -329,9 +322,6 @@ importlib.machinery.ExtensionFileLoader.get_filename
inspect.Parameter.__init__
inspect.Signature.__init__

inspect.Parameter.empty # set as private marker _empty
inspect.Signature.empty # set as private marker _empty

logging.LogRecord.__setattr__ # doesn't exist, but makes things easy if we pretend it does

# Iterable classes that don't define __iter__ at runtime (usually iterable via __getitem__)
Expand Down Expand Up @@ -377,11 +367,6 @@ multiprocessing.queues.JoinableQueue.__init__
multiprocessing.queues.Queue.__init__
multiprocessing.queues.SimpleQueue.__init__

# alias for a class defined elsewhere,
# mypy infers the variable has type `(*args) -> ForkingPickler`
# but stubtest infers the runtime type as <class ForkingPickler>
multiprocessing.reduction.AbstractReducer.ForkingPickler

# These methods are dynamically created after object initialization,
# copied from a wrapped lock object. Stubtest doesn't think they exist
# because of that.
Expand All @@ -406,7 +391,6 @@ os.PathLike.__class_getitem__ # PathLike is a protocol; we don't expect all Pat

pickle._Pickler\..* # Best effort typing for undocumented internals
pickle._Unpickler\..* # Best effort typing for undocumented internals
_?queue.SimpleQueue.__init__ # C signature is broader than what is actually accepted
shutil.rmtree # function with attributes, which we approximate with a callable protocol
socketserver.BaseServer.get_request # Not implemented, but expected to exist on subclasses.
ssl.PROTOCOL_SSLv2 # Depends on the existence and flags of SSL
Expand Down
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/darwin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ curses.LINES # Initialized only after initscr call
curses.has_key # stubtest gets confused because this is both a module and a function in curses
multiprocessing.popen_spawn_win32 # exists on Darwin but fails to import
readline.append_history_file # Only available if compiled with GNU readline, not editline
select.kqueue.__init__ # default C signature is wrong
select.poll # Actually a function; we have a class so it can be used as a type

# Some of these exist on non-windows, but they are useless and this is not intended
Expand Down
3 changes: 2 additions & 1 deletion stdlib/@tests/stubtest_allowlists/py310.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ asyncio.locks.Lock.__init__
asyncio.locks.Semaphore.__init__
asyncio.queues.Queue.__init__

_random.Random.__init__ # Issues with __new__/__init__ correspondence

bdb.Breakpoint.clearBreakpoints # Exists at runtime, but missing from stubs


Expand Down Expand Up @@ -100,7 +102,6 @@ typing_extensions.Sentinel.__call__
# <= 3.11
# =======

_?bz2.BZ2Decompressor.__init__ # function does not accept parameters but C signature is set
enum.Enum._generate_next_value_
importlib.abc.Finder.find_module
urllib.request.HTTPPasswordMgrWithPriorAuth.__init__ # Args are passed as is to super, so super args are specified
Expand Down
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/py311.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ importlib.metadata._meta.SimplePath.__truediv__ # Runtime definition of protoco
# <= 3.11
# =======

_?bz2.BZ2Decompressor.__init__ # function does not accept parameters but C signature is set
enum.Enum._generate_next_value_
importlib.abc.Finder.find_module
urllib.request.HTTPPasswordMgrWithPriorAuth.__init__ # Args are passed as is to super, so super args are specified
Expand Down
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/py39.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ typing_extensions.Sentinel.__call__
# <= 3.11
# =======

_?bz2.BZ2Decompressor.__init__ # function does not accept parameters but C signature is set
enum.Enum._generate_next_value_
importlib.abc.Finder.find_module
urllib.request.HTTPPasswordMgrWithPriorAuth.__init__ # Args are passed as is to super, so super args are specified
Expand Down
4 changes: 0 additions & 4 deletions stdlib/@tests/stubtest_allowlists/win32.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
# TODO: Allowlist entries that should be fixed
# ============================================

# alias for a class defined elsewhere,
# mypy infers the variable has type `(*args) -> DupHandle` but stubtest infers the runtime type as <class DupHandle>
multiprocessing.reduction.AbstractReducer.DupHandle

# Exists at runtime, but missing from stubs
_winapi.CreateFileMapping
_winapi.MapViewOfFile
Expand Down
4 changes: 3 additions & 1 deletion stdlib/_asyncio.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ from collections.abc import Awaitable, Callable, Coroutine, Generator
from contextvars import Context
from types import FrameType, GenericAlias
from typing import Any, Literal, TextIO, TypeVar
from typing_extensions import Self, TypeAlias
from typing_extensions import Self, TypeAlias, disjoint_base

_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
_TaskYieldType: TypeAlias = Future[object] | None

@disjoint_base
class Future(Awaitable[_T]):
_state: str
@property
Expand Down Expand Up @@ -49,6 +50,7 @@ else:
# While this is true in general, here it's sort-of okay to have a covariant subclass,
# since the only reason why `asyncio.Future` is invariant is the `set_result()` method,
# and `asyncio.Task.set_result()` always raises.
@disjoint_base
class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments]
if sys.version_info >= (3, 12):
def __init__(
Expand Down
5 changes: 4 additions & 1 deletion stdlib/_csv.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import sys
from _typeshed import SupportsWrite
from collections.abc import Iterable
from typing import Any, Final, Literal, type_check_only
from typing_extensions import Self, TypeAlias
from typing_extensions import Self, TypeAlias, disjoint_base

__version__: Final[str]

Expand All @@ -24,6 +24,7 @@ class Error(Exception): ...

_DialectLike: TypeAlias = str | Dialect | csv.Dialect | type[Dialect | csv.Dialect]

@disjoint_base
class Dialect:
delimiter: str
quotechar: str | None
Expand All @@ -48,6 +49,7 @@ class Dialect:

if sys.version_info >= (3, 10):
# This class calls itself _csv.reader.
@disjoint_base
class Reader:
@property
def dialect(self) -> Dialect: ...
Expand All @@ -56,6 +58,7 @@ if sys.version_info >= (3, 10):
def __next__(self) -> list[str]: ...

# This class calls itself _csv.writer.
@disjoint_base
class Writer:
@property
def dialect(self) -> Dialect: ...
Expand Down
3 changes: 2 additions & 1 deletion stdlib/_hashlib.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer
from collections.abc import Callable
from types import ModuleType
from typing import AnyStr, Protocol, final, overload, type_check_only
from typing_extensions import Self, TypeAlias
from typing_extensions import Self, TypeAlias, disjoint_base

_DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType | None

Expand All @@ -22,6 +22,7 @@ class _HashObject(Protocol):
def hexdigest(self) -> str: ...
def update(self, obj: ReadableBuffer, /) -> None: ...

@disjoint_base
class HASH:
@property
def digest_size(self) -> int: ...
Expand Down
3 changes: 2 additions & 1 deletion stdlib/_interpreters.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import types
from collections.abc import Callable
from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, disjoint_base

_R = TypeVar("_R")

Expand All @@ -12,6 +12,7 @@ class InterpreterError(Exception): ...
class InterpreterNotFoundError(InterpreterError): ...
class NotShareableError(ValueError): ...

@disjoint_base
class CrossInterpreterBufferView:
def __buffer__(self, flags: int, /) -> memoryview: ...

Expand Down
Loading
Loading