Skip to content

Commit 2c509de

Browse files
committed
Reduce Sentinel as singletons
Use pickles method of handing singleton objects. This is simpler and more predictable than a custom unpickle function. Anonymous sentinels can no longer be pickled and will raise PicklingError instead of TypeError.
1 parent 5c0e8a7 commit 2c509de

File tree

2 files changed

+14
-24
lines changed

2 files changed

+14
-24
lines changed

src/test_typing_extensions.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9324,10 +9324,19 @@ def test_sentinel_import(self):
93249324
self.assertIs(Sentinel._import_sentinel("nonexistent", ""), None)
93259325
self.assertIs(Sentinel._import_sentinel("nonexistent", "nonexistent.nonexistent.nonexistent"), None)
93269326

9327-
def test_sentinel_picklable(self):
9327+
def test_sentinel_picklable_qualified(self):
93289328
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
93299329
self.assertIs(self.SENTINEL, pickle.loads(pickle.dumps(self.SENTINEL, protocol=proto)))
93309330

9331+
def test_sentinel_picklable_anonymous(self):
9332+
anonymous_sentinel = Sentinel("anonymous_sentinel") # Anonymous sentinel can not be pickled
9333+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
9334+
with self.assertRaisesRegex(
9335+
pickle.PicklingError,
9336+
r"attribute lookup anonymous_sentinel on test_typing_extensions failed|not found as test_typing_extensions.anonymous_sentinel"
9337+
):
9338+
self.assertIs(anonymous_sentinel, pickle.loads(pickle.dumps(anonymous_sentinel, protocol=proto)))
9339+
93319340

93329341

93339342
if __name__ == '__main__':

src/typing_extensions.py

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4158,16 +4158,6 @@ def evaluate_forward_ref(
41584158

41594159
_sentinel_registry = {}
41604160

4161-
def _unpickle_sentinel(
4162-
name: str,
4163-
module_name: str,
4164-
config: typing.Dict[str, typing.Any],
4165-
/,
4166-
):
4167-
"""Stable Sentinel unpickling function, get Sentinel at 'module_name.name'."""
4168-
# Explicit repr=name because a saved module_name is known to be valid
4169-
return Sentinel(name, module_name, repr=config.get("repr", name))
4170-
41714161
class Sentinel:
41724162
"""A sentinel object.
41734163
@@ -4229,7 +4219,7 @@ def __new__(
42294219
# Create initial or anonymous sentinel
42304220
sentinel = super().__new__(cls)
42314221
sentinel._name = name
4232-
sentinel._module_name = module_name
4222+
sentinel.__module__ = module_name # Assign which module defined this instance
42334223
sentinel._repr = repr if repr is not None else name
42344224
return _sentinel_registry.setdefault(registry_key, sentinel)
42354225

@@ -4262,18 +4252,9 @@ def __or__(self, other):
42624252
def __ror__(self, other):
42634253
return typing.Union[other, self]
42644254

4265-
def __reduce__(self):
4266-
"""Record where this sentinel is defined and its current parameters."""
4267-
config = {"repr": self._repr}
4268-
# Reduce callable must be at the top-level to be stable whenever Sentinel changes
4269-
return (
4270-
_unpickle_sentinel,
4271-
(
4272-
self._name,
4273-
self._module_name,
4274-
config,
4275-
),
4276-
)
4255+
def __reduce__(self) -> str:
4256+
"""Reduce this sentinel to a singleton."""
4257+
return self._name # Module is set from __module__ attribute
42774258

42784259

42794260
# Aliases for items that are in typing in all supported versions.

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