Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3358,6 +3358,30 @@ Functions and decorators

.. 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.

.. decorator:: type_check_only

Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Summary -- Release highlights
* :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>`
* :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
Expand Down Expand Up @@ -1240,6 +1241,13 @@ typing
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
-----------
Expand Down
14 changes: 13 additions & 1 deletion Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
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
Expand Down Expand Up @@ -10920,6 +10920,18 @@ def bar(self):
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()
Expand Down
24 changes: 24 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
'cast',
'clear_overloads',
'dataclass_transform',
'disjoint_base',
'evaluate_forward_ref',
'final',
'get_args',
Expand Down Expand Up @@ -2794,6 +2795,29 @@ class Other(Leaf): # Error reported by type checker
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 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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Implement :pep:`800`, adding the :deco:`typing.disjoint_base` decorator.
Patch by Jelle Zijlstra.
Loading