2015-01-27 16:53:17 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2015-01-29 15:24:49 +00:00
|
|
|
"""
|
|
|
|
Tests for `attrib._dunders`.
|
|
|
|
"""
|
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
2015-01-29 15:24:49 +00:00
|
|
|
import copy
|
|
|
|
|
2015-01-28 14:54:41 +00:00
|
|
|
import pytest
|
|
|
|
|
2015-01-29 11:20:17 +00:00
|
|
|
from . import simple_attr
|
2015-01-27 16:53:17 +00:00
|
|
|
from attr._make import Attribute
|
|
|
|
from attr._dunders import (
|
|
|
|
NOTHING,
|
2015-01-29 15:24:49 +00:00
|
|
|
_Nothing,
|
2015-01-27 16:53:17 +00:00
|
|
|
_add_cmp,
|
|
|
|
_add_hash,
|
|
|
|
_add_init,
|
|
|
|
_add_repr,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def make_class():
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Return a new simple class.
|
|
|
|
"""
|
2015-01-27 16:53:17 +00:00
|
|
|
class C(object):
|
|
|
|
__attrs_attrs__ = [simple_attr("a"), simple_attr("b")]
|
|
|
|
|
|
|
|
def __init__(self, a, b):
|
|
|
|
self.a = a
|
|
|
|
self.b = b
|
|
|
|
return C
|
|
|
|
|
|
|
|
CmpC = _add_cmp(make_class())
|
|
|
|
ReprC = _add_repr(make_class())
|
|
|
|
HashC = _add_hash(make_class())
|
|
|
|
|
|
|
|
|
|
|
|
class InitC(object):
|
|
|
|
__attrs_attrs__ = [simple_attr("a"), simple_attr("b")]
|
|
|
|
|
|
|
|
InitC = _add_init(InitC)
|
|
|
|
|
|
|
|
|
|
|
|
class TestMakeClass(object):
|
|
|
|
"""
|
|
|
|
Tests for the testing helper function `make_class`.
|
|
|
|
"""
|
|
|
|
def test_returns_class(self):
|
|
|
|
"""
|
|
|
|
Returns a class object.
|
|
|
|
"""
|
|
|
|
assert type is make_class().__class__
|
|
|
|
|
|
|
|
def returns_distinct_classes(self):
|
|
|
|
"""
|
|
|
|
Each call returns a completely new class.
|
|
|
|
"""
|
|
|
|
assert make_class() is not make_class()
|
|
|
|
|
|
|
|
|
|
|
|
class TestAddCmp(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_cmp`.
|
|
|
|
"""
|
2015-01-27 16:53:17 +00:00
|
|
|
def test_equal(self):
|
|
|
|
"""
|
|
|
|
Equal objects are detected as equal.
|
|
|
|
"""
|
|
|
|
assert CmpC(1, 2) == CmpC(1, 2)
|
|
|
|
assert not (CmpC(1, 2) != CmpC(1, 2))
|
|
|
|
|
|
|
|
def test_unequal_same_class(self):
|
|
|
|
"""
|
|
|
|
Unequal objects of correct type are detected as unequal.
|
|
|
|
"""
|
|
|
|
assert CmpC(1, 2) != CmpC(2, 1)
|
|
|
|
assert not (CmpC(1, 2) == CmpC(2, 1))
|
|
|
|
|
|
|
|
def test_unequal_different_class(self):
|
|
|
|
"""
|
|
|
|
Unequal objects of differnt type are detected even if their attributes
|
|
|
|
match.
|
|
|
|
"""
|
|
|
|
class NotCmpC(object):
|
|
|
|
a = 1
|
|
|
|
b = 2
|
|
|
|
assert CmpC(1, 2) != NotCmpC()
|
|
|
|
assert not (CmpC(1, 2) == NotCmpC())
|
|
|
|
|
|
|
|
def test_lt(self):
|
|
|
|
"""
|
|
|
|
__lt__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((1, 2), (2, 1)),
|
|
|
|
((1, 2), (1, 3)),
|
|
|
|
(("a", "b"), ("b", "a")),
|
|
|
|
]:
|
|
|
|
assert CmpC(*a) < CmpC(*b)
|
|
|
|
|
|
|
|
def test_lt_unordable(self):
|
|
|
|
"""
|
|
|
|
__lt__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
|
|
|
assert NotImplemented == (CmpC(1, 2).__lt__(42))
|
|
|
|
|
|
|
|
def test_le(self):
|
|
|
|
"""
|
|
|
|
__le__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((1, 2), (2, 1)),
|
|
|
|
((1, 2), (1, 3)),
|
|
|
|
((1, 1), (1, 1)),
|
|
|
|
(("a", "b"), ("b", "a")),
|
|
|
|
(("a", "b"), ("a", "b")),
|
|
|
|
]:
|
|
|
|
assert CmpC(*a) <= CmpC(*b)
|
|
|
|
|
|
|
|
def test_le_unordable(self):
|
|
|
|
"""
|
|
|
|
__le__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
|
|
|
assert NotImplemented == (CmpC(1, 2).__le__(42))
|
|
|
|
|
|
|
|
def test_gt(self):
|
|
|
|
"""
|
|
|
|
__gt__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((2, 1), (1, 2)),
|
|
|
|
((1, 3), (1, 2)),
|
|
|
|
(("b", "a"), ("a", "b")),
|
|
|
|
]:
|
|
|
|
assert CmpC(*a) > CmpC(*b)
|
|
|
|
|
|
|
|
def test_gt_unordable(self):
|
|
|
|
"""
|
|
|
|
__gt__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
|
|
|
assert NotImplemented == (CmpC(1, 2).__gt__(42))
|
|
|
|
|
|
|
|
def test_ge(self):
|
|
|
|
"""
|
|
|
|
__ge__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((2, 1), (1, 2)),
|
|
|
|
((1, 3), (1, 2)),
|
|
|
|
((1, 1), (1, 1)),
|
|
|
|
(("b", "a"), ("a", "b")),
|
|
|
|
(("a", "b"), ("a", "b")),
|
|
|
|
]:
|
|
|
|
assert CmpC(*a) >= CmpC(*b)
|
|
|
|
|
|
|
|
def test_ge_unordable(self):
|
|
|
|
"""
|
|
|
|
__ge__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
|
|
|
assert NotImplemented == (CmpC(1, 2).__ge__(42))
|
|
|
|
|
|
|
|
|
|
|
|
class TestAddRepr(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_repr`.
|
|
|
|
"""
|
2015-01-27 16:53:17 +00:00
|
|
|
def test_repr(self):
|
|
|
|
"""
|
2015-01-29 12:05:04 +00:00
|
|
|
repr returns a sensible value.
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2015-01-27 22:08:55 +00:00
|
|
|
assert "C(a=1, b=2)" == repr(ReprC(1, 2))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2015-01-29 12:05:04 +00:00
|
|
|
def test_underscores(self):
|
|
|
|
"""
|
|
|
|
repr does not strip underscores.
|
|
|
|
"""
|
|
|
|
class C(object):
|
|
|
|
__attrs_attrs__ = [simple_attr("_x")]
|
|
|
|
|
|
|
|
C = _add_repr(C)
|
|
|
|
i = C()
|
|
|
|
i._x = 42
|
|
|
|
|
|
|
|
assert "C(_x=42)" == repr(i)
|
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
class TestAddHash(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_hash`.
|
|
|
|
"""
|
2015-01-27 16:53:17 +00:00
|
|
|
def test_hash(self):
|
|
|
|
"""
|
|
|
|
__hash__ returns different hashes for different values.
|
|
|
|
"""
|
|
|
|
assert hash(HashC(1, 2)) != hash(HashC(1, 1))
|
|
|
|
|
|
|
|
|
|
|
|
class TestAddInit(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_init`.
|
|
|
|
"""
|
2015-01-27 16:53:17 +00:00
|
|
|
def test_sets_attributes(self):
|
|
|
|
"""
|
|
|
|
The attributes are initialized using the passed keywords.
|
|
|
|
"""
|
|
|
|
obj = InitC(a=1, b=2)
|
|
|
|
assert 1 == obj.a
|
|
|
|
assert 2 == obj.b
|
|
|
|
|
|
|
|
def test_default_value(self):
|
|
|
|
"""
|
|
|
|
If a default value is present, it's used as fallback.
|
|
|
|
"""
|
|
|
|
class C(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
__attrs_attrs__ = [
|
2015-01-29 15:24:49 +00:00
|
|
|
Attribute(name="a",
|
2015-01-28 14:54:41 +00:00
|
|
|
default_value=2,
|
|
|
|
default_factory=NOTHING,
|
|
|
|
validator=None,),
|
2015-01-29 15:24:49 +00:00
|
|
|
Attribute(name="b",
|
2015-01-28 14:54:41 +00:00
|
|
|
default_value="hallo",
|
|
|
|
default_factory=NOTHING,
|
|
|
|
validator=None,),
|
2015-01-29 15:24:49 +00:00
|
|
|
Attribute(name="c",
|
2015-01-28 14:54:41 +00:00
|
|
|
default_value=None,
|
|
|
|
default_factory=NOTHING,
|
|
|
|
validator=None,),
|
|
|
|
]
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
C = _add_init(C)
|
|
|
|
i = C()
|
|
|
|
assert 2 == i.a
|
|
|
|
assert "hallo" == i.b
|
|
|
|
assert None is i.c
|
|
|
|
|
|
|
|
def test_default_factory(self):
|
|
|
|
"""
|
|
|
|
If a default factory is present, it's used as fallback.
|
|
|
|
"""
|
|
|
|
class D(object):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class C(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
__attrs_attrs__ = [
|
2015-01-29 15:24:49 +00:00
|
|
|
Attribute(name="a",
|
2015-01-28 14:54:41 +00:00
|
|
|
default_value=NOTHING,
|
|
|
|
default_factory=list,
|
|
|
|
validator=None,),
|
2015-01-29 15:24:49 +00:00
|
|
|
Attribute(name="b",
|
2015-01-28 14:54:41 +00:00
|
|
|
default_value=NOTHING,
|
|
|
|
default_factory=D,
|
|
|
|
validator=None,)
|
|
|
|
]
|
2015-01-27 16:53:17 +00:00
|
|
|
C = _add_init(C)
|
|
|
|
i = C()
|
|
|
|
assert [] == i.a
|
|
|
|
assert isinstance(i.b, D)
|
2015-01-28 14:54:41 +00:00
|
|
|
|
|
|
|
def test_validator(self):
|
|
|
|
"""
|
2015-01-29 08:22:06 +00:00
|
|
|
If a validator is passed, call it with the Attribute and the argument.
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
class VException(Exception):
|
|
|
|
pass
|
|
|
|
|
2015-01-29 08:22:06 +00:00
|
|
|
def raiser(*args):
|
|
|
|
raise VException(args)
|
|
|
|
|
2015-01-29 15:24:49 +00:00
|
|
|
a = Attribute(name="a",
|
2015-01-29 08:22:06 +00:00
|
|
|
default_value=NOTHING,
|
|
|
|
default_factory=NOTHING,
|
|
|
|
validator=raiser)
|
2015-01-28 14:54:41 +00:00
|
|
|
|
|
|
|
class C(object):
|
2015-01-29 08:22:06 +00:00
|
|
|
__attrs_attrs__ = [a]
|
|
|
|
|
2015-01-28 14:54:41 +00:00
|
|
|
C = _add_init(C)
|
|
|
|
|
|
|
|
with pytest.raises(VException) as e:
|
|
|
|
C(42)
|
2015-01-29 11:20:17 +00:00
|
|
|
assert ((a, 42,),) == e.value.args
|
2015-01-29 12:05:04 +00:00
|
|
|
|
|
|
|
def test_underscores(self):
|
|
|
|
"""
|
|
|
|
The argument names in `__init__` are without leading and trailing
|
|
|
|
underscores.
|
|
|
|
"""
|
|
|
|
class C(object):
|
|
|
|
__attrs_attrs__ = [simple_attr("_private")]
|
|
|
|
|
|
|
|
C = _add_init(C)
|
|
|
|
i = C(private=42)
|
|
|
|
assert 42 == i._private
|
2015-01-29 15:24:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestNothing(object):
|
|
|
|
"""
|
|
|
|
Tests for `_Nothing`.
|
|
|
|
"""
|
|
|
|
def test_copy(self):
|
|
|
|
"""
|
|
|
|
__copy__ returns the same object.
|
|
|
|
"""
|
|
|
|
n = _Nothing()
|
|
|
|
assert n is copy.copy(n)
|
|
|
|
|
|
|
|
def test_deepcopy(self):
|
|
|
|
"""
|
|
|
|
__deepcopy__ returns the same object.
|
|
|
|
"""
|
|
|
|
n = _Nothing()
|
|
|
|
assert n is copy.deepcopy(n)
|
|
|
|
|
|
|
|
def test_eq(self):
|
|
|
|
"""
|
|
|
|
All instances are equal.
|
|
|
|
"""
|
|
|
|
assert _Nothing() == _Nothing() == NOTHING
|
|
|
|
assert not (_Nothing() != _Nothing)
|