Skip to content

Commit

Permalink
Use enum for typing NOTHING (#983)
Browse files Browse the repository at this point in the history
* Use enum for typing NOTHING.

This will allow those extending attrs
to type NOTHING as Literal[NOTHING].

* Make test_dunders test `NOTHING` directly

* Reflect NOTHING enum typing in __init__.pyi

* Fix docs for NOTHING

* Add changelog entry

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

Co-authored-by: Hynek Schlawack <hs@ox.cx>
Co-authored-by: Tin Tvrtković <tinchester@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
4 people committed Aug 27, 2022
1 parent 20453b1 commit c860e9d
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 18 deletions.
1 change: 1 addition & 0 deletions 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``.
1 change: 1 addition & 0 deletions docs/api.rst
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
@@ -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
@@ -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
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

0 comments on commit c860e9d

Please sign in to comment.