Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use enum for typing NOTHING #983

Merged
merged 9 commits into from
Aug 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/983.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``attrs.NOTHING`` is now an enum value, making it possible to use with f.e. ``typing.Literal``.
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion src/attr/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import enum
import sys

from typing import (
Expand Down Expand Up @@ -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)`
Expand Down
18 changes: 8 additions & 10 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: MIT

import copy
import enum
import linecache
import sys
import types
Expand Down Expand Up @@ -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"
Expand All @@ -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.
"""
Expand Down
13 changes: 6 additions & 7 deletions tests/test_dunders.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
_add_repr,
_is_slot_cls,
_make_init,
_Nothing,
fields,
make_class,
)
Expand Down Expand Up @@ -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):
"""
Expand Down