Skip to content

Commit f4b1a21

Browse files
Make MISSING usable in type annotations, and add EMPTY and SENTINEL variations.
1 parent f933f31 commit f4b1a21

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

spec_classes/__init__.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
from ._version import __version__, __version_tuple__
22
from .errors import FrozenInstanceError
33
from .spec_class import spec_class
4-
from .types import Alias, DeprecatedAlias, Attr, AttrProxy, MISSING, spec_property
4+
from .types import (
5+
spec_property,
6+
Alias,
7+
DeprecatedAlias,
8+
Attr,
9+
AttrProxy,
10+
MISSING,
11+
EMPTY,
12+
SENTINEL,
13+
)
514

615
__author__ = "Matthew Wardrop"
716
__author_email__ = "mpwardrop@gmail.com"
@@ -12,11 +21,13 @@
1221
"__author__",
1322
"__author_email__",
1423
"spec_class",
24+
"spec_property",
1525
"FrozenInstanceError",
16-
"MISSING",
1726
"Alias",
1827
"DeprecatedAlias",
1928
"Attr",
2029
"AttrProxy",
21-
"spec_property",
30+
"MISSING",
31+
"EMPTY",
32+
"SENTINEL",
2233
]

spec_classes/types/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .attr import Attr
33
from .attr_proxy import AttrProxy
44
from .keyed import KeyedList, KeyedSet
5-
from .missing import MISSING
5+
from .missing import MISSING, EMPTY, SENTINEL
66
from .spec_property import spec_property
77
from .validated import ValidatedType, bounded, validated
88

@@ -14,6 +14,8 @@
1414
"KeyedList",
1515
"KeyedSet",
1616
"MISSING",
17+
"EMPTY",
18+
"SENTINEL",
1719
"ValidatedType",
1820
"bounded",
1921
"spec_property",

spec_classes/types/missing.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
# Sentinel for unset inputs to spec_class methods
2-
class _MissingType:
3-
__instance__ = None
42

5-
def __new__(cls):
6-
if cls.__instance__ is None:
7-
cls.__instance__ = super(_MissingType, cls).__new__(cls)
8-
return cls.__instance__
93

10-
def __bool__(self):
4+
class _MissingType(type):
5+
"""
6+
This metaclass is used to create singleton falsey classes for use as missing
7+
and/or sentinel placeholder values.
8+
"""
9+
10+
def __repr__(cls):
11+
return cls.__name__
12+
13+
def __bool__(cls):
1114
return False
1215

13-
def __repr__(self):
14-
return "MISSING"
16+
def __call__(cls):
17+
return cls
18+
19+
20+
class MISSING(metaclass=_MissingType):
21+
"""
22+
Used to represent attributes that have not yet been assigned a value.
23+
"""
1524

16-
def __copy__(self):
17-
return self
1825

19-
def __deepcopy__(self, memo):
20-
return self
26+
class EMPTY(metaclass=_MissingType):
27+
"""
28+
Used to represent empty arguments in methods/etc, which is useful when it is
29+
important to distinguish between empty and "MISSING" values.
30+
"""
2131

2232

23-
MISSING = _MissingType()
33+
class SENTINEL(metaclass=_MissingType):
34+
"""
35+
A generic sentinel that can be used to check fallthrough conditions.
36+
"""

tests/types/test_missing.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import copy
22

3-
from spec_classes.types.missing import _MissingType, MISSING
3+
from spec_classes.types.missing import MISSING, EMPTY, SENTINEL
44

55

66
def test_missing():
7-
assert MISSING is _MissingType()
7+
assert MISSING != EMPTY
8+
assert MISSING != SENTINEL
89
assert bool(MISSING) is False
910
assert repr(MISSING) == "MISSING"
1011
assert copy.copy(MISSING) is MISSING
1112
assert copy.deepcopy(MISSING) is MISSING
13+
assert MISSING() is MISSING

0 commit comments

Comments
 (0)