From 8b51be699c55d50c50a1d90542ba4b7b5c2143e6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 21 Jul 2025 18:49:05 -0700 Subject: [PATCH 1/7] Add @solid_base (PEP 800) --- CHANGELOG.md | 5 +++++ doc/index.rst | 11 +++++++++++ src/test_typing_extensions.py | 13 +++++++++++++ src/typing_extensions.py | 26 ++++++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8855595e..62918bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# Unreleased + +- Add the `@typing_extensions.solid_base` decorator, as specified + in PEP 800. Patch by Jelle Zijlstra. + # Release 4.14.1 (July 4, 2025) - Fix usage of `typing_extensions.TypedDict` nested inside other types diff --git a/doc/index.rst b/doc/index.rst index 21d6fa60..6569a39e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -748,6 +748,17 @@ Decorators improved, and ``typing_extensions`` backports these performance improvements. +.. decorator:: solid_base + + See :pep:`800`. A class decorator that marks a class as a "solid base", meaning that + child classes of the decorated class cannot inherit from other solid bases that are not + parent classes of the decorated class. + + This helps type checkers to detect unreachable code and to understand when two types + can overlap. + + .. versionadded:: 4.15.0 + Functions ~~~~~~~~~ diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index cb3b462b..c493c9a3 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -101,6 +101,7 @@ reveal_type, runtime, runtime_checkable, + solid_base, ) NoneType = type(None) @@ -6669,6 +6670,18 @@ def cached(self): ... self.assertIs(True, Methods.cached.__final__) +class SolidBaseTests(BaseTestCase): + def test_solid_base_unmodified(self): + class C: ... + self.assertIs(C, solid_base(C)) + + def test_dunder_solid_base(self): + @solid_base + class C: ... + + self.assertIs(C.__solid_base__, True) + + class RevealTypeTests(BaseTestCase): def test_reveal_type(self): obj = object() diff --git a/src/typing_extensions.py b/src/typing_extensions.py index efa09d55..7c2a96a4 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -315,6 +315,32 @@ class Other(Leaf): # Error reported by type checker return f +if hasattr(typing, "solid_base"): + solid_base = typing.solid_base +else: + def solid_base(cls): + """This decorator marks a class a solid base. + Child classes of a solid base cannot inherit from other solid bases that are + not parent classes of the solid base. + + For example: + + @solid_base + class Solid1: pass + + @solid_base + class Solid2: pass + + class Solid3(Solid1, Solid2): pass # Type checker error + + Type checkers can use solid bases to detect unreachable code + and determine when two types can overlap. + + See PEP 800.""" + cls.__solid_base__ = True + return cls + + def IntVar(name): return typing.TypeVar(name) From 7b00a6a7285b8e426c966d47776fc4484de60bf6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 21 Jul 2025 18:50:33 -0700 Subject: [PATCH 2/7] __all__ --- src/typing_extensions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 7c2a96a4..aae3552a 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -93,6 +93,7 @@ 'reveal_type', 'runtime', 'runtime_checkable', + 'solid_base', 'Text', 'TypeAlias', 'TypeAliasType', From f75df299e8ed711451244b1b4b575be968d70ea9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 22 Jul 2025 07:01:23 -0700 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Alex Waygood --- src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index aae3552a..d8ae8fb5 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -334,7 +334,7 @@ class Solid2: pass class Solid3(Solid1, Solid2): pass # Type checker error - Type checkers can use solid bases to detect unreachable code + Type checkers can use knowledge of solid bases to detect unreachable code and determine when two types can overlap. See PEP 800.""" From 7b6b3a3cffda2d6b522f67b9ac57f2cac907b034 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 22 Jul 2025 07:01:30 -0700 Subject: [PATCH 4/7] Update src/typing_extensions.py Co-authored-by: Alex Waygood --- src/typing_extensions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index d8ae8fb5..2d4a198a 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -321,6 +321,7 @@ class Other(Leaf): # Error reported by type checker else: def solid_base(cls): """This decorator marks a class a solid base. + Child classes of a solid base cannot inherit from other solid bases that are not parent classes of the solid base. From 808a0d2c9c06304f5999756d69facd05afd6f94f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 23 Jul 2025 12:56:39 -0700 Subject: [PATCH 5/7] Update src/typing_extensions.py Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> --- src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 2d4a198a..8e2c5332 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -316,7 +316,7 @@ class Other(Leaf): # Error reported by type checker return f -if hasattr(typing, "solid_base"): +if hasattr(typing, "solid_base"): # 3.15 solid_base = typing.solid_base else: def solid_base(cls): From 05acc477ed1bf390d854c865b85fb064b539576a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 17 Aug 2025 19:16:48 -0700 Subject: [PATCH 6/7] now disjoint base --- doc/index.rst | 22 +++++++++++----------- src/test_typing_extensions.py | 14 +++++++------- src/typing_extensions.py | 28 ++++++++++++++-------------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 3030cd8b..6aa95f5b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -705,6 +705,17 @@ Decorators Inheriting from a deprecated class now also raises a runtime :py:exc:`DeprecationWarning`. +.. decorator:: disjoint_base + + See :pep:`800`. A class decorator that marks a class as a "disjoint base", meaning that + child classes of the decorated class cannot inherit from other disjoint bases that are not + parent classes of the decorated class. + + This helps type checkers to detect unreachable code and to understand when two types + can overlap. + + .. versionadded:: 4.15.0 + .. decorator:: final See :py:func:`typing.final` and :pep:`591`. In ``typing`` since 3.8. @@ -748,17 +759,6 @@ Decorators improved, and ``typing_extensions`` backports these performance improvements. -.. decorator:: solid_base - - See :pep:`800`. A class decorator that marks a class as a "solid base", meaning that - child classes of the decorated class cannot inherit from other solid bases that are not - parent classes of the decorated class. - - This helps type checkers to detect unreachable code and to understand when two types - can overlap. - - .. versionadded:: 4.15.0 - Functions ~~~~~~~~~ diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 335552b3..e8b545e9 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -84,6 +84,7 @@ clear_overloads, dataclass_transform, deprecated, + disjoint_base, evaluate_forward_ref, final, get_annotations, @@ -101,7 +102,6 @@ reveal_type, runtime, runtime_checkable, - solid_base, type_repr, ) @@ -6671,16 +6671,16 @@ def cached(self): ... self.assertIs(True, Methods.cached.__final__) -class SolidBaseTests(BaseTestCase): - def test_solid_base_unmodified(self): +class DisjointBaseTests(BaseTestCase): + def test_disjoint_base_unmodified(self): class C: ... - self.assertIs(C, solid_base(C)) + self.assertIs(C, disjoint_base(C)) - def test_dunder_solid_base(self): - @solid_base + def test_dunder_disjoint_base(self): + @disjoint_base class C: ... - self.assertIs(C.__solid_base__, True) + self.assertIs(C.__disjoint_base__, True) class RevealTypeTests(BaseTestCase): diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 56f54317..a25128fc 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -71,6 +71,7 @@ 'clear_overloads', 'dataclass_transform', 'deprecated', + 'disjoint_base', 'Doc', 'evaluate_forward_ref', 'get_overloads', @@ -94,7 +95,6 @@ 'reveal_type', 'runtime', 'runtime_checkable', - 'solid_base', 'Text', 'TypeAlias', 'TypeAliasType', @@ -322,30 +322,30 @@ class Other(Leaf): # Error reported by type checker return f -if hasattr(typing, "solid_base"): # 3.15 - solid_base = typing.solid_base +if hasattr(typing, "disjoint_base"): # 3.15 + disjoint_base = typing.disjoint_base else: - def solid_base(cls): - """This decorator marks a class a solid base. + def disjoint_base(cls): + """This decorator marks a class as a disjoint base. - Child classes of a solid base cannot inherit from other solid bases that are - not parent classes of the solid base. + Child classes of a disjoint base cannot inherit from other disjoint bases that are + not parent classes of the disjoint base. For example: - @solid_base - class Solid1: pass + @disjoint_base + class Disjoint1: pass - @solid_base - class Solid2: pass + @disjoint_base + class Disjoint2: pass - class Solid3(Solid1, Solid2): pass # Type checker error + class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error - Type checkers can use knowledge of solid bases to detect unreachable code + Type checkers can use knowledge of disjoint bases to detect unreachable code and determine when two types can overlap. See PEP 800.""" - cls.__solid_base__ = True + cls.__disjoint_base__ = True return cls From 4c3ad586d62895495b1452c823c7c4bf5c579eb3 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 Aug 2025 06:40:54 -0700 Subject: [PATCH 7/7] Update CHANGELOG.md Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0877aef..0de587d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Unreleased -- Add the `@typing_extensions.solid_base` decorator, as specified +- Add the `@typing_extensions.disjoint_base` decorator, as specified in PEP 800. Patch by Jelle Zijlstra. - Add `typing_extensions.type_repr`, a backport of [`annotationlib.type_repr`](https://docs.python.org/3.14/library/annotationlib.html#annotationlib.type_repr),