diff --git a/changelog.d/983.change.rst b/changelog.d/983.change.rst new file mode 100644 index 000000000..bb1a96849 --- /dev/null +++ b/changelog.d/983.change.rst @@ -0,0 +1 @@ +``attrs.NOTHING`` is now an enum value, making it possible to use with f.e. ``typing.Literal``. diff --git a/docs/api.rst b/docs/api.rst index a609833c5..f7298118b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -28,6 +28,7 @@ Core Therefore if a class, method, or function claims that it has been added in an older version, it is only available in the ``attr`` namespace. .. autodata:: attrs.NOTHING + :no-value: .. autofunction:: attrs.define diff --git a/src/attr/__init__.pyi b/src/attr/__init__.pyi index 42d48e8e6..c21fec7a1 100644 --- a/src/attr/__init__.pyi +++ b/src/attr/__init__.pyi @@ -1,3 +1,4 @@ +import enum import sys from typing import ( @@ -70,7 +71,10 @@ class AttrsInstance(Protocol): # _make -- -NOTHING: object +class _Nothing(enum.Enum): + NOTHING = enum.auto() + +NOTHING = _Nothing.NOTHING # NOTE: Factory lies about its return type to make this possible: # `x: List[int] # = Factory(list)` diff --git a/src/attr/_make.py b/src/attr/_make.py index bd666cc35..680738151 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: MIT import copy +import enum import linecache import sys import types @@ -43,21 +44,18 @@ _ng_default_on_setattr = setters.pipe(setters.convert, setters.validate) -class _Nothing: +class _Nothing(enum.Enum): """ - Sentinel class to indicate the lack of a value when ``None`` is ambiguous. + Sentinel to indicate the lack of a value when ``None`` is ambiguous. - ``_Nothing`` is a singleton. There is only ever one of it. + If extending attrs, you can use ``typing.Literal[NOTHING]`` to show + that a value may be ``NOTHING``. .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. """ - _singleton = None - - def __new__(cls): - if _Nothing._singleton is None: - _Nothing._singleton = super().__new__(cls) - return _Nothing._singleton + NOTHING = enum.auto() def __repr__(self): return "NOTHING" @@ -66,7 +64,7 @@ def __bool__(self): return False -NOTHING = _Nothing() +NOTHING = _Nothing.NOTHING """ Sentinel to indicate the lack of a value when ``None`` is ambiguous. """ diff --git a/tests/test_dunders.py b/tests/test_dunders.py index 8cb171650..209f05304 100644 --- a/tests/test_dunders.py +++ b/tests/test_dunders.py @@ -21,7 +21,6 @@ _add_repr, _is_slot_cls, _make_init, - _Nothing, fields, make_class, ) @@ -903,30 +902,30 @@ class C: class TestNothing: """ - Tests for `_Nothing`. + Tests for `NOTHING`. """ def test_copy(self): """ __copy__ returns the same object. """ - n = _Nothing() + n = NOTHING assert n is copy.copy(n) def test_deepcopy(self): """ __deepcopy__ returns the same object. """ - n = _Nothing() + n = NOTHING assert n is copy.deepcopy(n) def test_eq(self): """ All instances are equal. """ - assert _Nothing() == _Nothing() == NOTHING - assert not (_Nothing() != _Nothing()) - assert 1 != _Nothing() + assert NOTHING == NOTHING == NOTHING + assert not (NOTHING != NOTHING) + assert 1 != NOTHING def test_false(self): """