Skip to content

Commit f44b2d3

Browse files
PEP 800: Mark as Accepted (#4915)
1 parent 7cab606 commit f44b2d3

File tree

1 file changed

+17
-10
lines changed

1 file changed

+17
-10
lines changed

peps/pep-0800.rst

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ PEP: 800
22
Title: Disjoint bases in the type system
33
Author: Jelle Zijlstra <jelle.zijlstra@gmail.com>
44
Discussions-To: https://discuss.python.org/t/99910/
5-
Status: Draft
5+
Status: Accepted
66
Type: Standards Track
77
Topic: Typing
88
Created: 21-Jul-2025
99
Python-Version: 3.15
1010
Post-History: `18-Jul-2025 <https://discuss.python.org/t/solid-bases-for-detecting-incompatible-base-classes/99280>`__,
1111
`23-Jul-2025 <https://discuss.python.org/t/99910>`__,
12+
Resolution: `15-Apr-2026 <https://discuss.python.org/t/pep-800-solid-bases-in-the-type-system/99910/41>`__
1213

1314

1415
Abstract
@@ -19,6 +20,10 @@ However, the information necessary to determine this is not currently part of th
1920
decorator, ``@typing.disjoint_base``, that indicates that a class is a "disjoint base". Two classes that have distinct, unrelated
2021
disjoint bases cannot have a common child class.
2122

23+
This decorator is not expected to be used directly by most users. It is primarily intended for use in stub files for
24+
standard library and extension-module classes, where it helps type checkers reflect runtime restrictions
25+
consistently.
26+
2227
Motivation
2328
==========
2429

@@ -63,11 +68,11 @@ incorrect in general, as discussed in more detail :ref:`below <pep-800-mypy-inco
6368
The experimental ``ty`` type checker uses a third approach that aligns more closely with the :ref:`runtime behavior of Python <pep-800-solid-bases-cpython>`:
6469
it recognizes certain classes as "solid bases" that restrict multiple inheritance. Broadly speaking, every class must
6570
inherit from at most one unique solid base, and if there is no unique solid base, the class cannot exist; we'll provide a more
66-
precise definition below. However, ty's approach relies on hardcoded knowledge of particular built-in types. The term "solid base" derives from the
71+
precise definition below. However, ty's current approach relies on hardcoded knowledge of particular built-in types. The term "solid base" derives from the
6772
CPython implementation; this PEP uses the newly proposed term "disjoint base" instead.
6873

6974
This PEP proposes an extension to the type system that makes it possible to express when multiple inheritance is not
70-
allowed at runtime: an ``@disjoint_base`` decorator that marks a classes as a *disjoint base*.
75+
allowed at runtime: an ``@disjoint_base`` decorator that marks a class as a *disjoint base*.
7176
This gives type checkers a more precise understanding of reachability, and helps in several concrete areas.
7277

7378
Invalid class definitions
@@ -256,7 +261,7 @@ Similarly, the concept of a "disjoint base" is not meaningful on ``TypedDict`` d
256261
Although they receive some special treatment in the type system, ``NamedTuple`` definitions create real nominal classes that can
257262
have child classes, so it makes sense to allow ``@disjoint_base`` on them and treat them like regular classes for the purposes
258263
of the disjoint base mechanism. All ``NamedTuple`` classes have ``tuple``, a disjoint base, in their MRO, so they
259-
cannot multiple inherit from other disjoint bases.
264+
cannot use multiple inheritance with other disjoint bases.
260265

261266
Specification
262267
=============
@@ -363,8 +368,9 @@ None known.
363368
How to Teach This
364369
=================
365370

366-
Most users will not have to directly use or understand the ``@disjoint_base`` decorator, as the expectation is that will be
367-
primarily used in library stubs for low-level libraries. Teachers of Python can introduce
371+
Most users will not have to directly use or understand the ``@disjoint_base`` decorator, as the expectation is that it will be
372+
primarily used in library stubs for low-level libraries. It should not be taught as a decorator that users should routinely
373+
add to classes. Teachers of Python can introduce
368374
the concept of "disjoint bases" to explain why multiple inheritance is not allowed in certain cases. Teachers of
369375
Python typing can introduce the decorator when teaching type narrowing constructs like ``isinstance()`` to
370376
explain to users why type checkers treat certain branches as unreachable.
@@ -377,7 +383,8 @@ The runtime implementation of the ``@disjoint_base`` decorator is available in
377383
Mypy and its stubtest tool support the decorator as of version 1.18.1; this was implemented in
378384
`python/mypy#19678 <https://github.com/python/mypy/pull/19678>`__.
379385
Support was added to the ty type checker in
380-
`astral-sh/ruff#20084 <https://github.com/astral-sh/ruff/pull/20084>`__.
386+
`astral-sh/ruff#20084 <https://github.com/astral-sh/ruff/pull/20084>`__
387+
and to pycroscope in `JelleZijlstra/pycroscope#431 <https://github.com/JelleZijlstra/pycroscope/pull/431>`__.
381388

382389
Appendix
383390
========
@@ -465,9 +472,9 @@ Nevertheless, it accepts the following class definition without error::
465472
def __rmul__(self, other: object) -> Never:
466473
raise TypeError
467474
def __ge__(self, other: int | str) -> bool:
468-
return int(self) > other if isinstance(other, int) else str(self) > other
469-
def __gt__(self, other: int | str) -> bool:
470475
return int(self) >= other if isinstance(other, int) else str(self) >= other
476+
def __gt__(self, other: int | str) -> bool:
477+
return int(self) > other if isinstance(other, int) else str(self) > other
471478
def __lt__(self, other: int | str) -> bool:
472479
return int(self) < other if isinstance(other, int) else str(self) < other
473480
def __le__(self, other: int | str) -> bool:
@@ -519,7 +526,7 @@ can also reject classes that have more practically useful implementations::
519526
pass
520527

521528
Mypy's rule works reasonably well in practice for deducing whether an intersection of two
522-
classes is inhabited. Most builtin classes that are disjoint bases happen to implement common dunder
529+
classes is inhabited. Most built-in classes that are disjoint bases happen to implement common dunder
523530
methods such as ``__add__`` and ``__iter__`` in incompatible ways, so mypy will consider them
524531
incompatible. There are some exceptions: mypy allows ``class C(BaseException, int): ...``,
525532
though both of these classes are disjoint bases and the class definition is rejected at runtime.

0 commit comments

Comments
 (0)