Skip to content

Commit

Permalink
Warn if a method is missing
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek committed Mar 8, 2020
1 parent a4756b0 commit 590ef43
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
2 changes: 1 addition & 1 deletion changelog.d/607.change.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
``attrs`` can now automatically detect your own implementations and infer ``init=False``, ``repr=False``, ``eq=False``, ``order=False``, and ``hash=False`` if you set ``@attr.s(auto_detect=True)``.
``attrs`` will ignore inherited methods.
If the argument implies more than one methods (e.g. ``eq=True`` creates both ``__eq__`` and ``__ne__``), it's enough for *one* of them to exist and ``attrs`` will create *neither*.
If the argument implies more than one method (e.g. ``eq=True`` creates both ``__eq__`` and ``__ne__``), it's enough for *one* of them to exist and ``attrs`` will create *neither*.

This feature requires Python 3.
22 changes: 21 additions & 1 deletion src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,11 @@ def attrs(
So for example by implementing ``__eq__`` on a class yourself,
``attrs`` will deduce ``eq=False`` and won't create *neither*
``__eq__`` *nor* ``__ne__``.
``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible
``__ne__`` by default, so it *should* be enough to only implement
``__eq__`` in most cases). If you implement only a subset of the
ordering methods but miss some, ``attrs`` will raise a `UserWarning`.
Consider using `functools.total_ordering`.
Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*,
*cmp*, or *hash* overrides whatever *auto_detect* would determine.
Expand Down Expand Up @@ -1050,6 +1054,22 @@ def wrap(cls):
cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__")
):
builder.add_order()
elif not is_exc and order_ is None and auto_detect:
# This means it was auto-detected to not implement, warn the user
# if their class is not sound.
if not all(
[
_has_own_attribute(cls, meth_name)
for meth_name in ("__lt__", "__le__", "__gt__", "__ge__",)
]
):
warnings.warn(
"Your class implements only a subset of "
"__lt_, __le__, __gt__, and __ge__. "
"Its orderring behavior is not sound; consider "
"implementing them or using functools.total_ordering().",
stacklevel=2,
)

if (
hash_ is None
Expand Down
37 changes: 25 additions & 12 deletions tests/test_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,9 @@ def test_detect_auto_order(self, slots, frozen):
It's surprisingly difficult to test this programmatically, so we do it
by hand.
Also verify that we warn the user if they don't implement all of the
ordering methods.
"""

def assert_not_set(cls, ex, meth_name):
Expand All @@ -1828,21 +1831,31 @@ def assert_none_set(cls, ex):
for m in ("le", "lt", "ge", "gt"):
assert_not_set(cls, ex, "__" + m + "__")

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class LE(object):
__le__ = 42
msg = "Your class implements only a subset of __lt_, __le__, __gt__,"

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class LT(object):
__lt__ = 42
with pytest.warns(UserWarning, match=msg):

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class GE(object):
__ge__ = 42
@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class LE(object):
__le__ = 42

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class GT(object):
__gt__ = 42
with pytest.warns(UserWarning, match=msg):

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class LT(object):
__lt__ = 42

with pytest.warns(UserWarning, match=msg):

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class GE(object):
__ge__ = 42

with pytest.warns(UserWarning, match=msg):

@attr.s(auto_detect=True, slots=slots, frozen=frozen)
class GT(object):
__gt__ = 42

assert_none_set(LE, "__le__")
assert_none_set(LT, "__lt__")
Expand Down

0 comments on commit 590ef43

Please sign in to comment.