File tree Expand file tree Collapse file tree 5 files changed +77
-1
lines changed
Expand file tree Collapse file tree 5 files changed +77
-1
lines changed Original file line number Diff line number Diff line change @@ -3358,6 +3358,36 @@ Functions and decorators
33583358
33593359 .. versionadded :: 3.12
33603360
3361+ .. decorator :: disjoint_base
3362+
3363+ Decorator to mark a class as a disjoint base.
3364+
3365+ Type checkers do not allow child classes of a disjoint base ``C `` to
3366+ inherit from other disjoint bases that are not parent or child classes of ``C ``.
3367+
3368+ For example::
3369+
3370+ @disjoint_base
3371+ class Disjoint1: pass
3372+
3373+ @disjoint_base
3374+ class Disjoint2: pass
3375+
3376+ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error
3377+
3378+ Type checkers can use knowledge of disjoint bases to detect unreachable code
3379+ and determine when two types can overlap.
3380+
3381+ The corresponding runtime concept is a solid base (see :ref: `multiple-inheritance `).
3382+ Classes that are solid bases at runtime can be marked with ``@disjoint_base `` in stub files.
3383+ Users may also mark other classes as disjoint bases to indicate to type checkers that
3384+ multiple inheritance with other disjoint bases should not be allowed.
3385+
3386+ Note that the concept of a solid base is a CPython implementation
3387+ detail, and the exact set of standard library classes that are
3388+ disjoint bases at runtime may change in future versions of Python.
3389+
3390+ .. versionadded :: next
33613391
33623392.. decorator :: type_check_only
33633393
Original file line number Diff line number Diff line change @@ -80,6 +80,7 @@ Summary -- Release highlights
8080* :pep: `728 `: ``TypedDict `` with typed extra items
8181* :pep: `747 `: :ref: `Annotating type forms with TypeForm
8282 <whatsnew315-typeform>`
83+ * :pep: `800 `: Disjoint bases in the type system
8384* :pep: `782 `: :ref: `A new PyBytesWriter C API to create a Python bytes object
8485 <whatsnew315-pybyteswriter>`
8586* :pep: `803 `: :ref: `Stable ABI for Free-Threaded Builds <whatsnew315-abi3t >`
@@ -1290,6 +1291,13 @@ typing
12901291 as it was incorrectly inferred in runtime before.
12911292 (Contributed by Nikita Sobolev in :gh: `137191 `.)
12921293
1294+ * :pep: `800 `: Add :deco: `typing.disjoint_base `, a new decorator marking a class
1295+ as a disjoint base. This is an advanced feature primarily intended to allow
1296+ type checkers to faithfully reflect the runtime semantics of types defined
1297+ as builtins or in compiled extensions. If a class ``C `` is a disjoint base, then
1298+ child classes of that class cannot inherit from other disjoint bases that are
1299+ not parent or child classes of ``C ``. (Contributed by Jelle Zijlstra in :gh: `148639 `.)
1300+
12931301
12941302unicodedata
12951303-----------
Original file line number Diff line number Diff line change 2929from typing import assert_type , cast , runtime_checkable
3030from typing import get_type_hints
3131from typing import get_origin , get_args , get_protocol_members
32- from typing import override
32+ from typing import override , disjoint_base
3333from typing import is_typeddict , is_protocol
3434from typing import reveal_type
3535from typing import dataclass_transform
@@ -10920,6 +10920,18 @@ def bar(self):
1092010920 self .assertNotIn ('__magic__' , dir_items )
1092110921
1092210922
10923+ class DisjointBaseTests (BaseTestCase ):
10924+ def test_disjoint_base_unmodified (self ):
10925+ class C : ...
10926+ self .assertIs (C , disjoint_base (C ))
10927+
10928+ def test_dunder_disjoint_base (self ):
10929+ @disjoint_base
10930+ class C : ...
10931+
10932+ self .assertIs (C .__disjoint_base__ , True )
10933+
10934+
1092310935class RevealTypeTests (BaseTestCase ):
1092410936 def test_reveal_type (self ):
1092510937 obj = object ()
Original file line number Diff line number Diff line change 126126 'cast' ,
127127 'clear_overloads' ,
128128 'dataclass_transform' ,
129+ 'disjoint_base' ,
129130 'evaluate_forward_ref' ,
130131 'final' ,
131132 'get_args' ,
@@ -2794,6 +2795,29 @@ class Other(Leaf): # Error reported by type checker
27942795 return f
27952796
27962797
2798+ def disjoint_base (cls ):
2799+ """This decorator marks a class as a disjoint base.
2800+
2801+ Child classes of a disjoint base cannot inherit from other disjoint bases that are
2802+ not parent or child classes of the disjoint base.
2803+
2804+ For example:
2805+
2806+ @disjoint_base
2807+ class Disjoint1: pass
2808+
2809+ @disjoint_base
2810+ class Disjoint2: pass
2811+
2812+ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error
2813+
2814+ Type checkers can use knowledge of disjoint bases to detect unreachable code
2815+ and determine when two types can overlap.
2816+ """
2817+ cls .__disjoint_base__ = True
2818+ return cls
2819+
2820+
27972821# Some unconstrained type variables. These were initially used by the container types.
27982822# They were never meant for export and are now unused, but we keep them around to
27992823# avoid breaking compatibility with users who import them.
Original file line number Diff line number Diff line change 1+ Implement :pep: `800 `, adding the :deco: `typing.disjoint_base ` decorator.
2+ Patch by Jelle Zijlstra.
You can’t perform that action at this time.
0 commit comments