Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- 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),
introduced in Python 3.14 (CPython PR [#124551](https://github.com/python/cpython/pull/124551),
Expand Down
11 changes: 11 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
clear_overloads,
dataclass_transform,
deprecated,
disjoint_base,
evaluate_forward_ref,
final,
get_annotations,
Expand Down Expand Up @@ -6670,6 +6671,18 @@ def cached(self): ...
self.assertIs(True, Methods.cached.__final__)


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
28 changes: 28 additions & 0 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
'clear_overloads',
'dataclass_transform',
'deprecated',
'disjoint_base',
'Doc',
'evaluate_forward_ref',
'get_overloads',
Expand Down Expand Up @@ -321,6 +322,33 @@ class Other(Leaf): # Error reported by type checker
return f


if hasattr(typing, "disjoint_base"): # 3.15
disjoint_base = typing.disjoint_base
else:
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.

See PEP 800."""
cls.__disjoint_base__ = True
return cls


def IntVar(name):
return typing.TypeVar(name)

Expand Down
Loading