.. versionadded:: 3.12
+.. decorator:: disjoint_base
+
+ Decorator to mark a class as a disjoint base.
+
+ Type checkers do not allow child classes of a disjoint base ``C`` to
+ inherit from other disjoint bases that are not parent or child classes of ``C``.
+
+ For example::
+
+ @disjoint_base
+ class Disjoint1: pass
+
+ @disjoint_base
+ class Disjoint2: pass
+
+ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error
+
+ Type checkers can use knowledge of disjoint bases to detect unreachable code
+ and determine when two types can overlap.
+
+ The corresponding runtime concept is a solid base (see :ref:`multiple-inheritance`).
+ Classes that are solid bases at runtime can be marked with ``@disjoint_base`` in stub files.
+ Users may also mark other classes as disjoint bases to indicate to type checkers that
+ multiple inheritance with other disjoint bases should not be allowed.
+
+ Note that the concept of a solid base is a CPython implementation
+ detail, and the exact set of standard library classes that are
+ disjoint bases at runtime may change in future versions of Python.
+
+ .. versionadded:: next
.. decorator:: type_check_only
* :pep:`728`: ``TypedDict`` with typed extra items
* :pep:`747`: :ref:`Annotating type forms with TypeForm
<whatsnew315-typeform>`
+* :pep:`800`: Disjoint bases in the type system
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
<whatsnew315-pybyteswriter>`
* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
as it was incorrectly inferred in runtime before.
(Contributed by Nikita Sobolev in :gh:`137191`.)
+* :pep:`800`: Add :deco:`typing.disjoint_base`, a new decorator marking a class
+ as a disjoint base. This is an advanced feature primarily intended to allow
+ type checkers to faithfully reflect the runtime semantics of types defined
+ as builtins or in compiled extensions. If a class ``C`` is a disjoint base, then
+ child classes of that class cannot inherit from other disjoint bases that are
+ not parent or child classes of ``C``. (Contributed by Jelle Zijlstra in :gh:`148639`.)
+
unicodedata
-----------
from typing import assert_type, cast, runtime_checkable
from typing import get_type_hints
from typing import get_origin, get_args, get_protocol_members
-from typing import override
+from typing import override, disjoint_base
from typing import is_typeddict, is_protocol
from typing import reveal_type
from typing import dataclass_transform
self.assertNotIn('__magic__', dir_items)
+class DisjointBaseTests(BaseTestCase):
+ def test_disjoint_base_unmodified(self):
+ class C: ...
+ self.assertIs(C, disjoint_base(C))
+
+ def test_dunder_disjoint_base(self):
+ @disjoint_base
+ class C: ...
+
+ self.assertIs(C.__disjoint_base__, True)
+
+
class RevealTypeTests(BaseTestCase):
def test_reveal_type(self):
obj = object()
'cast',
'clear_overloads',
'dataclass_transform',
+ 'disjoint_base',
'evaluate_forward_ref',
'final',
'get_args',
return f
+def disjoint_base(cls):
+ """This decorator marks a class as a disjoint base.
+
+ Child classes of a disjoint base cannot inherit from other disjoint bases that are
+ not parent or child classes of the disjoint base.
+
+ For example:
+
+ @disjoint_base
+ class Disjoint1: pass
+
+ @disjoint_base
+ class Disjoint2: pass
+
+ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error
+
+ Type checkers can use knowledge of disjoint bases to detect unreachable code
+ and determine when two types can overlap.
+ """
+ cls.__disjoint_base__ = True
+ return cls
+
+
# Some unconstrained type variables. These were initially used by the container types.
# They were never meant for export and are now unused, but we keep them around to
# avoid breaking compatibility with users who import them.
--- /dev/null
+Implement :pep:`800`, adding the :deco:`typing.disjoint_base` decorator.
+Patch by Jelle Zijlstra.