Completed
Push — master ( dc4131...1e3562 )
by Ionel Cristian
58s
created

tests.test_multiple_inheritance()   A

Complexity

Conditions 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 12
cc 1
rs 9.4285
1
from __future__ import print_function
2
3
import pickle
4
try:
5
    import cPickle
6
except ImportError:
7
    import pickle as cPickle
8
from functools import partial
9
from itertools import chain
10
11
from pytest import fixture
12
from pytest import raises
13
14
from fields import BareFields, make_init_func, InheritableFields
15
from fields import ComparableMixin
16
from fields import ConvertibleFields
17
from fields import ConvertibleMixin
18
from fields import Fields
19
from fields import PrintableMixin
20
from fields import SlotsFields
21
from fields import Tuple
22
from fields.extras import RegexValidate
23
from fields.extras import ValidationError
24
25
26
@fixture(params=[
27
    partial(pickle.dumps, protocol=i)
28
    for i in range(pickle.HIGHEST_PROTOCOL)
29
] + [
30
    partial(cPickle.dumps, protocol=i)
31
    for i in range(cPickle.HIGHEST_PROTOCOL)
32
])
33
def pickler(request):
34
    return request.param
35
36
37
@fixture(params=[pickle.loads, cPickle.loads])
38
def unpickler(request):
39
    return request.param
40
41
42
@fixture(params=[Fields, SlotsFields])
43
def impl(request):
44
    print(request.param)
45
    return request.param
46
47
48
class G1(Tuple.a.b):
49
    pass
50
51
52
class G2(Fields.a.b[1].c[2]):
53
    pass
54
55
56
class G3(Fields.a.b[1].c[2]):
57
    pass
58
59
60
def test_slots_class_has_slots():
61
    class Slots(SlotsFields.a.b[1]):
62
        pass
63
64
    i = Slots(0)
65
    i.a = 1
66
    assert Slots.__slots__ == ['a', 'b']
67
    assert i.a == 1
68
    assert not hasattr(i, "__dict__")
69
    raises(AttributeError, setattr, i, "bogus", 1)
70
    raises(AttributeError, getattr, i, "bogus")
71
72
73
def test_slots_class_customizable_slots():
74
    class Slots(SlotsFields.a.b[1]):
75
        __slots__ = ["a", "b", "foo", "bar"]
76
77
    i = Slots(0)
78
    i.a = 1
79
    assert Slots.__slots__ == ["a", "b", "foo", "bar"]
80
    assert i.a == 1
81
    assert not hasattr(i, "__dict__")
82
    i.foo = 2
83
    assert i.foo == 2
84
    i.bar = 3
85
    assert i.bar == 3
86
    raises(AttributeError, setattr, i, "bogus", 1)
87
    raises(AttributeError, getattr, i, "bogus")
88
89
90
def test_defaults_on_tuples(impl):
91
    class Ok(impl.a.b['def']):
92
        pass
93
    assert Ok(1).b == 'def'
94
    assert Ok(1, 2).b == 2
95
96
97
def test_required_after_defaults(impl):
98
    def test():
99
        class Fail(impl.a['def'].b):
100
            pass
101
102
    raises(TypeError, test)
103
104
105
def test_extra_args(impl):
106
    raises(TypeError, impl.a.b, 1, 2, 3)
107
108
109
def test_comparable():
110
    class C(ComparableMixin.a):
111
        pass
112
113
    raises(TypeError, C, 1, 2, 3)
114
115
    class D(BareFields.a.b.c, ComparableMixin.a):
116
        pass
117
118
    assert D(1, 2, 3) == D(1, 3, 4)
119
    assert D(1, 2, 3) != D(2, 2, 3)
120
121
122
def test_printable():
123
    class D(BareFields.a.b.c, PrintableMixin.a.b):
124
        pass
125
126
    assert str(D(1, 2, 3)) == "D(a=1, b=2)"
127
128
129
def test_extra_args_2(impl):
130
    class X1(impl.a.b):
131
        pass
132
133
    raises(TypeError, X1, 1, 2, 3)
134
135
136
def test_missing_args(impl):
137
    raises(TypeError, impl.a.b, 1)
138
139
140
def test_missing_args_2(impl):
141
    class X1(impl.a.b):
142
        pass
143
144
    raises(TypeError, X1, 1)
145
146
147
def test_already_specified(impl):
148
    raises(TypeError, lambda: impl.a[1].a)
149
150
151
def test_already_specified_2(impl):
152
    raises(TypeError, lambda: impl.a.a)
153
154
155
def test_already_specified_3(impl):
156
    raises(TypeError, lambda: impl.a.a[2])
157
158
159
def test_already_specified_4(impl):
160
    raises(TypeError, lambda: impl.a.b.a)
161
162
163
def test_no_field(impl):
164
    raises(TypeError, lambda: impl[123])
165
166
167
def test_tuple_pickle(pickler, unpickler):
168
    g = G1(1, 2)
169
    assert unpickler(pickler(g)) == g
170
171
172
def test_class_pickle(pickler, unpickler):
173
    g = G2(1, c=0)
174
    assert unpickler(pickler(g)) == g
175
176
177
def test_slots_class_pickle(pickler, unpickler):
178
    g = G3(1, c=0)
179
    assert unpickler(pickler(g)) == g
180
181
182
def test_tuple_factory():
183
    class Z1(Tuple.a.b):
184
        pass
185
186
    t = Z1(1, 2)
187
    assert repr(t) == "Z1(a=1, b=2)"
188
189
190
def test_nosubclass(impl):
191
    T1 = impl.a.b.c[1].d[2]
192
193
    t = T1(1, 2)
194
    assert repr(t) == "FieldsBase(a=1, b=2, c=1, d=2)"
195
196
197
def test_factory(impl):
198
    class T1(impl.a.b.c[1].d[2]):
199
        pass
200
201
    t = T1(1, 2)
202
    assert repr(t) == "T1(a=1, b=2, c=1, d=2)"
203
204
205
def test_factory_all_defaults(impl):
206
    class T2(impl.a[0].b[1].c[2].d[3]):
207
        pass
208
209
    t = T2()
210
    assert repr(t) == "T2(a=0, b=1, c=2, d=3)"
211
212
213
def test_factory_no_required_after_defaults(impl):
214
    raises(TypeError, getattr, impl.a[0].b, 'c')
215
216
217
def test_factory_no_required_after_defaults_2(impl):
218
    raises(TypeError, type, 'Broken', (impl.a[0].b,), {})
219
220
221
def test_factory_t3(impl):
222
    class T3(impl.a):
223
        pass
224
225
    t = T3(1)
226
    assert repr(t) == "T3(a=1)"
227
228
229
def test_factory_empty_raise(impl):
230
    raises(TypeError, type, "T5", (impl,), {})
231
232
233
@fixture
234
def CmpC(request, impl):
235
    class CmpC(impl.a.b):
236
        pass
237
238
    return CmpC
239
240
241
def test_equal(CmpC):
242
    """
243
    Equal objects are detected as equal.
244
    """
245
    assert CmpC(1, 2) == CmpC(1, 2)
246
    assert not (CmpC(1, 2) != CmpC(1, 2))
247
248
249
def test_unequal_same_class(CmpC):
250
    """
251
    Unequal objects of correct type are detected as unequal.
252
    """
253
    assert CmpC(1, 2) != CmpC(2, 1)
254
    assert not (CmpC(1, 2) == CmpC(2, 1))
255
256
257
def test_unequal_different_class(CmpC):
258
    """
259
    Unequal objects of differnt type are detected even if their attributes
260
    match.
261
    """
262
    class NotCmpC(object):
263
        a = 1
264
        b = 2
265
    assert CmpC(1, 2) != NotCmpC()
266
    assert not (CmpC(1, 2) == NotCmpC())
267
268
269
def test_lt(CmpC):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
    """
271
    __lt__ compares objects as tuples of attribute values.
272
    """
273
    for a, b in [
274
        ((1, 2),  (2, 1)),
275
        ((1, 2),  (1, 3)),
276
        (("a", "b"), ("b", "a")),
277
    ]:
278
        assert CmpC(*a) < CmpC(*b)
279
280
281
def test_lt_unordable(CmpC):
282
    """
283
    __lt__ returns NotImplemented if classes differ.
284
    """
285
    assert NotImplemented == (CmpC(1, 2).__lt__(42))
286
287
288
def test_le(CmpC):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
    """
290
    __le__ compares objects as tuples of attribute values.
291
    """
292
    for a, b in [
293
        ((1, 2),  (2, 1)),
294
        ((1, 2),  (1, 3)),
295
        ((1, 1),  (1, 1)),
296
        (("a", "b"), ("b", "a")),
297
        (("a", "b"), ("a", "b")),
298
    ]:
299
        assert CmpC(*a) <= CmpC(*b)
300
301
302
def test_le_unordable(CmpC):
303
    """
304
    __le__ returns NotImplemented if classes differ.
305
    """
306
    assert NotImplemented == (CmpC(1, 2).__le__(42))
307
308
309
def test_gt(CmpC):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
310
    """
311
    __gt__ compares objects as tuples of attribute values.
312
    """
313
    for a, b in [
314
        ((2, 1), (1, 2)),
315
        ((1, 3), (1, 2)),
316
        (("b", "a"), ("a", "b")),
317
    ]:
318
        assert CmpC(*a) > CmpC(*b)
319
320
321
def test_gt_unordable(CmpC):
322
    """
323
    __gt__ returns NotImplemented if classes differ.
324
    """
325
    assert NotImplemented == (CmpC(1, 2).__gt__(42))
326
327
328
def test_ne_unordable(CmpC):
329
    """
330
    __gt__ returns NotImplemented if classes differ.
331
    """
332
    assert NotImplemented == (CmpC(1, 2).__ne__(42))
333
334
335
def test_ge(CmpC):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
    """
337
    __ge__ compares objects as tuples of attribute values.
338
    """
339
    for a, b in [
340
        ((2, 1), (1, 2)),
341
        ((1, 3), (1, 2)),
342
        ((1, 1), (1, 1)),
343
        (("b", "a"), ("a", "b")),
344
        (("a", "b"), ("a", "b")),
345
    ]:
346
        assert CmpC(*a) >= CmpC(*b)
347
348
349
def test_ge_unordable(CmpC):
350
    """
351
    __ge__ returns NotImplemented if classes differ.
352
    """
353
    assert NotImplemented == (CmpC(1, 2).__ge__(42))
354
355
356
def test_hash(CmpC):
357
    """
358
    __hash__ returns different hashes for different values.
359
    """
360
    assert hash(CmpC(1, 2)) != hash(CmpC(1, 1))
361
362
363
@fixture
364
def InitC(request, impl):
365
    class InitC(impl.a.b):
366
        def __init__(self, *args, **kwargs):
367
            super(InitC, self).__init__(*args, **kwargs)
368
            if self.a == self.b:
369
                raise ValueError
370
371
    return InitC
372
373
374
def test_sets_attributes(InitC):
375
    """
376
    The attributes are initialized using the passed keywords.
377
    """
378
    obj = InitC(a=1, b=2)
379
    assert 1 == obj.a
380
    assert 2 == obj.b
381
382
383
def test_custom_init(InitC):
384
    """
385
    The class initializer is called too.
386
    """
387
    with raises(ValueError):
388
        InitC(a=1, b=1)
389
390
391
def test_passes_args(InitC):
392
    """
393
    All positional parameters are passed to the original initializer.
394
    """
395
    class InitWithArg(Fields.a):
396
        def __init__(self, arg, **kwargs):
397
            super(InitWithArg, self).__init__(**kwargs)
398
            self.arg = arg
399
400
    obj = InitWithArg(42, a=1)
401
    assert 42 == obj.arg
402
    assert 1 == obj.a
403
404
405
def test_missing_arg(InitC):
406
    """
407
    Raises `ValueError` if a value isn't passed.
408
    """
409
    with raises(TypeError):
410
        InitC(a=1)
411
412
413
def test_regex_validator():
414
    class Test(RegexValidate.value['aa+'], Fields.value):
415
        pass
416
417
    raises(ValidationError, Test, 'a')
418
    raises(ValidationError, Test, '')
419
    raises(ValidationError, Test, 'bb')
420
421
    t = Test('aa')
422
    assert t.value == 'aa'
423
424
425
def test_regex_validator_rev():
426
    class Test(Fields.value, RegexValidate.value['aa+']):
427
        pass
428
429
    raises(ValidationError, Test, 'a')
430
    raises(ValidationError, Test, '')
431
    raises(ValidationError, Test, 'bb')
432
433
    t = Test('aa')
434
    assert t.value == 'aa'
435
436
437
def test_regex_validator_incompatible_layout():
438
    def test():
439
        class Test(RegexValidate.value['aa+'].extra['x'], Fields.value.extar['2'].a[3].b[4].c[5].d[6]):
440
            pass
441
    raises(TypeError, test)
442
443
444
def test_regex_validator_incompatible_layout_2():
445
    def test():
446
        class Test(RegexValidate.value['aa+'], Fields.other):
447
            pass
448
    raises(TypeError, test)
449
450
451
def test_regex_validator_bad_declaration():
452
    def test():
453
        class Test(RegexValidate.a.b['aa+']):
454
            pass
455
    raises(TypeError, test)
456
457
458
def test_regex_validator_fail_validation():
459
    class Test(RegexValidate.a['aa+']):
460
        pass
461
    raises(ValidationError, Test, 'a')
462
463
464
def test_regex_validator_rev_incompatible_layout(impl):
465
    def test():
466
        class Test(impl.value, RegexValidate.balue['aa+']):
467
            pass
468
    raises(TypeError, test)
469
470
471
def test_init_default_args_as_positional_args(impl):
472
    class MyContainer(impl.a.b[2].c[3]):
473
        pass
474
475
    assert repr(MyContainer(0, 1, 2)) == 'MyContainer(a=0, b=1, c=2)'
476
477
478
def test_init_default_args_as_positional_misaligned(impl):
479
    class MyContainer(impl.a.b[2].c[3]):
480
        pass
481
482
    raises(TypeError, MyContainer, 0, 1, b=2)
483
484
485
def test_init_default_args_as_positional_partial(impl):
486
    class MyContainer(impl.a.b[2].c[3]):
487
        pass
488
489
    assert repr(MyContainer(0, 1, c=2)) == 'MyContainer(a=0, b=1, c=2)'
490
491
492
def test_init_unknown_kwargs(impl):
493
    class MyContainer(impl.a.b[2].c[3]):
494
        pass
495
496
    raises(TypeError, MyContainer, 0, 1, x=2)
497
498
499
def test_init_too_many_positional(impl):
500
    class MyContainer(impl.a.b[2].c[3]):
501
        pass
502
    raises(TypeError, MyContainer, 0, 1, 2, 3)
503
504
505
def test_convertible():
506
    class TestContainer(ConvertibleFields.a.b):
507
        pass
508
509
    assert TestContainer(1, 2).as_dict == dict(a=1, b=2)
510
511
512
def test_convertible_mixin():
513
    class TestContainer(BareFields.a.b.c, ConvertibleMixin.a.b):
514
        pass
515
516
    assert TestContainer(1, 2, 3).as_tuple == (1, 2)
517
518
519
def test_bad_make_init_func():
520
    exc = raises(ValueError, make_init_func, ['a', 'b', 'c'], {'b': 1})
521
    assert exc.value.args == ("Cannot have positional fields after fields with defaults."
522
                              " Field 'c' is missing a default value!",)
523
524
525
def test_multiple_inheritance():
526
    class A(InheritableFields.name):
527
        pass
528
529
    class B(InheritableFields.age):
530
        pass
531
532
    class Person(A, B):
533
        pass
534
535
    Person(name='hans', age='43')
536
    raises(TypeError, Person, name='hans', age='43', bogus='crappo')
537