Skip to content

Commit 0ce109a

Browse files
committed
improved sentinel after learning from @taleinat on python-dev
1 parent 08a4001 commit 0ce109a

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,41 @@
11
"""
2+
This module provides a ``Sentinel`` class that can be used directly as a
3+
sentinel singleton, or subclassed if a distinct sentinel singleton is needed.
4+
5+
The ``repr`` of a ``Sentinel`` class is its name::
26
37
>>> class Missing(Sentinel): pass
48
>>> Missing
59
Missing
10+
11+
If a different ``repr`` is required,
12+
you can define it as a class attribute::
13+
614
>>> class CustomRepr(Sentinel):
715
... repr = '<CustomRepr>'
816
...
917
>>> CustomRepr
1018
<CustomRepr>
1119
20+
``Sentinel`` classes cannot be instantiated::
21+
22+
>>> Missing()
23+
Traceback (most recent call last):
24+
...
25+
TypeError: 'Missing' is a sentinel and cannot be instantiated
26+
1227
"""
1328

14-
class SentinelMeta(type):
29+
30+
class _SentinelMeta(type):
1531
def __repr__(cls):
1632
try:
1733
return cls.repr
1834
except AttributeError:
19-
return cls.__name__
35+
return f'{cls.__name__}'
36+
2037

21-
class Sentinel(metaclass=SentinelMeta):
38+
class Sentinel(metaclass=_SentinelMeta):
2239
def __new__(cls):
23-
return cls
40+
msg = 'is a sentinel and cannot be instantiated'
41+
raise TypeError(f"'{cls!r}' {msg}")

25-class-metaprog/sentinel/sentinel_test.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import pickle
22

3+
import pytest
4+
35
from sentinel import Sentinel
46

5-
class PlainSentinel(Sentinel): pass
7+
8+
class PlainSentinel(Sentinel):
9+
pass
610

711

812
class SentinelCustomRepr(Sentinel):
@@ -13,16 +17,23 @@ def test_repr():
1317
assert repr(PlainSentinel) == 'PlainSentinel'
1418

1519

16-
def test_pickle():
17-
s = pickle.dumps(PlainSentinel)
18-
ps = pickle.loads(s)
19-
assert ps is PlainSentinel
20+
def test_cannot_instantiate():
21+
with pytest.raises(TypeError) as e:
22+
PlainSentinel()
23+
msg = "'PlainSentinel' is a sentinel and cannot be instantiated"
24+
assert msg in str(e.value)
2025

2126

2227
def test_custom_repr():
2328
assert repr(SentinelCustomRepr) == '***SentinelRepr***'
2429

2530

31+
def test_pickle():
32+
s = pickle.dumps(SentinelCustomRepr)
33+
ps = pickle.loads(s)
34+
assert ps is SentinelCustomRepr
35+
36+
2637
def test_sentinel_comes_ready_to_use():
2738
assert repr(Sentinel) == 'Sentinel'
2839
s = pickle.dumps(Sentinel)

0 commit comments

Comments
 (0)