1"""
2Tests for `attr._make`.
3"""
4
5from __future__ import absolute_import, division, print_function
6
7import copy
8import inspect
9import itertools
10import sys
11
12from operator import attrgetter
13
14import pytest
15
16from hypothesis import given
17from hypothesis.strategies import booleans, integers, lists, sampled_from, text
18
19import attr
20
21from attr import _config
22from attr._compat import PY2, ordered_dict
23from attr._make import (
24    Attribute, Factory, _AndValidator, _Attributes, _ClassBuilder,
25    _CountingAttr, _transform_attrs, and_, fields, fields_dict, make_class,
26    validate
27)
28from attr.exceptions import DefaultAlreadySetError, NotAnAttrsClassError
29
30from .strategies import (
31    gen_attr_names, list_of_attrs, simple_attrs, simple_attrs_with_metadata,
32    simple_attrs_without_metadata, simple_classes
33)
34from .utils import simple_attr
35
36
37attrs_st = simple_attrs.map(lambda c: Attribute.from_counting_attr("name", c))
38
39
40class TestCountingAttr(object):
41    """
42    Tests for `attr`.
43    """
44    def test_returns_Attr(self):
45        """
46        Returns an instance of _CountingAttr.
47        """
48        a = attr.ib()
49
50        assert isinstance(a, _CountingAttr)
51
52    def test_validators_lists_to_wrapped_tuples(self):
53        """
54        If a list is passed as validator, it's just converted to a tuple.
55        """
56        def v1(_, __):
57            pass
58
59        def v2(_, __):
60            pass
61
62        a = attr.ib(validator=[v1, v2])
63
64        assert _AndValidator((v1, v2,)) == a._validator
65
66    def test_validator_decorator_single(self):
67        """
68        If _CountingAttr.validator is used as a decorator and there is no
69        decorator set, the decorated method is used as the validator.
70        """
71        a = attr.ib()
72
73        @a.validator
74        def v():
75            pass
76
77        assert v == a._validator
78
79    @pytest.mark.parametrize("wrap", [
80        lambda v: v,
81        lambda v: [v],
82        lambda v: and_(v)
83
84    ])
85    def test_validator_decorator(self, wrap):
86        """
87        If _CountingAttr.validator is used as a decorator and there is already
88        a decorator set, the decorators are composed using `and_`.
89        """
90        def v(_, __):
91            pass
92
93        a = attr.ib(validator=wrap(v))
94
95        @a.validator
96        def v2(self, _, __):
97            pass
98
99        assert _AndValidator((v, v2,)) == a._validator
100
101    def test_default_decorator_already_set(self):
102        """
103        Raise DefaultAlreadySetError if the decorator is used after a default
104        has been set.
105        """
106        a = attr.ib(default=42)
107
108        with pytest.raises(DefaultAlreadySetError):
109            @a.default
110            def f(self):
111                pass
112
113    def test_default_decorator_sets(self):
114        """
115        Decorator wraps the method in a Factory with pass_self=True and sets
116        the default.
117        """
118        a = attr.ib()
119
120        @a.default
121        def f(self):
122            pass
123
124        assert Factory(f, True) == a._default
125
126
127class TestAttribute(object):
128    """
129    Tests for `attr.Attribute`.
130    """
131    def test_deprecated_convert_argument(self):
132        """
133        Using *convert* raises a DeprecationWarning and sets the converter
134        field.
135        """
136        def conv(v):
137            return v
138
139        with pytest.warns(DeprecationWarning) as wi:
140            a = Attribute(
141                "a", True, True, True, True, True, True, convert=conv
142            )
143        w = wi.pop()
144
145        assert conv == a.converter
146        assert (
147            "The `convert` argument is deprecated in favor of `converter`.  "
148            "It will be removed after 2019/01.",
149        ) == w.message.args
150        assert __file__ == w.filename
151
152    def test_deprecated_convert_attribute(self):
153        """
154        If Attribute.convert is accessed, a DeprecationWarning is raised.
155        """
156        def conv(v):
157            return v
158
159        a = simple_attr("a", converter=conv)
160        with pytest.warns(DeprecationWarning) as wi:
161            convert = a.convert
162        w = wi.pop()
163
164        assert conv is convert is a.converter
165        assert (
166            "The `convert` attribute is deprecated in favor of `converter`.  "
167            "It will be removed after 2019/01.",
168        ) == w.message.args
169        assert __file__ == w.filename
170
171    def test_convert_converter(self):
172        """
173        A TypeError is raised if both *convert* and *converter* are passed.
174        """
175        with pytest.raises(RuntimeError) as ei:
176            Attribute(
177                "a", True, True, True, True, True, True,
178                convert=lambda v: v, converter=lambda v: v,
179            )
180
181        assert (
182            "Can't pass both `convert` and `converter`.  "
183            "Please use `converter` only.",
184        ) == ei.value.args
185
186
187def make_tc():
188    class TransformC(object):
189        z = attr.ib()
190        y = attr.ib()
191        x = attr.ib()
192        a = 42
193    return TransformC
194
195
196class TestTransformAttrs(object):
197    """
198    Tests for `_transform_attrs`.
199    """
200    def test_no_modifications(self):
201        """
202        Doesn't attach __attrs_attrs__ to the class anymore.
203        """
204        C = make_tc()
205        _transform_attrs(C, None, False)
206
207        assert None is getattr(C, "__attrs_attrs__", None)
208
209    def test_normal(self):
210        """
211        Transforms every `_CountingAttr` and leaves others (a) be.
212        """
213        C = make_tc()
214        attrs, _, _ = _transform_attrs(C, None, False)
215
216        assert ["z", "y", "x"] == [a.name for a in attrs]
217
218    def test_empty(self):
219        """
220        No attributes works as expected.
221        """
222        @attr.s
223        class C(object):
224            pass
225
226        assert _Attributes(((), [], {})) == _transform_attrs(C, None, False)
227
228    def test_transforms_to_attribute(self):
229        """
230        All `_CountingAttr`s are transformed into `Attribute`s.
231        """
232        C = make_tc()
233        attrs, super_attrs, _ = _transform_attrs(C, None, False)
234
235        assert [] == super_attrs
236        assert 3 == len(attrs)
237        assert all(isinstance(a, Attribute) for a in attrs)
238
239    def test_conflicting_defaults(self):
240        """
241        Raises `ValueError` if attributes with defaults are followed by
242        mandatory attributes.
243        """
244        class C(object):
245            x = attr.ib(default=None)
246            y = attr.ib()
247
248        with pytest.raises(ValueError) as e:
249            _transform_attrs(C, None, False)
250        assert (
251            "No mandatory attributes allowed after an attribute with a "
252            "default value or factory.  Attribute in question: Attribute"
253            "(name='y', default=NOTHING, validator=None, repr=True, "
254            "cmp=True, hash=None, init=True, metadata=mappingproxy({}), "
255            "type=None, converter=None)",
256        ) == e.value.args
257
258    def test_these(self):
259        """
260        If these is passed, use it and ignore body and super classes.
261        """
262        class Base(object):
263            z = attr.ib()
264
265        class C(Base):
266            y = attr.ib()
267
268        attrs, super_attrs, _ = _transform_attrs(C, {"x": attr.ib()}, False)
269
270        assert [] == super_attrs
271        assert (
272            simple_attr("x"),
273        ) == attrs
274
275    def test_these_leave_body(self):
276        """
277        If these is passed, no attributes are removed from the body.
278        """
279        @attr.s(init=False, these={"x": attr.ib()})
280        class C(object):
281            x = 5
282
283        assert 5 == C().x
284        assert "C(x=5)" == repr(C())
285
286    def test_these_ordered(self):
287        """
288        If these is passed ordered attrs, their order respect instead of the
289        counter.
290        """
291        b = attr.ib(default=2)
292        a = attr.ib(default=1)
293
294        @attr.s(these=ordered_dict([("a", a), ("b", b)]))
295        class C(object):
296            pass
297
298        assert "C(a=1, b=2)" == repr(C())
299
300    def test_multiple_inheritance(self):
301        """
302        Order of attributes doesn't get mixed up by multiple inheritance.
303
304        See #285
305        """
306        @attr.s
307        class A(object):
308            a1 = attr.ib(default="a1")
309            a2 = attr.ib(default="a2")
310
311        @attr.s
312        class B(A):
313            b1 = attr.ib(default="b1")
314            b2 = attr.ib(default="b2")
315
316        @attr.s
317        class C(B, A):
318            c1 = attr.ib(default="c1")
319            c2 = attr.ib(default="c2")
320
321        @attr.s
322        class D(A):
323            d1 = attr.ib(default="d1")
324            d2 = attr.ib(default="d2")
325
326        @attr.s
327        class E(C, D):
328            e1 = attr.ib(default="e1")
329            e2 = attr.ib(default="e2")
330
331        assert (
332            "E(a1='a1', a2='a2', b1='b1', b2='b2', c1='c1', c2='c2', d1='d1', "
333            "d2='d2', e1='e1', e2='e2')"
334        ) == repr(E())
335
336
337class TestAttributes(object):
338    """
339    Tests for the `attrs`/`attr.s` class decorator.
340    """
341    @pytest.mark.skipif(not PY2, reason="No old-style classes in Py3")
342    def test_catches_old_style(self):
343        """
344        Raises TypeError on old-style classes.
345        """
346        with pytest.raises(TypeError) as e:
347            @attr.s
348            class C:
349                pass
350
351        assert ("attrs only works with new-style classes.",) == e.value.args
352
353    def test_sets_attrs(self):
354        """
355        Sets the `__attrs_attrs__` class attribute with a list of `Attribute`s.
356        """
357        @attr.s
358        class C(object):
359            x = attr.ib()
360
361        assert "x" == C.__attrs_attrs__[0].name
362        assert all(isinstance(a, Attribute) for a in C.__attrs_attrs__)
363
364    def test_empty(self):
365        """
366        No attributes, no problems.
367        """
368        @attr.s
369        class C3(object):
370            pass
371
372        assert "C3()" == repr(C3())
373        assert C3() == C3()
374
375    @given(attr=attrs_st, attr_name=sampled_from(Attribute.__slots__))
376    def test_immutable(self, attr, attr_name):
377        """
378        Attribute instances are immutable.
379        """
380        with pytest.raises(AttributeError):
381            setattr(attr, attr_name, 1)
382
383    @pytest.mark.parametrize("method_name", [
384        "__repr__",
385        "__eq__",
386        "__hash__",
387        "__init__",
388    ])
389    def test_adds_all_by_default(self, method_name):
390        """
391        If no further arguments are supplied, all add_XXX functions except
392        add_hash are applied.  __hash__ is set to None.
393        """
394        # Set the method name to a sentinel and check whether it has been
395        # overwritten afterwards.
396        sentinel = object()
397
398        class C(object):
399            x = attr.ib()
400
401        setattr(C, method_name, sentinel)
402
403        C = attr.s(C)
404        meth = getattr(C, method_name)
405
406        assert sentinel != meth
407        if method_name == "__hash__":
408            assert meth is None
409
410    @pytest.mark.parametrize("arg_name, method_name", [
411        ("repr", "__repr__"),
412        ("cmp", "__eq__"),
413        ("hash", "__hash__"),
414        ("init", "__init__"),
415    ])
416    def test_respects_add_arguments(self, arg_name, method_name):
417        """
418        If a certain `add_XXX` is `False`, `__XXX__` is not added to the class.
419        """
420        # Set the method name to a sentinel and check whether it has been
421        # overwritten afterwards.
422        sentinel = object()
423
424        am_args = {
425            "repr": True,
426            "cmp": True,
427            "hash": True,
428            "init": True
429        }
430        am_args[arg_name] = False
431
432        class C(object):
433            x = attr.ib()
434
435        setattr(C, method_name, sentinel)
436
437        C = attr.s(**am_args)(C)
438
439        assert sentinel == getattr(C, method_name)
440
441    @pytest.mark.skipif(PY2, reason="__qualname__ is PY3-only.")
442    @given(slots_outer=booleans(), slots_inner=booleans())
443    def test_repr_qualname(self, slots_outer, slots_inner):
444        """
445        On Python 3, the name in repr is the __qualname__.
446        """
447        @attr.s(slots=slots_outer)
448        class C(object):
449            @attr.s(slots=slots_inner)
450            class D(object):
451                pass
452
453        assert "C.D()" == repr(C.D())
454        assert "GC.D()" == repr(GC.D())
455
456    @given(slots_outer=booleans(), slots_inner=booleans())
457    def test_repr_fake_qualname(self, slots_outer, slots_inner):
458        """
459        Setting repr_ns overrides a potentially guessed namespace.
460        """
461        @attr.s(slots=slots_outer)
462        class C(object):
463            @attr.s(repr_ns="C", slots=slots_inner)
464            class D(object):
465                pass
466        assert "C.D()" == repr(C.D())
467
468    @pytest.mark.skipif(PY2, reason="__qualname__ is PY3-only.")
469    @given(slots_outer=booleans(), slots_inner=booleans())
470    def test_name_not_overridden(self, slots_outer, slots_inner):
471        """
472        On Python 3, __name__ is different from __qualname__.
473        """
474        @attr.s(slots=slots_outer)
475        class C(object):
476            @attr.s(slots=slots_inner)
477            class D(object):
478                pass
479
480        assert C.D.__name__ == "D"
481        assert C.D.__qualname__ == C.__qualname__ + ".D"
482
483    @given(with_validation=booleans())
484    def test_post_init(self, with_validation, monkeypatch):
485        """
486        Verify that __attrs_post_init__ gets called if defined.
487        """
488        monkeypatch.setattr(_config, "_run_validators", with_validation)
489
490        @attr.s
491        class C(object):
492            x = attr.ib()
493            y = attr.ib()
494
495            def __attrs_post_init__(self2):
496                self2.z = self2.x + self2.y
497
498        c = C(x=10, y=20)
499
500        assert 30 == getattr(c, 'z', None)
501
502    def test_types(self):
503        """
504        Sets the `Attribute.type` attr from type argument.
505        """
506        @attr.s
507        class C(object):
508            x = attr.ib(type=int)
509            y = attr.ib(type=str)
510            z = attr.ib()
511
512        assert int is fields(C).x.type
513        assert str is fields(C).y.type
514        assert None is fields(C).z.type
515
516    @pytest.mark.parametrize("slots", [True, False])
517    def test_clean_class(self, slots):
518        """
519        Attribute definitions do not appear on the class body after @attr.s.
520        """
521        @attr.s(slots=slots)
522        class C(object):
523            x = attr.ib()
524
525        x = getattr(C, "x", None)
526
527        assert not isinstance(x, _CountingAttr)
528
529    def test_factory_sugar(self):
530        """
531        Passing factory=f is syntactic sugar for passing default=Factory(f).
532        """
533        @attr.s
534        class C(object):
535            x = attr.ib(factory=list)
536
537        assert Factory(list) == attr.fields(C).x.default
538
539    def test_sugar_factory_mutex(self):
540        """
541        Passing both default and factory raises ValueError.
542        """
543        with pytest.raises(ValueError, match="mutually exclusive"):
544            @attr.s
545            class C(object):
546                x = attr.ib(factory=list, default=Factory(list))
547
548    def test_sugar_callable(self):
549        """
550        Factory has to be a callable to prevent people from passing Factory
551        into it.
552        """
553        with pytest.raises(ValueError, match="must be a callable"):
554            @attr.s
555            class C(object):
556                x = attr.ib(factory=Factory(list))
557
558
559@attr.s
560class GC(object):
561    @attr.s
562    class D(object):
563        pass
564
565
566class TestMakeClass(object):
567    """
568    Tests for `make_class`.
569    """
570    @pytest.mark.parametrize("ls", [
571        list,
572        tuple
573    ])
574    def test_simple(self, ls):
575        """
576        Passing a list of strings creates attributes with default args.
577        """
578        C1 = make_class("C1", ls(["a", "b"]))
579
580        @attr.s
581        class C2(object):
582            a = attr.ib()
583            b = attr.ib()
584
585        assert C1.__attrs_attrs__ == C2.__attrs_attrs__
586
587    def test_dict(self):
588        """
589        Passing a dict of name: _CountingAttr creates an equivalent class.
590        """
591        C1 = make_class("C1", {
592            "a": attr.ib(default=42),
593            "b": attr.ib(default=None),
594        })
595
596        @attr.s
597        class C2(object):
598            a = attr.ib(default=42)
599            b = attr.ib(default=None)
600
601        assert C1.__attrs_attrs__ == C2.__attrs_attrs__
602
603    def test_attr_args(self):
604        """
605        attributes_arguments are passed to attributes
606        """
607        C = make_class("C", ["x"], repr=False)
608
609        assert repr(C(1)).startswith("<tests.test_make.C object at 0x")
610
611    def test_catches_wrong_attrs_type(self):
612        """
613        Raise `TypeError` if an invalid type for attrs is passed.
614        """
615        with pytest.raises(TypeError) as e:
616            make_class("C", object())
617
618        assert (
619            "attrs argument must be a dict or a list.",
620        ) == e.value.args
621
622    def test_bases(self):
623        """
624        Parameter bases default to (object,) and subclasses correctly
625        """
626        class D(object):
627            pass
628
629        cls = make_class("C", {})
630
631        assert cls.__mro__[-1] == object
632
633        cls = make_class("C", {}, bases=(D,))
634
635        assert D in cls.__mro__
636        assert isinstance(cls(), D)
637
638    @pytest.mark.parametrize("slots", [True, False])
639    def test_clean_class(self, slots):
640        """
641        Attribute definitions do not appear on the class body.
642        """
643        C = make_class("C", ["x"], slots=slots)
644
645        x = getattr(C, "x", None)
646
647        assert not isinstance(x, _CountingAttr)
648
649    def test_missing_sys_getframe(self, monkeypatch):
650        """
651        `make_class()` does not fail when `sys._getframe()` is not available.
652        """
653        monkeypatch.delattr(sys, '_getframe')
654        C = make_class("C", ["x"])
655
656        assert 1 == len(C.__attrs_attrs__)
657
658    def test_make_class_ordered(self):
659        """
660        If `make_class()` is passed ordered attrs, their order is respected
661        instead of the counter.
662        """
663        b = attr.ib(default=2)
664        a = attr.ib(default=1)
665
666        C = attr.make_class("C", ordered_dict([("a", a), ("b", b)]))
667
668        assert "C(a=1, b=2)" == repr(C())
669
670
671class TestFields(object):
672    """
673    Tests for `fields`.
674    """
675    def test_instance(self, C):
676        """
677        Raises `TypeError` on non-classes.
678        """
679        with pytest.raises(TypeError) as e:
680            fields(C(1, 2))
681
682        assert "Passed object must be a class." == e.value.args[0]
683
684    def test_handler_non_attrs_class(self, C):
685        """
686        Raises `ValueError` if passed a non-``attrs`` instance.
687        """
688        with pytest.raises(NotAnAttrsClassError) as e:
689            fields(object)
690
691        assert (
692            "{o!r} is not an attrs-decorated class.".format(o=object)
693        ) == e.value.args[0]
694
695    @given(simple_classes())
696    def test_fields(self, C):
697        """
698        Returns a list of `Attribute`a.
699        """
700        assert all(isinstance(a, Attribute) for a in fields(C))
701
702    @given(simple_classes())
703    def test_fields_properties(self, C):
704        """
705        Fields returns a tuple with properties.
706        """
707        for attribute in fields(C):
708            assert getattr(fields(C), attribute.name) is attribute
709
710
711class TestFieldsDict(object):
712    """
713    Tests for `fields_dict`.
714    """
715    def test_instance(self, C):
716        """
717        Raises `TypeError` on non-classes.
718        """
719        with pytest.raises(TypeError) as e:
720            fields_dict(C(1, 2))
721
722        assert "Passed object must be a class." == e.value.args[0]
723
724    def test_handler_non_attrs_class(self, C):
725        """
726        Raises `ValueError` if passed a non-``attrs`` instance.
727        """
728        with pytest.raises(NotAnAttrsClassError) as e:
729            fields_dict(object)
730
731        assert (
732            "{o!r} is not an attrs-decorated class.".format(o=object)
733        ) == e.value.args[0]
734
735    @given(simple_classes())
736    def test_fields_dict(self, C):
737        """
738        Returns an ordered dict of ``{attribute_name: Attribute}``.
739        """
740        d = fields_dict(C)
741
742        assert isinstance(d, ordered_dict)
743        assert list(fields(C)) == list(d.values())
744        assert [a.name for a in fields(C)] == [field_name for field_name in d]
745
746
747class TestConverter(object):
748    """
749    Tests for attribute conversion.
750    """
751    def test_convert(self):
752        """
753        Return value of converter is used as the attribute's value.
754        """
755        C = make_class("C", {
756            "x": attr.ib(converter=lambda v: v + 1),
757            "y": attr.ib(),
758        })
759        c = C(1, 2)
760
761        assert c.x == 2
762        assert c.y == 2
763
764    @given(integers(), booleans())
765    def test_convert_property(self, val, init):
766        """
767        Property tests for attributes with convert.
768        """
769        C = make_class("C", {
770            "y": attr.ib(),
771            "x": attr.ib(init=init, default=val, converter=lambda v: v + 1),
772        })
773        c = C(2)
774
775        assert c.x == val + 1
776        assert c.y == 2
777
778    @given(integers(), booleans())
779    def test_convert_factory_property(self, val, init):
780        """
781        Property tests for attributes with convert, and a factory default.
782        """
783        C = make_class("C", ordered_dict([
784            ("y", attr.ib()),
785            ("x", attr.ib(
786                init=init,
787                default=Factory(lambda: val),
788                converter=lambda v: v + 1
789            )),
790        ]))
791        c = C(2)
792
793        assert c.x == val + 1
794        assert c.y == 2
795
796    def test_factory_takes_self(self):
797        """
798        If takes_self on factories is True, self is passed.
799        """
800        C = make_class("C", {
801            "x": attr.ib(
802                default=Factory((lambda self: self), takes_self=True)
803            ),
804        })
805
806        i = C()
807
808        assert i is i.x
809
810    def test_factory_hashable(self):
811        """
812        Factory is hashable.
813        """
814        assert hash(Factory(None, False)) == hash(Factory(None, False))
815
816    def test_convert_before_validate(self):
817        """
818        Validation happens after conversion.
819        """
820        def validator(inst, attr, val):
821            raise RuntimeError("foo")
822        C = make_class(
823            "C", {
824                "x": attr.ib(validator=validator, converter=lambda v: 1 / 0),
825                "y": attr.ib(),
826            })
827        with pytest.raises(ZeroDivisionError):
828            C(1, 2)
829
830    def test_frozen(self):
831        """
832        Converters circumvent immutability.
833        """
834        C = make_class("C", {
835            "x": attr.ib(converter=lambda v: int(v)),
836        }, frozen=True)
837        C("1")
838
839    def test_deprecated_convert(self):
840        """
841        Using *convert* raises a DeprecationWarning and sets the converter
842        field.
843        """
844        def conv(v):
845            return v
846
847        with pytest.warns(DeprecationWarning) as wi:
848            @attr.s
849            class C(object):
850                x = attr.ib(convert=conv)
851
852            convert = fields(C).x.convert
853
854        assert 2 == len(wi.list)
855        w = wi.pop()
856
857        assert conv == fields(C).x.converter == convert
858        assert (
859            "The `convert` argument is deprecated in favor of `converter`.  "
860            "It will be removed after 2019/01.",
861        ) == w.message.args
862        assert __file__ == w.filename
863
864    def test_convert_converter(self):
865        """
866        A TypeError is raised if both *convert* and *converter* are passed.
867        """
868        with pytest.raises(RuntimeError) as ei:
869            @attr.s
870            class C(object):
871                x = attr.ib(convert=lambda v: v, converter=lambda v: v)
872
873        assert (
874            "Can't pass both `convert` and `converter`.  "
875            "Please use `converter` only.",
876        ) == ei.value.args
877
878
879class TestValidate(object):
880    """
881    Tests for `validate`.
882    """
883    def test_success(self):
884        """
885        If the validator succeeds, nothing gets raised.
886        """
887        C = make_class("C", {
888            "x": attr.ib(validator=lambda *a: None),
889            "y": attr.ib()
890        })
891        validate(C(1, 2))
892
893    def test_propagates(self):
894        """
895        The exception of the validator is handed through.
896        """
897        def raiser(_, __, value):
898            if value == 42:
899                raise FloatingPointError
900
901        C = make_class("C", {"x": attr.ib(validator=raiser)})
902        i = C(1)
903        i.x = 42
904
905        with pytest.raises(FloatingPointError):
906            validate(i)
907
908    def test_run_validators(self):
909        """
910        Setting `_run_validators` to False prevents validators from running.
911        """
912        _config._run_validators = False
913        obj = object()
914
915        def raiser(_, __, ___):
916            raise Exception(obj)
917
918        C = make_class("C", {"x": attr.ib(validator=raiser)})
919        c = C(1)
920        validate(c)
921        assert 1 == c.x
922        _config._run_validators = True
923
924        with pytest.raises(Exception):
925            validate(c)
926
927        with pytest.raises(Exception) as e:
928            C(1)
929        assert (obj,) == e.value.args
930
931    def test_multiple_validators(self):
932        """
933        If a list is passed as a validator, all of its items are treated as one
934        and must pass.
935        """
936        def v1(_, __, value):
937            if value == 23:
938                raise TypeError("omg")
939
940        def v2(_, __, value):
941            if value == 42:
942                raise ValueError("omg")
943
944        C = make_class("C", {"x": attr.ib(validator=[v1, v2])})
945
946        validate(C(1))
947
948        with pytest.raises(TypeError) as e:
949            C(23)
950
951        assert "omg" == e.value.args[0]
952
953        with pytest.raises(ValueError) as e:
954            C(42)
955
956        assert "omg" == e.value.args[0]
957
958    def test_multiple_empty(self):
959        """
960        Empty list/tuple for validator is the same as None.
961        """
962        C1 = make_class("C", {"x": attr.ib(validator=[])})
963        C2 = make_class("C", {"x": attr.ib(validator=None)})
964
965        assert inspect.getsource(C1.__init__) == inspect.getsource(C2.__init__)
966
967
968# Hypothesis seems to cache values, so the lists of attributes come out
969# unsorted.
970sorted_lists_of_attrs = list_of_attrs.map(
971    lambda l: sorted(l, key=attrgetter("counter")))
972
973
974class TestMetadata(object):
975    """
976    Tests for metadata handling.
977    """
978    @given(sorted_lists_of_attrs)
979    def test_metadata_present(self, list_of_attrs):
980        """
981        Assert dictionaries are copied and present.
982        """
983        C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
984
985        for hyp_attr, class_attr in zip(list_of_attrs, fields(C)):
986            if hyp_attr.metadata is None:
987                # The default is a singleton empty dict.
988                assert class_attr.metadata is not None
989                assert len(class_attr.metadata) == 0
990            else:
991                assert hyp_attr.metadata == class_attr.metadata
992
993                # Once more, just to assert getting items and iteration.
994                for k in class_attr.metadata:
995                    assert hyp_attr.metadata[k] == class_attr.metadata[k]
996                    assert (hyp_attr.metadata.get(k) ==
997                            class_attr.metadata.get(k))
998
999    @given(simple_classes(), text())
1000    def test_metadata_immutability(self, C, string):
1001        """
1002        The metadata dict should be best-effort immutable.
1003        """
1004        for a in fields(C):
1005            with pytest.raises(TypeError):
1006                a.metadata[string] = string
1007            with pytest.raises(AttributeError):
1008                a.metadata.update({string: string})
1009            with pytest.raises(AttributeError):
1010                a.metadata.clear()
1011            with pytest.raises(AttributeError):
1012                a.metadata.setdefault(string, string)
1013
1014            for k in a.metadata:
1015                # For some reason, Python 3's MappingProxyType throws an
1016                # IndexError for deletes on a large integer key.
1017                with pytest.raises((TypeError, IndexError)):
1018                    del a.metadata[k]
1019                with pytest.raises(AttributeError):
1020                    a.metadata.pop(k)
1021            with pytest.raises(AttributeError):
1022                    a.metadata.popitem()
1023
1024    @given(lists(simple_attrs_without_metadata, min_size=2, max_size=5))
1025    def test_empty_metadata_singleton(self, list_of_attrs):
1026        """
1027        All empty metadata attributes share the same empty metadata dict.
1028        """
1029        C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
1030        for a in fields(C)[1:]:
1031            assert a.metadata is fields(C)[0].metadata
1032
1033    @given(lists(simple_attrs_without_metadata, min_size=2, max_size=5))
1034    def test_empty_countingattr_metadata_independent(self, list_of_attrs):
1035        """
1036        All empty metadata attributes are independent before ``@attr.s``.
1037        """
1038        for x, y in itertools.combinations(list_of_attrs, 2):
1039            assert x.metadata is not y.metadata
1040
1041    @given(lists(simple_attrs_with_metadata(), min_size=2, max_size=5))
1042    def test_not_none_metadata(self, list_of_attrs):
1043        """
1044        Non-empty metadata attributes exist as fields after ``@attr.s``.
1045        """
1046        C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
1047
1048        assert len(fields(C)) > 0
1049
1050        for cls_a, raw_a in zip(fields(C), list_of_attrs):
1051            assert cls_a.metadata != {}
1052            assert cls_a.metadata == raw_a.metadata
1053
1054    def test_metadata(self):
1055        """
1056        If metadata that is not None is passed, it is used.
1057
1058        This is necessary for coverage because the previous test is
1059        hypothesis-based.
1060        """
1061        md = {}
1062        a = attr.ib(metadata=md)
1063
1064        assert md is a.metadata
1065
1066
1067class TestClassBuilder(object):
1068    """
1069    Tests for `_ClassBuilder`.
1070    """
1071    def test_repr_str(self):
1072        """
1073        Trying to add a `__str__` without having a `__repr__` raises a
1074        ValueError.
1075        """
1076        with pytest.raises(ValueError) as ei:
1077            make_class("C", {}, repr=False, str=True)
1078
1079        assert (
1080            "__str__ can only be generated if a __repr__ exists.",
1081        ) == ei.value.args
1082
1083    def test_repr(self):
1084        """
1085        repr of builder itself makes sense.
1086        """
1087        class C(object):
1088            pass
1089
1090        b = _ClassBuilder(C, None, True, True, False)
1091
1092        assert "<_ClassBuilder(cls=C)>" == repr(b)
1093
1094    def test_returns_self(self):
1095        """
1096        All methods return the builder for chaining.
1097        """
1098        class C(object):
1099            x = attr.ib()
1100
1101        b = _ClassBuilder(C, None, True, True, False)
1102
1103        cls = b.add_cmp().add_hash().add_init().add_repr("ns").add_str() \
1104            .build_class()
1105
1106        assert "ns.C(x=1)" == repr(cls(1))
1107
1108    @pytest.mark.parametrize("meth_name", [
1109        "__init__", "__hash__", "__repr__", "__str__",
1110        "__eq__", "__ne__", "__lt__", "__le__", "__gt__", "__ge__",
1111    ])
1112    def test_attaches_meta_dunders(self, meth_name):
1113        """
1114        Generated methods have correct __module__, __name__, and __qualname__
1115        attributes.
1116        """
1117        @attr.s(hash=True, str=True)
1118        class C(object):
1119            def organic(self):
1120                pass
1121
1122        meth = getattr(C, meth_name)
1123
1124        assert meth_name == meth.__name__
1125        assert C.organic.__module__ == meth.__module__
1126        if not PY2:
1127            organic_prefix = C.organic.__qualname__.rsplit(".", 1)[0]
1128            assert organic_prefix + "." + meth_name == meth.__qualname__
1129
1130    def test_handles_missing_meta_on_class(self):
1131        """
1132        If the class hasn't a __module__ or __qualname__, the method hasn't
1133        either.
1134        """
1135        class C(object):
1136            pass
1137
1138        b = _ClassBuilder(
1139            C, these=None, slots=False, frozen=False, auto_attribs=False,
1140        )
1141        b._cls = {}  # no __module__; no __qualname__
1142
1143        def fake_meth(self):
1144            pass
1145
1146        fake_meth.__module__ = "42"
1147        fake_meth.__qualname__ = "23"
1148
1149        rv = b._add_method_dunders(fake_meth)
1150
1151        assert "42" == rv.__module__ == fake_meth.__module__
1152        assert "23" == rv.__qualname__ == fake_meth.__qualname__
1153
1154    def test_weakref_setstate(self):
1155        """
1156        __weakref__ is not set on in setstate because it's not writable in
1157        slots classes.
1158        """
1159        @attr.s(slots=True)
1160        class C(object):
1161            __weakref__ = attr.ib(
1162                init=False, hash=False, repr=False, cmp=False
1163            )
1164
1165        assert C() == copy.deepcopy(C())
1166