def test_no_cmp(self): """ If `no_cmp` is set, ignore that attribute. """ C = make_class("C", {"a": attr(no_cmp=True), "b": attr()}) assert C(1, 2) == C(2, 2)
def test_hash(self): """ If `hash` is False, ignore that attribute. """ C = make_class("C", {"a": attr(hash=False), "b": attr()}) assert hash(C(1, 2)) == hash(C(2, 2))
def test_repr(self): """ If `repr` is False, ignore that attribute. """ C = make_class("C", {"a": attr(repr=False), "b": attr()}) assert "C(b=2)" == repr(C(1, 2))
def test_cmp(self): """ If `cmp` is False, ignore that attribute. """ C = make_class("C", {"a": attr(cmp=False), "b": attr()}) assert C(1, 2) == C(2, 2)
def test_no_repr(self): """ If `no_repr` is set, ignore that attribute. """ C = make_class("C", {"a": attr(no_repr=True), "b": attr()}) assert "C(b=2)" == repr(C(1, 2))
def test_no_hash(self): """ If `no_hash` is set, ignore that attribute. """ C = make_class("C", {"a": attr(no_hash=True), "b": attr()}) assert hash(C(1, 2)) == hash(C(2, 2))
def test_repr(self, slots): """ If `repr` is False, ignore that attribute. """ C = make_class("C", {"a": attr(repr=False), "b": attr()}, slots=slots) assert "C(b=2)" == repr(C(1, 2))
def test_hash(self, slots): """ If `hash` is False, ignore that attribute. """ C = make_class("C", {"a": attr(hash=False), "b": attr()}, slots=slots) assert hash(C(1, 2)) == hash(C(2, 2))
def test_success(self): """ If the validator suceeds, nothing gets raised. """ C = make_class("C", {"x": attr(validator=lambda *a: None), "y": attr()}) validate(C(1, 2))
def test_cmp(self, slots): """ If `cmp` is False, ignore that attribute. """ C = make_class("C", {"a": attr(cmp=False), "b": attr()}, slots=slots) assert C(1, 2) == C(2, 2)
def test_hash_attribute(self, slots): """ If `hash` is False on an attribute, ignore that attribute. """ C = make_class("C", {"a": attr(hash=False), "b": attr()}, slots=slots, hash=True) assert hash(C(1, 2)) == hash(C(2, 2))
def test_convert(self): """ Return value of convert is used as the attribute's value. """ C = make_class("C", {"x": attr(convert=lambda v: v + 1), "y": attr()}) c = C(1, 2) assert c.x == 2 assert c.y == 2
def test_multiple_empty(self): """ Empty list/tuple for validator is the same as None. """ C1 = make_class("C", {"x": attr(validator=[])}) C2 = make_class("C", {"x": attr(validator=None)}) assert inspect.getsource(C1.__init__) == inspect.getsource(C2.__init__)
def test_no_init_order(self): """ If an attribute is `init=False`, it's legal to come after a mandatory attribute. """ make_class("C", { "a": attr(default=Factory(list)), "b": attr(init=False), })
def test_no_init_order(self, slots, frozen): """ If an attribute is `init=False`, it's legal to come after a mandatory attribute. """ make_class("C", { "a": attr(default=Factory(list)), "b": attr(init=False), }, slots=slots, frozen=frozen)
def test_no_init(self): """ If `no_init` is set, ignore that attribute. """ C = make_class("C", {"a": attr(no_init=True), "b": attr()}) with pytest.raises(TypeError) as e: C(a=1, b=2) msg = e.value if PY26 else e.value.args[0] assert "__init__() got an unexpected keyword argument 'a'" == msg
def test_init(self): """ If `init` is False, ignore that attribute. """ C = make_class("C", {"a": attr(init=False), "b": attr()}) with pytest.raises(TypeError) as e: C(a=1, b=2) msg = e.value if PY26 else e.value.args[0] assert "__init__() got an unexpected keyword argument 'a'" == msg
def test_init(self, slots): """ If `init` is False, ignore that attribute. """ C = make_class("C", {"a": attr(init=False), "b": attr()}, slots=slots) with pytest.raises(TypeError) as e: C(a=1, b=2) assert ("__init__() got an unexpected keyword argument 'a'" == e.value.args[0])
def test_convert_property(self, val, init): """ Property tests for attributes with convert. """ C = make_class("C", {"y": attr(), "x": attr(init=init, default=val, convert=lambda v: v + 1), }) c = C(2) assert c.x == val + 1 assert c.y == 2
def test_dict(self): """ Passing a dict of name: _CountingAttr creates an equivalent class. """ C1 = make_class("C1", {"a": attr(default=42), "b": attr(default=None)}) @attributes class C2(object): a = attr(default=42) b = attr(default=None) assert C1.__attrs_attrs__ == C2.__attrs_attrs__
def test_convert_before_validate(self): """ Validation happens after conversion. """ def validator(inst, attr, val): raise RuntimeError("foo") C = make_class( "C", {"x": attr(validator=validator, convert=lambda v: 1 / 0), "y": attr()}) with pytest.raises(ZeroDivisionError): C(1, 2)
def test_init(self, slots, frozen): """ If `init` is False, ignore that attribute. """ C = make_class("C", {"a": attr(init=False), "b": attr()}, slots=slots, frozen=frozen) with pytest.raises(TypeError) as e: C(a=1, b=2) assert ( "__init__() got an unexpected keyword argument 'a'" == e.value.args[0] )
def test_multiple_validators(self): """ If a list is passed as a validator, all of its items are treated as one and must pass. """ def v1(_, __, value): if value == 23: raise TypeError("omg") def v2(_, __, value): if value == 42: raise ValueError("omg") C = make_class("C", {"x": attr(validator=[v1, v2])}) validate(C(1)) with pytest.raises(TypeError) as e: C(23) assert "omg" == e.value.args[0] with pytest.raises(ValueError) as e: C(42) assert "omg" == e.value.args[0]
def test_returns_Attr(self): """ Returns an instance of _CountingAttr. """ a = attr() assert isinstance(a, _CountingAttr)
def _test_validator_none_is_empty_tuple(self): """ If validator is None, it's transformed into an empty tuple. """ a = attr(validator=None) assert () == a._validator
def test_no_init_default(self): """ If `init` is False but a Factory is specified, don't allow passing that argument but initialize it anyway. """ C = make_class("C", { "_a": attr(init=False, default=42), "_b": attr(init=False, default=Factory(list)), "c": attr() }) 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)
def test_no_init_default(self, slots, frozen): """ If `init` is False but a Factory is specified, don't allow passing that argument but initialize it anyway. """ C = make_class("C", { "_a": attr(init=False, default=42), "_b": attr(init=False, default=Factory(list)), "c": attr() }, slots=slots, frozen=frozen) 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)
def test_validator_others(self): """ Does not interfere when setting non-attrs attributes. """ C = make_class("C", {"a": attr("a", validator=instance_of(int))}) i = C(1) i.b = "foo" assert 1 == i.a assert "foo" == i.b
def test_these(self): """ If these is passed, use it and ignore body. """ class C(object): y = attr() _transform_attrs(C, {"x": attr()}) assert (simple_attr("x"), ) == C.__attrs_attrs__ assert isinstance(C.y, _CountingAttr)
def test_validator_decorator_single(self): """ """ a = attr() @a.validator def v(): pass assert _AndValidator((v, )) == a._validator
def test_default_decorator_already_set(self): """ Raise DefaultAlreadySetError if the decorator is used after a default has been set. """ a = attr(default=42) with pytest.raises(DefaultAlreadySetError): @a.default def f(self): pass
def test_factory_takes_self(self): """ If takes_self on factories is True, self is passed. """ C = make_class("C", {"x": attr(default=Factory( (lambda self: self), takes_self=True ))}) i = C() assert i is i.x
def test_these(self): """ If these is passed, use it and ignore body. """ class C(object): y = attr() _transform_attrs(C, {"x": attr()}) assert ( simple_attr("x"), ) == C.__attrs_attrs__ assert isinstance(C.y, _CountingAttr)
def test_default_decorator_sets(self): """ Decorator wraps the method in a Factory with pass_self=True and sets the default. """ a = attr() @a.default def f(self): pass assert Factory(f, True) == a._default
def test_validator_decorator_single(self): """ If _CountingAttr.validator is used as a decorator and there is no decorator set, the decorated method is used as the validator. """ a = attr() @a.validator def v(): pass assert v == a._validator
def test_validators_lists_to_wrapped_tuples(self): """ If a list is passed as validator, it's just converted to a tuple. """ def v1(_, __): pass def v2(_, __): pass a = attr(validator=[v1, v2]) assert _AndValidator((v1, v2,)) == a._validator
def test_hash_attribute_mirrors_cmp(self, cmp): """ If `hash` is None, the hash generation mirrors `cmp`. """ C = make_class("C", {"a": attr(cmp=cmp)}, cmp=True, frozen=True) 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))
def test_validator_others(self, slots): """ Does not interfere when setting non-attrs attributes. """ C = make_class("C", {"a": attr("a", validator=instance_of(int))}, slots=slots) i = C(1) assert 1 == i.a if not slots: i.b = "foo" assert "foo" == i.b else: with pytest.raises(AttributeError): i.b = "foo"
def test_propagates(self): """ The exception of the validator is handed through. """ def raiser(_, __, value): if value == 42: raise FloatingPointError C = make_class("C", {"x": attr(validator=raiser)}) i = C(1) i.x = 42 with pytest.raises(FloatingPointError): validate(i)
def test_validator_decorator(self, wrap): """ If _CountingAttr.validator is used as a decorator and there is already a decorator set, the decorators are composed using `and_`. """ def v(_, __): pass a = attr(validator=wrap(v)) @a.validator def v2(self, _, __): pass assert _AndValidator((v, v2,)) == a._validator
def test_validator(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) C = make_class("C", {"a": attr("a", validator=raiser)}) with pytest.raises(VException) as e: C(42) assert (C.a, 42,) == e.value.args[1:] assert isinstance(e.value.args[0], C)
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: make_class("C", {"a": attr(hash=1)}), assert exc_args == e.value.args