File tree Expand file tree Collapse file tree 5 files changed +71
-1
lines changed
Expand file tree Collapse file tree 5 files changed +71
-1
lines changed Original file line number Diff line number Diff line change @@ -3358,6 +3358,30 @@ 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.
33613385
33623386.. decorator :: type_check_only
33633387
Original file line number Diff line number Diff line change @@ -1565,6 +1565,8 @@ child class. It must be a metaclass that is a subclass of
15651565all other candidate metaclasses. If no such metaclass exists among the candidates,
15661566the class cannot be created, as explained in :ref: `metaclass-determination `.
15671567
1568+ .. _solid-bases :
1569+
15681570Finally, the instance layouts of the bases must be compatible. This means that it must be
15691571possible to compute a *solid base * for the class. Exactly which classes are solid bases
15701572depends on the Python implementation.
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* :ref: `The JIT compiler has been significantly upgraded <whatsnew315-jit >`
@@ -1240,6 +1241,13 @@ typing
12401241 as it was incorrectly inferred in runtime before.
12411242 (Contributed by Nikita Sobolev in :gh: `137191 `.)
12421243
1244+ * :pep: `800 `: Add :deco: `typing.disjoint_base `, a new decorator marking a class
1245+ as a disjoint base. This is an advanced feature primarily intended to allow
1246+ type checkers to faithfully reflect the runtime semantics of types defined
1247+ as builtins or in compiled extensions. If a class ``C `` is a disjoint base, then
1248+ child classes of that class cannot inherit from other disjoint bases that are
1249+ not parent or child classes of ``C ``. (Contributed by Jelle Zijlstra in :gh: `148639 `.)
1250+
12431251
12441252unicodedata
12451253-----------
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 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.
You can’t perform that action at this time.
0 commit comments