2015-01-29 15:24:49 +00:00
|
|
|
"""
|
2015-01-29 22:10:56 +00:00
|
|
|
Tests for dunder methods from `attrib._make`.
|
2015-01-29 15:24:49 +00:00
|
|
|
"""
|
|
|
|
|
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
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2016-03-22 23:15:14 +00:00
|
|
|
from hypothesis import given
|
|
|
|
from hypothesis.strategies import booleans
|
2015-01-28 14:54:41 +00:00
|
|
|
|
2017-10-02 17:32:10 +00:00
|
|
|
import attr
|
|
|
|
|
2015-01-29 22:10:56 +00:00
|
|
|
from attr._make import (
|
2017-11-26 21:18:07 +00:00
|
|
|
NOTHING, Factory, _add_init, _add_repr, _Nothing, fields, make_class
|
2015-01-27 16:53:17 +00:00
|
|
|
)
|
2015-01-30 12:23:43 +00:00
|
|
|
from attr.validators import instance_of
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2017-10-02 17:32:10 +00:00
|
|
|
from .utils import simple_attr, simple_class
|
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2015-02-20 12:29:47 +00:00
|
|
|
CmpC = simple_class(cmp=True)
|
2016-03-14 02:02:13 +00:00
|
|
|
CmpCSlots = simple_class(cmp=True, slots=True)
|
2015-02-20 12:29:47 +00:00
|
|
|
ReprC = simple_class(repr=True)
|
2016-03-14 02:02:13 +00:00
|
|
|
ReprCSlots = simple_class(repr=True, slots=True)
|
2017-02-19 08:51:43 +00:00
|
|
|
|
|
|
|
# HashC is hashable by explicit definition while HashCSlots is hashable
|
|
|
|
# implicitly.
|
2015-02-20 12:29:47 +00:00
|
|
|
HashC = simple_class(hash=True)
|
2017-02-19 08:51:43 +00:00
|
|
|
HashCSlots = simple_class(hash=None, cmp=True, frozen=True, slots=True)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
class InitC(object):
|
|
|
|
__attrs_attrs__ = [simple_attr("a"), simple_attr("b")]
|
|
|
|
|
2016-11-19 08:47:03 +00:00
|
|
|
|
2016-08-20 16:45:15 +00:00
|
|
|
InitC = _add_init(InitC, False)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
|
2015-01-30 16:48:34 +00:00
|
|
|
class TestAddCmp(object):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2015-01-30 16:48:34 +00:00
|
|
|
Tests for `_add_cmp`.
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2016-03-22 23:15:14 +00:00
|
|
|
@given(booleans())
|
2016-03-14 02:02:13 +00:00
|
|
|
def test_cmp(self, slots):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2015-02-20 12:29:47 +00:00
|
|
|
If `cmp` is False, ignore that attribute.
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {
|
|
|
|
"a": attr.ib(cmp=False),
|
|
|
|
"b": attr.ib()
|
|
|
|
}, slots=slots)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2015-01-30 16:48:34 +00:00
|
|
|
assert C(1, 2) == C(2, 2)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_equal(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
Equal objects are detected as equal.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(1, 2) == cls(1, 2)
|
|
|
|
assert not (cls(1, 2) != cls(1, 2))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_unequal_same_class(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
Unequal objects of correct type are detected as unequal.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(1, 2) != cls(2, 1)
|
|
|
|
assert not (cls(1, 2) == cls(2, 1))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_unequal_different_class(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
Unequal objects of different type are detected even if their attributes
|
2015-01-27 16:53:17 +00:00
|
|
|
match.
|
|
|
|
"""
|
|
|
|
class NotCmpC(object):
|
|
|
|
a = 1
|
|
|
|
b = 2
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(1, 2) != NotCmpC()
|
|
|
|
assert not (cls(1, 2) == NotCmpC())
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_lt(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__lt__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((1, 2), (2, 1)),
|
|
|
|
((1, 2), (1, 3)),
|
|
|
|
(("a", "b"), ("b", "a")),
|
|
|
|
]:
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(*a) < cls(*b)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_lt_unordable(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__lt__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert NotImplemented == (cls(1, 2).__lt__(42))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_le(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__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")),
|
|
|
|
]:
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(*a) <= cls(*b)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_le_unordable(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__le__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert NotImplemented == (cls(1, 2).__le__(42))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_gt(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__gt__ compares objects as tuples of attribute values.
|
|
|
|
"""
|
|
|
|
for a, b in [
|
|
|
|
((2, 1), (1, 2)),
|
|
|
|
((1, 3), (1, 2)),
|
|
|
|
(("b", "a"), ("a", "b")),
|
|
|
|
]:
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(*a) > cls(*b)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_gt_unordable(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__gt__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert NotImplemented == (cls(1, 2).__gt__(42))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_ge(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__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")),
|
|
|
|
]:
|
2016-03-17 18:40:11 +00:00
|
|
|
assert cls(*a) >= cls(*b)
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
|
|
|
|
def test_ge_unordable(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__ge__ returns NotImplemented if classes differ.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert NotImplemented == (cls(1, 2).__ge__(42))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestAddRepr(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_repr`.
|
|
|
|
"""
|
2016-11-20 14:51:53 +00:00
|
|
|
@pytest.mark.parametrize("slots", [True, False])
|
2016-03-14 02:02:13 +00:00
|
|
|
def test_repr(self, slots):
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2015-02-20 12:29:47 +00:00
|
|
|
If `repr` is False, ignore that attribute.
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {
|
|
|
|
"a": attr.ib(repr=False),
|
|
|
|
"b": attr.ib()
|
|
|
|
}, slots=slots)
|
2015-01-30 16:48:34 +00:00
|
|
|
|
|
|
|
assert "C(b=2)" == repr(C(1, 2))
|
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [ReprC, ReprCSlots])
|
|
|
|
def test_repr_works(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2015-01-29 12:05:04 +00:00
|
|
|
repr returns a sensible value.
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert "C(a=1, b=2)" == repr(cls(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)
|
|
|
|
|
2017-12-06 18:21:58 +00:00
|
|
|
def test_repr_uninitialized_member(self):
|
|
|
|
"""
|
|
|
|
repr signals unset attributes
|
|
|
|
"""
|
|
|
|
C = make_class("C", {
|
|
|
|
"a": attr.ib(init=False),
|
|
|
|
})
|
|
|
|
|
|
|
|
assert "C(a=NOTHING)" == repr(C())
|
|
|
|
|
2017-06-01 07:30:31 +00:00
|
|
|
@given(add_str=booleans(), slots=booleans())
|
|
|
|
def test_str(self, add_str, slots):
|
2016-11-20 14:51:53 +00:00
|
|
|
"""
|
|
|
|
If str is True, it returns the same as repr.
|
|
|
|
|
|
|
|
This only makes sense when subclassing a class with an poor __str__
|
|
|
|
(like Exceptions).
|
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
@attr.s(str=add_str, slots=slots)
|
2016-11-20 14:51:53 +00:00
|
|
|
class Error(Exception):
|
2017-10-02 17:32:10 +00:00
|
|
|
x = attr.ib()
|
2016-11-20 14:51:53 +00:00
|
|
|
|
|
|
|
e = Error(42)
|
|
|
|
|
|
|
|
assert (str(e) == repr(e)) is add_str
|
|
|
|
|
|
|
|
def test_str_no_repr(self):
|
|
|
|
"""
|
|
|
|
Raises a ValueError if repr=False and str=True.
|
|
|
|
"""
|
|
|
|
with pytest.raises(ValueError) as e:
|
|
|
|
simple_class(repr=False, str=True)
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"__str__ can only be generated if a __repr__ exists."
|
|
|
|
) == e.value.args[0]
|
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
class TestAddHash(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_hash`.
|
|
|
|
"""
|
2017-02-19 08:51:43 +00:00
|
|
|
def test_enforces_type(self):
|
|
|
|
"""
|
|
|
|
The `hash` argument to both attrs and attrib must be None, True, or
|
|
|
|
False.
|
|
|
|
"""
|
|
|
|
exc_args = ("Invalid value for hash. Must be True, False, or None.",)
|
|
|
|
|
|
|
|
with pytest.raises(TypeError) as e:
|
|
|
|
make_class("C", {}, hash=1),
|
|
|
|
|
|
|
|
assert exc_args == e.value.args
|
|
|
|
|
|
|
|
with pytest.raises(TypeError) as e:
|
2017-10-02 17:32:10 +00:00
|
|
|
make_class("C", {"a": attr.ib(hash=1)}),
|
2017-02-19 08:51:43 +00:00
|
|
|
|
|
|
|
assert exc_args == e.value.args
|
|
|
|
|
2016-03-22 23:15:14 +00:00
|
|
|
@given(booleans())
|
2017-02-19 08:51:43 +00:00
|
|
|
def test_hash_attribute(self, slots):
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2017-02-19 08:51:43 +00:00
|
|
|
If `hash` is False on an attribute, ignore that attribute.
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib(hash=False), "b": attr.ib()},
|
2017-02-19 08:51:43 +00:00
|
|
|
slots=slots, hash=True)
|
2015-01-30 16:48:34 +00:00
|
|
|
|
|
|
|
assert hash(C(1, 2)) == hash(C(2, 2))
|
|
|
|
|
2017-02-19 08:51:43 +00:00
|
|
|
@given(booleans())
|
|
|
|
def test_hash_attribute_mirrors_cmp(self, cmp):
|
|
|
|
"""
|
|
|
|
If `hash` is None, the hash generation mirrors `cmp`.
|
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib(cmp=cmp)}, cmp=True, frozen=True)
|
2017-02-19 08:51:43 +00:00
|
|
|
|
|
|
|
if cmp:
|
|
|
|
assert C(1) != C(2)
|
|
|
|
assert hash(C(1)) != hash(C(2))
|
|
|
|
assert hash(C(1)) == hash(C(1))
|
|
|
|
else:
|
|
|
|
assert C(1) == C(2)
|
|
|
|
assert hash(C(1)) == hash(C(2))
|
|
|
|
|
|
|
|
@given(booleans())
|
|
|
|
def test_hash_mirrors_cmp(self, cmp):
|
|
|
|
"""
|
|
|
|
If `hash` is None, the hash generation mirrors `cmp`.
|
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib()}, cmp=cmp, frozen=True)
|
2017-02-19 08:51:43 +00:00
|
|
|
|
|
|
|
i = C(1)
|
|
|
|
|
|
|
|
assert i == i
|
|
|
|
assert hash(i) == hash(i)
|
|
|
|
|
|
|
|
if cmp:
|
|
|
|
assert C(1) == C(1)
|
|
|
|
assert hash(C(1)) == hash(C(1))
|
|
|
|
else:
|
|
|
|
assert C(1) != C(1)
|
|
|
|
assert hash(C(1)) != hash(C(1))
|
|
|
|
|
2016-03-17 18:40:11 +00:00
|
|
|
@pytest.mark.parametrize("cls", [HashC, HashCSlots])
|
|
|
|
def test_hash_works(self, cls):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
__hash__ returns different hashes for different values.
|
|
|
|
"""
|
2016-03-17 18:40:11 +00:00
|
|
|
assert hash(cls(1, 2)) != hash(cls(1, 1))
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2017-02-19 08:51:43 +00:00
|
|
|
def test_hash_default(self):
|
|
|
|
"""
|
|
|
|
Classes are not hashable by default.
|
|
|
|
"""
|
|
|
|
C = make_class("C", {})
|
|
|
|
|
|
|
|
with pytest.raises(TypeError) as e:
|
|
|
|
hash(C())
|
|
|
|
|
|
|
|
assert e.value.args[0] in (
|
|
|
|
"'C' objects are unhashable", # PyPy
|
|
|
|
"unhashable type: 'C'", # CPython
|
|
|
|
)
|
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
|
|
|
|
class TestAddInit(object):
|
2015-01-28 14:54:41 +00:00
|
|
|
"""
|
|
|
|
Tests for `_add_init`.
|
|
|
|
"""
|
2016-08-20 16:45:15 +00:00
|
|
|
@given(booleans(), booleans())
|
|
|
|
def test_init(self, slots, frozen):
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2015-02-20 12:29:47 +00:00
|
|
|
If `init` is False, ignore that attribute.
|
2015-01-30 16:48:34 +00:00
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib(init=False), "b": attr.ib()},
|
2016-08-20 16:45:15 +00:00
|
|
|
slots=slots, frozen=frozen)
|
2015-01-30 16:48:34 +00:00
|
|
|
with pytest.raises(TypeError) as e:
|
|
|
|
C(a=1, b=2)
|
|
|
|
|
2015-07-03 09:30:53 +00:00
|
|
|
assert (
|
|
|
|
"__init__() got an unexpected keyword argument 'a'" ==
|
|
|
|
e.value.args[0]
|
|
|
|
)
|
2015-01-30 16:48:34 +00:00
|
|
|
|
2016-08-20 16:45:15 +00:00
|
|
|
@given(booleans(), booleans())
|
|
|
|
def test_no_init_default(self, slots, frozen):
|
2015-12-19 12:43:07 +00:00
|
|
|
"""
|
|
|
|
If `init` is False but a Factory is specified, don't allow passing that
|
|
|
|
argument but initialize it anyway.
|
|
|
|
"""
|
|
|
|
C = make_class("C", {
|
2017-10-02 17:32:10 +00:00
|
|
|
"_a": attr.ib(init=False, default=42),
|
|
|
|
"_b": attr.ib(init=False, default=Factory(list)),
|
|
|
|
"c": attr.ib()
|
2016-08-20 16:45:15 +00:00
|
|
|
}, slots=slots, frozen=frozen)
|
2015-12-19 12:43:07 +00:00
|
|
|
with pytest.raises(TypeError):
|
|
|
|
C(a=1, c=2)
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
C(b=1, c=2)
|
|
|
|
|
|
|
|
i = C(23)
|
|
|
|
assert (42, [], 23) == (i._a, i._b, i.c)
|
|
|
|
|
2016-08-20 16:45:15 +00:00
|
|
|
@given(booleans(), booleans())
|
|
|
|
def test_no_init_order(self, slots, frozen):
|
2016-02-06 11:20:37 +00:00
|
|
|
"""
|
|
|
|
If an attribute is `init=False`, it's legal to come after a mandatory
|
|
|
|
attribute.
|
|
|
|
"""
|
|
|
|
make_class("C", {
|
2017-10-02 17:32:10 +00:00
|
|
|
"a": attr.ib(default=Factory(list)),
|
|
|
|
"b": attr.ib(init=False),
|
2016-08-20 16:45:15 +00:00
|
|
|
}, slots=slots, frozen=frozen)
|
2016-02-06 11:20:37 +00:00
|
|
|
|
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
|
|
|
|
|
2015-01-29 21:32:41 +00:00
|
|
|
def test_default(self):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
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-30 16:48:34 +00:00
|
|
|
simple_attr(name="a", default=2),
|
|
|
|
simple_attr(name="b", default="hallo"),
|
|
|
|
simple_attr(name="c", default=None),
|
2015-01-28 14:54:41 +00:00
|
|
|
]
|
2015-01-27 16:53:17 +00:00
|
|
|
|
2016-08-20 16:45:15 +00:00
|
|
|
C = _add_init(C, False)
|
2015-01-27 16:53:17 +00:00
|
|
|
i = C()
|
|
|
|
assert 2 == i.a
|
|
|
|
assert "hallo" == i.b
|
|
|
|
assert None is i.c
|
|
|
|
|
2015-01-29 21:29:37 +00:00
|
|
|
def test_factory(self):
|
2015-01-27 16:53:17 +00:00
|
|
|
"""
|
|
|
|
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-30 16:48:34 +00:00
|
|
|
simple_attr(name="a", default=Factory(list)),
|
|
|
|
simple_attr(name="b", default=Factory(D)),
|
2015-01-28 14:54:41 +00:00
|
|
|
]
|
2016-08-20 16:45:15 +00:00
|
|
|
C = _add_init(C, False)
|
2015-01-27 16:53:17 +00:00
|
|
|
i = C()
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2015-01-27 16:53:17 +00:00
|
|
|
assert [] == i.a
|
|
|
|
assert isinstance(i.b, D)
|
2015-01-28 14:54:41 +00:00
|
|
|
|
|
|
|
def test_validator(self):
|
|
|
|
"""
|
2015-02-09 12:16:56 +00:00
|
|
|
If a validator is passed, call it with the preliminary instance, 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):
|
2015-02-09 12:16:56 +00:00
|
|
|
raise VException(*args)
|
2015-01-29 08:22:06 +00:00
|
|
|
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib("a", validator=raiser)})
|
2015-01-28 14:54:41 +00:00
|
|
|
with pytest.raises(VException) as e:
|
|
|
|
C(42)
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2017-10-02 10:14:37 +00:00
|
|
|
assert (fields(C).a, 42,) == e.value.args[1:]
|
2015-02-09 12:16:56 +00:00
|
|
|
assert isinstance(e.value.args[0], C)
|
2015-01-30 12:23:43 +00:00
|
|
|
|
2016-03-14 02:02:13 +00:00
|
|
|
def test_validator_slots(self):
|
|
|
|
"""
|
|
|
|
If a validator is passed, call it with the preliminary instance, the
|
|
|
|
Attribute, and the argument.
|
|
|
|
"""
|
|
|
|
class VException(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def raiser(*args):
|
|
|
|
raise VException(*args)
|
|
|
|
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {"a": attr.ib("a", validator=raiser)}, slots=True)
|
2016-03-14 02:02:13 +00:00
|
|
|
with pytest.raises(VException) as e:
|
|
|
|
C(42)
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2016-03-14 02:02:13 +00:00
|
|
|
assert (fields(C)[0], 42,) == e.value.args[1:]
|
|
|
|
assert isinstance(e.value.args[0], C)
|
|
|
|
|
2016-03-22 23:15:14 +00:00
|
|
|
@given(booleans())
|
2016-03-14 02:02:13 +00:00
|
|
|
def test_validator_others(self, slots):
|
2015-01-30 12:23:43 +00:00
|
|
|
"""
|
|
|
|
Does not interfere when setting non-attrs attributes.
|
|
|
|
"""
|
2017-10-02 17:32:10 +00:00
|
|
|
C = make_class("C", {
|
|
|
|
"a": attr.ib("a", validator=instance_of(int))
|
|
|
|
}, slots=slots)
|
2015-01-30 12:23:43 +00:00
|
|
|
i = C(1)
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2015-01-30 12:23:43 +00:00
|
|
|
assert 1 == i.a
|
2017-10-02 17:32:10 +00:00
|
|
|
|
2016-03-14 02:02:13 +00:00
|
|
|
if not slots:
|
|
|
|
i.b = "foo"
|
|
|
|
assert "foo" == i.b
|
|
|
|
else:
|
|
|
|
with pytest.raises(AttributeError):
|
|
|
|
i.b = "foo"
|
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")]
|
|
|
|
|
2016-08-20 16:45:15 +00:00
|
|
|
C = _add_init(C, False)
|
2015-01-29 12:05:04 +00:00
|
|
|
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
|
2016-04-28 08:32:38 +00:00
|
|
|
assert not (_Nothing() != _Nothing())
|
|
|
|
assert 1 != _Nothing()
|