improved sentinel after learning from @taleinat on python-dev

This commit is contained in:
Luciano Ramalho 2021-05-24 13:27:07 -03:00
parent 08a4001b43
commit 0ce109a9fe
2 changed files with 38 additions and 9 deletions

View File

@ -1,23 +1,41 @@
""" """
This module provides a ``Sentinel`` class that can be used directly as a
sentinel singleton, or subclassed if a distinct sentinel singleton is needed.
The ``repr`` of a ``Sentinel`` class is its name::
>>> class Missing(Sentinel): pass >>> class Missing(Sentinel): pass
>>> Missing >>> Missing
Missing Missing
If a different ``repr`` is required,
you can define it as a class attribute::
>>> class CustomRepr(Sentinel): >>> class CustomRepr(Sentinel):
... repr = '<CustomRepr>' ... repr = '<CustomRepr>'
... ...
>>> CustomRepr >>> CustomRepr
<CustomRepr> <CustomRepr>
``Sentinel`` classes cannot be instantiated::
>>> Missing()
Traceback (most recent call last):
...
TypeError: 'Missing' is a sentinel and cannot be instantiated
""" """
class SentinelMeta(type):
class _SentinelMeta(type):
def __repr__(cls): def __repr__(cls):
try: try:
return cls.repr return cls.repr
except AttributeError: except AttributeError:
return cls.__name__ return f'{cls.__name__}'
class Sentinel(metaclass=SentinelMeta):
class Sentinel(metaclass=_SentinelMeta):
def __new__(cls): def __new__(cls):
return cls msg = 'is a sentinel and cannot be instantiated'
raise TypeError(f"'{cls!r}' {msg}")

View File

@ -1,8 +1,12 @@
import pickle import pickle
import pytest
from sentinel import Sentinel from sentinel import Sentinel
class PlainSentinel(Sentinel): pass
class PlainSentinel(Sentinel):
pass
class SentinelCustomRepr(Sentinel): class SentinelCustomRepr(Sentinel):
@ -13,16 +17,23 @@ def test_repr():
assert repr(PlainSentinel) == 'PlainSentinel' assert repr(PlainSentinel) == 'PlainSentinel'
def test_pickle(): def test_cannot_instantiate():
s = pickle.dumps(PlainSentinel) with pytest.raises(TypeError) as e:
ps = pickle.loads(s) PlainSentinel()
assert ps is PlainSentinel msg = "'PlainSentinel' is a sentinel and cannot be instantiated"
assert msg in str(e.value)
def test_custom_repr(): def test_custom_repr():
assert repr(SentinelCustomRepr) == '***SentinelRepr***' assert repr(SentinelCustomRepr) == '***SentinelRepr***'
def test_pickle():
s = pickle.dumps(SentinelCustomRepr)
ps = pickle.loads(s)
assert ps is SentinelCustomRepr
def test_sentinel_comes_ready_to_use(): def test_sentinel_comes_ready_to_use():
assert repr(Sentinel) == 'Sentinel' assert repr(Sentinel) == 'Sentinel'
s = pickle.dumps(Sentinel) s = pickle.dumps(Sentinel)