Skip to content

Commit 0a3b77f

Browse files
committed
feat: reverse parameters of SentinelValue() constructor
BREAKING CHANGE: SentinelValue() parameter order is different now: Previously it was: SentinelValue(instance_name, module_name) and now it is: SentinelValue(module_name, instance_name) because the latter is more natural, and also opens possibility for a little bit of extension in the future. This is the last major change in the API, I'm going to freeze it, so here I'm also triggering release of v.1.0.0 (by removing `major_on_zero = false` from `pyproject.toml`)
1 parent c5b098c commit 0a3b77f

File tree

5 files changed

+31
-32
lines changed

5 files changed

+31
-32
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Or, the same thing, but using the ``SentinelValue`` class
4040
class Missing(SentinelValue):
4141
pass
4242
43-
MISSING = Missing("MISSING", __name__)
43+
MISSING = Missing(__name__, "MISSING")
4444
4545
def get_something(default: Union[str, Missing] = MISSING):
4646
...

docs/index.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ A little bit more advanced way to create sentinel objects is to do this::
6868
>>> class Missing(SentinelValue):
6969
... pass
7070

71-
>>> MISSING = Missing("MISSING", __name__)
71+
>>> MISSING = Missing(__name__, "MISSING")
7272

7373
Such code is slightly more verbose (than using :func:`sentinel`), but, there are some benefits:
7474

@@ -118,7 +118,7 @@ and instead make your own sub-classes of :class:`SentinelValue`, like this::
118118
>>> class NotGiven(SentinelValue):
119119
... pass
120120

121-
>>> NOT_GIVEN = NotGiven("NOT_GIVEN", __name__)
121+
>>> NOT_GIVEN = NotGiven(__name__, "NOT_GIVEN")
122122

123123
>>> def foo(value: Union[int, NotGiven]) -> None:
124124
... return None
@@ -140,7 +140,7 @@ or, when subclassing :class:`SentinelValue`:
140140
>>> class NotSet(SentinelValue):
141141
... pass
142142
143-
>>> NOT_SET = NotSet("NOT_SET", __name__)
143+
>>> NOT_SET = NotSet(__name__, "NOT_SET")
144144
145145
Why? Because:
146146

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ version_toml = "pyproject.toml:tool.poetry.version"
6767
upload_to_pypi = true
6868
upload_to_release = true
6969
build_command = "poetry build"
70-
major_on_zero = false
7170

7271
[tool.pytest.ini_options]
7372
norecursedirs = "build dist .tox"

sentinel_value/sentinel_value.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class SentinelValue:
1010
Useful for distinguishing "value is not set" and "value is set to None" cases
1111
as shown in this example::
1212
13-
>>> NOT_SET = SentinelValue("NOT_SET", __name__)
13+
>>> NOT_SET = SentinelValue(__name__, "NOT_SET")
1414
1515
>>> value = getattr(object, "some_attribute", NOT_SET)
1616
>>> if value is NOT_SET:
@@ -25,7 +25,7 @@ class SentinelValue:
2525
>>> class Missing(SentinelValue):
2626
... pass
2727
28-
>>> MISSING = Missing("MISSING", __name__)
28+
>>> MISSING = Missing(__name__, "MISSING")
2929
3030
# Here is how the Missing class can be used for type hinting.
3131
>>> value: Union[str, None, Missing] = getattr(object, "some_attribute", MISSING)
@@ -34,12 +34,12 @@ class SentinelValue:
3434
value is missing
3535
"""
3636

37-
def __init__(self, instance_name: str, module_name: str) -> None:
37+
def __init__(self, module_name: str, instance_name: str) -> None:
3838
"""Initialize :class:`SentinelValue` object.
3939
40-
:param instance_name: name of Python variable that points to the sentinel value.
4140
:param module_name: name of Python module that hosts the sentinel value.
4241
In the majority of cases you should pass ``__name__`` here.
42+
:param instance_name: name of Python variable that points to the sentinel value.
4343
"""
4444
assert not ((module_name == self.__module__) and (instance_name == self.__class__.__name__))
4545

@@ -49,7 +49,7 @@ def __init__(self, instance_name: str, module_name: str) -> None:
4949

5050
super().__init__()
5151

52-
def __new__(cls, instance_name, module_name):
52+
def __new__(cls, module_name, instance_name):
5353
"""Create 1 instance of SentinelValue per name.
5454
5555
Usually, when you call a class, you expect to get a new instance on each call.
@@ -64,8 +64,8 @@ def __new__(cls, instance_name, module_name):
6464
That is, if you call :class:`SentinelValue` multiple times with the same arguments,
6565
you get the *exactly same* instance, check this out::
6666
67-
>>> MISSING1 = SentinelValue("MISSING", __name__)
68-
>>> MISSING2 = SentinelValue("MISSING", __name__)
67+
>>> MISSING1 = SentinelValue(__name__, "MISSING")
68+
>>> MISSING2 = SentinelValue(__name__, "MISSING")
6969
7070
>>> MISSING1 is MISSING2
7171
True
@@ -124,7 +124,7 @@ def __getnewargs__(self):
124124
# This is needed for pickle serialization.
125125
# In combination with magic in the overriden __new__() method above,
126126
# that allows to avoid constructing duplicates when un-pickling the object.
127-
return (self.instance_name, self.module_name)
127+
return (self.module_name, self.instance_name)
128128

129129
@staticmethod
130130
def _compose_qualified_name(instance_name: str, module_name: str) -> str:
@@ -153,7 +153,7 @@ def __bool__():
153153
So it is often handy to do ``if not value`` to check if there is no value
154154
(like if an attribute is set to ``None``, or not set at all)::
155155
156-
>>> NOT_SET = SentinelValue("NOT_SET", __name__)
156+
>>> NOT_SET = SentinelValue(__name__, "NOT_SET")
157157
158158
>>> value = getattr(object, "foobar", NOT_SET)
159159
@@ -173,9 +173,9 @@ def __bool__():
173173
This dictionary looks like this::
174174
175175
{
176-
"package1.module1.MISSING": SentinelValue("MISSING", module_name="package1.module1.MISSING"),
177-
"package2.module2.MISSING": SentinelValue("MISSING", module_name="package2.module2.MISSING"),
178-
"package2.module2.ABSENT": SentinelValue("ABSENT", module_name="package2.module2.ABSENT"),
176+
"package1.module1.MISSING": SentinelValue("package1.module1", "MISSING"),
177+
"package2.module2.MISSING": SentinelValue("package2.module2", "MISSING"),
178+
"package2.module2.ABSENT": SentinelValue("package2.module2", "ABSENT"),
179179
}
180180
181181
When a :class:`SentinelValue` object is instanciated, it registers itself in this dictionary
@@ -228,12 +228,12 @@ def sentinel(
228228
module_name = _get_caller_module_name()
229229
assert module_name
230230

231-
SentinelValueSubclass = _create_sentinel_value_subclass(instance_name, module_name)
231+
SentinelValueSubclass = _create_sentinel_value_subclass(module_name, instance_name)
232232

233233
if repr:
234234
SentinelValueSubclass.__repr__ = lambda self: repr # type: ignore
235235

236-
return SentinelValueSubclass(instance_name, module_name)
236+
return SentinelValueSubclass(module_name, instance_name)
237237

238238

239239
def _get_caller_module_name() -> Optional[str]:
@@ -253,7 +253,7 @@ def _get_caller_module_name() -> Optional[str]:
253253
return None
254254

255255

256-
def _create_sentinel_value_subclass(instance_name: str, module_name: str) -> Type[SentinelValue]:
256+
def _create_sentinel_value_subclass(module_name: str, instance_name: str) -> Type[SentinelValue]:
257257
module = sys.modules[module_name]
258258

259259
# Genarate class name from variable name.

tests/test_sentinel_value.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,44 @@ def cleanup_sentinel_value_instances_after_each_test():
1414

1515

1616
def test__SentinelValue__str_and_repr():
17-
MISSING = SentinelValue("MISSING", "some.module2")
17+
MISSING = SentinelValue("some.module2", "MISSING")
1818
assert str(MISSING) == "<MISSING>"
1919
assert repr(MISSING) == "<MISSING>"
2020

2121

2222
def test__SentinelValue__is_falsy():
23-
MISSING = SentinelValue("MISSING", "some.module3")
23+
MISSING = SentinelValue("some.module3", "MISSING")
2424
assert not bool(MISSING)
2525

2626

2727
def test__SentinelValue_creates_new_instance_only_once_per_name():
28-
MISSING = SentinelValue("MISSING", "some.module")
28+
MISSING = SentinelValue("some.module", "MISSING")
2929

30-
MISSING_duplicate = SentinelValue("MISSING", "some.module")
30+
MISSING_duplicate = SentinelValue("some.module", "MISSING")
3131
assert MISSING is MISSING_duplicate
3232

33-
MISSING2 = SentinelValue("MISSING2", "some.module")
33+
MISSING2 = SentinelValue("some.module", "MISSING2")
3434
assert MISSING is not MISSING2
3535

36-
MISSING_in_other_module = SentinelValue("MISSING", "some.module2")
36+
MISSING_in_other_module = SentinelValue("some.module2", "MISSING")
3737
assert MISSING is not MISSING_in_other_module
3838

3939

4040
def test__SentinelValue_can_change_class_on_the_fly():
41-
MISSING = SentinelValue("MISSING", __name__)
41+
MISSING = SentinelValue(__name__, "MISSING")
4242
assert MISSING.__class__ is SentinelValue
4343

4444
class Missing(SentinelValue):
4545
pass
4646

47-
MISSING_redefined = Missing("MISSING", __name__)
47+
MISSING_redefined = Missing(__name__, "MISSING")
4848

4949
assert MISSING_redefined is MISSING
5050
assert MISSING_redefined.__class__ is Missing
5151

5252

5353
def test__SentinelValue_is_pickleable():
54-
MISSING = SentinelValue("MISSING", __name__)
54+
MISSING = SentinelValue(__name__, "MISSING")
5555
MISSING_unpickled = pickle.loads(pickle.dumps(MISSING))
5656
assert MISSING_unpickled is MISSING
5757

@@ -61,7 +61,7 @@ class NoValue(SentinelValue):
6161

6262

6363
def test__SentinelValueSubclass_is_pickleable():
64-
NO_VALUE = NoValue("NO_VALUE", __name__)
64+
NO_VALUE = NoValue(__name__, "NO_VALUE")
6565
NO_VALUE_unpickled = pickle.loads(pickle.dumps(NO_VALUE))
6666
assert NO_VALUE_unpickled is NO_VALUE
6767

@@ -70,8 +70,8 @@ def test__SentinelValue__remains_singleton__when_copied():
7070
class Missing(SentinelValue):
7171
pass
7272

73-
DELETED = Missing("DELETED", __name__)
74-
NEVER_SET = Missing("NEVER_SET", __name__)
73+
DELETED = Missing(__name__, "DELETED")
74+
NEVER_SET = Missing(__name__, "NEVER_SET")
7575

7676
DELETED_copy = copy(DELETED)
7777
assert DELETED_copy is DELETED

0 commit comments

Comments
 (0)