Skip to content

Commit f962a6f

Browse files
committed
Add support for sentinels (PEP 661)
1 parent 2523523 commit f962a6f

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ New features:
99
- Add `typing_extensions.Reader` and `typing_extensions.Writer`. Patch by
1010
Sebastian Rittau.
1111
- Fix tests for Python 3.14. Patch by Jelle Zijlstra.
12+
- Add support for sentinels ([PEP 661](https://peps.python.org/pep-0661/)).
1213

1314
# Release 4.13.2 (April 10, 2025)
1415

src/test_typing_extensions.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
ReadOnly,
6767
Required,
6868
Self,
69+
Sentinel,
6970
Set,
7071
Tuple,
7172
Type,
@@ -9088,5 +9089,32 @@ def test_invalid_special_forms(self):
90889089
self.assertIs(evaluate_forward_ref(typing.ForwardRef("ClassVar", is_argument=False), globals=vars(typing)), ClassVar)
90899090

90909091

9092+
class TestSentinels(BaseTestCase):
9093+
def test_sentinel_no_repr(self):
9094+
sentinel_no_repr = Sentinel('sentinel_no_repr')
9095+
9096+
self.assertEqual(sentinel_no_repr._name, 'sentinel_no_repr')
9097+
self.assertEqual(repr(sentinel_no_repr), '<sentinel_no_repr>')
9098+
9099+
sentinel_no_repr_dots = Sentinel('Test.sentinel_no_repr')
9100+
9101+
self.assertEqual(sentinel_no_repr_dots._name, 'Test.sentinel_no_repr')
9102+
self.assertEqual(repr(sentinel_no_repr), '<sentinel_no_repr>')
9103+
9104+
def test_sentinel_explicit_repr(self):
9105+
sentinel_explicit_repr = Sentinel('sentinel_explicit_repr', repr='explicit_repr')
9106+
9107+
self.assertEqual(repr(sentinel_explicit_repr), 'explicit_repr')
9108+
9109+
def test_sentinel_type_expression_union(self):
9110+
sentinel = Sentinel('sentinel')
9111+
9112+
def func1(a: int | sentinel = sentinel): pass
9113+
def func2(a: sentinel | int = sentinel): pass
9114+
9115+
self.assertEqual(func1.__annotations__['a'], Union[int, sentinel])
9116+
self.assertEqual(func2.__annotations__['a'], Union[sentinel, int])
9117+
9118+
90919119
if __name__ == '__main__':
90929120
main()

src/typing_extensions.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# pyright: ignore
12
import abc
23
import builtins
34
import collections
@@ -89,6 +90,7 @@
8990
'overload',
9091
'override',
9192
'Protocol',
93+
'Sentinel',
9294
'reveal_type',
9395
'runtime',
9496
'runtime_checkable',
@@ -4222,6 +4224,44 @@ def evaluate_forward_ref(
42224224
)
42234225

42244226

4227+
class Sentinel:
4228+
"""Create a unique sentinel object.
4229+
4230+
*name* should be the fully-qualified name of the variable to which the
4231+
return value shall be assigned.
4232+
4233+
*repr*, if supplied, will be used for the repr of the sentinel object.
4234+
If not provided, "<name>" will be used (with any leading class names
4235+
removed).
4236+
"""
4237+
4238+
def __init__(
4239+
self,
4240+
name: str,
4241+
repr: str | None = None,
4242+
):
4243+
self._name = name
4244+
self._repr = repr if repr is not None else f'<{name.split(".")[-1]}>'
4245+
4246+
def __repr__(self):
4247+
return self._repr
4248+
4249+
def __reduce__(self):
4250+
return (
4251+
type(self),
4252+
(
4253+
self._name,
4254+
self._repr,
4255+
)
4256+
)
4257+
4258+
def __or__(self, other):
4259+
return Union[self, other]
4260+
4261+
def __ror__(self, other):
4262+
return Union[other, self]
4263+
4264+
42254265
# Aliases for items that are in typing in all supported versions.
42264266
# Explicitly assign these (rather than using `from typing import *` at the top),
42274267
# so that we get a CI error if one of these is deleted from typing.py

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy