Update sentinel (#757)
diff --git a/doc/index.rst b/doc/index.rst
index e8da24d..8e577db 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1075,12 +1075,12 @@
Sentinel objects
~~~~~~~~~~~~~~~~
-.. class:: sentinel(name, /)
+.. class:: sentinel(name, /, *, repr=None)
A type used to define sentinel values. The *name* argument should be the
name of the variable to which the return value shall be assigned.
- Assigning attributes to a sentinel is deprecated.
+ Assigning attributes to a sentinel is deprecated (except for __module__).
Example::
@@ -1106,7 +1106,7 @@
Now supports pickle and will be reduced as a singleton.
Renamed from `Sentinel` to `sentinel`, `Sentinel` is deprecated.
Automatic `repr` string no longer has angle brackets.
- `repr` parameter was deprecated.
+ `repr` as a positional argument is deprecated.
`name` as a keyword is deprecated.
Subclassing and attribute assignment are deprecated.
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
index 6dd0264..4149eeb 100644
--- a/src/test_typing_extensions.py
+++ b/src/test_typing_extensions.py
@@ -9619,10 +9619,18 @@
self.assertEqual(sentinel_no_repr.__name__, 'sentinel_no_repr')
self.assertEqual(repr(sentinel_no_repr), 'sentinel_no_repr')
+ @skipIf(TYPING_3_15_0, reason="'Passing 'repr' as a positional argument was removed in 3.15")
+ def test_sentinel_deprecated_argument_repr(self):
+ with self.assertWarnsRegex(DeprecationWarning, r"Passing 'repr' as a positional argument is deprecated; pass it by keyword instead."):
+ sentinel_argument_repr = sentinel('sentinel_argument_repr', 'argument_repr')
+
+ self.assertEqual(repr(sentinel_argument_repr), 'argument_repr')
+
@skipIf(TYPING_3_15_0_BETA_1, reason="'repr' parameter is not yet available in 3.15.0b1")
- def test_sentinel_explicit_repr(self):
- sentinel_explicit_repr = sentinel('sentinel_explicit_repr', repr='explicit_repr')
- self.assertEqual(repr(sentinel_explicit_repr), 'explicit_repr')
+ def test_sentinel_keyword_repr(self):
+ sentinel_keyword_repr = sentinel('sentinel_keyword_repr', repr='keyword_repr')
+
+ self.assertEqual(repr(sentinel_keyword_repr), 'keyword_repr')
@skipIf(sys.version_info < (3, 10), reason='New unions not available in 3.9')
def test_sentinel_type_expression_union(self):
@@ -9675,6 +9683,8 @@
my_sentinel = Sentinel(name="my_sentinel")
with self.assertWarnsRegex(DeprecationWarning, r"Setting attribute 'foo' on sentinel objects is deprecated"):
my_sentinel.foo = "bar"
+ with self.assertWarnsRegex(DeprecationWarning, r"Setting attribute '__name__' on sentinel objects is deprecated"):
+ my_sentinel.__name__ = "bar"
@skipUnless(TYPING_3_15_0, reason='Deprecated sentinel APIs are available before 3.15')
def test_sentinel_removed_deprecated_apis(self):
@@ -9687,6 +9697,8 @@
Sentinel(name="my_sentinel")
with self.assertRaises(AttributeError):
sentinel('my_sentinel').foo = "bar"
+ with self.assertRaises(AttributeError):
+ sentinel('my_sentinel').__name__ = "bar"
def load_tests(loader, tests, pattern):
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
index 31c37e5..64b2676 100644
--- a/src/typing_extensions.py
+++ b/src/typing_extensions.py
@@ -189,9 +189,10 @@
def __init__(
self,
__name: str = _sentinel_placeholder,
+ __repr: typing.Optional[str] = _sentinel_placeholder,
/,
- repr: typing.Optional[str] = None,
*,
+ repr: typing.Optional[str] = None,
name: str = _sentinel_placeholder,
) -> None:
if name is not _sentinel_placeholder:
@@ -204,8 +205,16 @@
__name = name
if __name is _sentinel_placeholder:
raise TypeError("First parameter 'name' is required")
+ if __repr is not _sentinel_placeholder:
+ warnings.warn(
+ "Passing 'repr' as a positional argument is deprecated; "
+ "pass it by keyword instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ repr = __repr
- self.__name__ = __name
+ self._name = __name
self._repr = repr if repr is not None else __name
# For pickling as a singleton:
@@ -221,7 +230,7 @@
super().__init_subclass__()
def __setattr__(self, attr: str, value: object) -> None:
- if attr not in {"__name__", "_repr", "__module__"}:
+ if attr not in {"_name", "_repr", "__module__"}:
warnings.warn(
f"Setting attribute {attr!r} on sentinel objects is deprecated "
"and will be disallowed in Python 3.15.",
@@ -230,7 +239,15 @@
)
super().__setattr__(attr, value)
- def __repr__(self):
+ @property
+ def __name__(self) -> str:
+ return self._name
+
+ @__name__.setter
+ def __name__(self, value: str) -> None:
+ self._name = value
+
+ def __repr__(self) -> str:
return self._repr
if sys.version_info < (3, 11):