InitC   A
last analyzed

Complexity

Total Complexity 2

Size/Duplication

Total Lines 5
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 0
loc 5
rs 10
wmc 2
1
from __future__ import print_function
2
3
import pickle
4
from functools import partial
5
6
from pytest import fixture
7
from pytest import raises
8
9
from fields import BareFields
10
from fields import ComparableMixin
11
from fields import ConvertibleFields
12
from fields import ConvertibleMixin
13
from fields import Fields
14
from fields import InheritableFields
15
from fields import PrintableMixin
16
from fields import SlotsFields
17
from fields import Tuple
18
from fields import make_init_func
19
from fields.extras import RegexValidate
20
from fields.extras import ValidationError
21
22
try:
23
    import cPickle
24
except ImportError:
25
    import pickle as cPickle
26
27
28
@fixture(params=[
29
    partial(pickle.dumps, protocol=i)
30
    for i in range(pickle.HIGHEST_PROTOCOL)
31
] + [
32
    partial(cPickle.dumps, protocol=i)
33
    for i in range(cPickle.HIGHEST_PROTOCOL)
34
])
35
def pickler(request):
36
    return request.param
37
38
39
@fixture(params=[pickle.loads, cPickle.loads])
40
def unpickler(request):
41
    return request.param
42
43
44
@fixture(params=[Fields, SlotsFields])
45
def impl(request):
46
    print(request.param)
47
    return request.param
48
49
50
class G1(Tuple.a.b):
51
    pass
52
53
54
class G2(Fields.a.b[1].c[2]):
55
    pass
56
57
58
class G3(Fields.a.b[1].c[2]):
59
    pass
60
61
62
def test_slots_class_has_slots():
63
    class Slots(SlotsFields.a.b[1]):
64
        pass
65
66
    i = Slots(0)
67
    i.a = 1
68
    assert Slots.__slots__ == ['a', 'b']
69
    assert i.a == 1
70
    assert not hasattr(i, "__dict__")
71
    raises(AttributeError, setattr, i, "bogus", 1)
72
    raises(AttributeError, getattr, i, "bogus")
73
74
75
def test_slots_class_customizable_slots():
76
    class Slots(SlotsFields.a.b[1]):
77
        __slots__ = ["a", "b", "foo", "bar"]
78
79
    i = Slots(0)
80
    i.a = 1
81
    assert Slots.__slots__ == ["a", "b", "foo", "bar"]
82
    assert i.a == 1
83
    assert not hasattr(i, "__dict__")
84
    i.foo = 2
85
    assert i.foo == 2
86
    i.bar = 3
87
    assert i.bar == 3
88
    raises(AttributeError, setattr, i, "bogus", 1)
89
    raises(AttributeError, getattr, i, "bogus")
90
91
92
def test_defaults_on_tuples(impl):
93
    class Ok(impl.a.b['def']):
94
        pass
95
    assert Ok(1).b == 'def'
96
    assert Ok(1, 2).b == 2
97
98
99
def test_required_after_defaults(impl):
100
    def test():
101
        class Fail(impl.a['def'].b):
102
            pass
103
104
    raises(TypeError, test)
105
106
107
def test_extra_args(impl):
108
    raises(TypeError, impl.a.b, 1, 2, 3)
109
110
111
def test_comparable():
112
    class C(ComparableMixin.a):
113
        pass
114
115
    raises(TypeError, C, 1, 2, 3)
116
117
    class D(BareFields.a.b.c, ComparableMixin.a):
118
        pass
119
120
    assert D(1, 2, 3) == D(1, 3, 4)
121
    assert D(1, 2, 3) != D(2, 2, 3)
122
123
124
def test_printable():
125
    class D(BareFields.a.b.c, PrintableMixin.a.b):
126
        pass
127
128
    assert str(D(1, 2, 3)) == "D(a=1, b=2)"
129
130
131
def test_extra_args_2(impl):
132
    class X1(impl.a.b):
133
        pass
134
135
    raises(TypeError, X1, 1, 2, 3)
136
137
138
def test_missing_args(impl):
139
    raises(TypeError, impl.a.b, 1)
140
141
142
def test_missing_args_2(impl):
143
    class X1(impl.a.b):
144
        pass
145
146
    raises(TypeError, X1, 1)
147
148
149
def test_already_specified(impl):
150
    raises(TypeError, lambda: impl.a[1].a)
151
152
153
def test_already_specified_2(impl):
154
    raises(TypeError, lambda: impl.a.a)
155
156
157
def test_already_specified_3(impl):
158
    raises(TypeError, lambda: impl.a.a[2])
159
160
161
def test_already_specified_4(impl):
162
    raises(TypeError, lambda: impl.a.b.a)
163
164
165
def test_no_field(impl):
166
    raises(TypeError, lambda: impl[123])
167
168
169
def test_tuple_pickle(pickler, unpickler):
170
    g = G1(1, 2)
171
    assert unpickler(pickler(g)) == g
172
173
174
def test_class_pickle(pickler, unpickler):
175
    g = G2(1, c=0)
176
    assert unpickler(pickler(g)) == g
177
178
179
def test_slots_class_pickle(pickler, unpickler):
180
    g = G3(1, c=0)
181
    assert unpickler(pickler(g)) == g
182
183
184
def test_tuple_factory():
185
    class Z1(Tuple.a.b):
186
        pass
187
188
    t = Z1(1, 2)
189
    assert repr(t) == "Z1(a=1, b=2)"
190
191
192
def test_nosubclass(impl):
193
    T1 = impl.a.b.c[1].d[2]
194
195
    t = T1(1, 2)
196
    assert repr(t) == "FieldsBase(a=1, b=2, c=1, d=2)"
197
198
199
def test_factory(impl):
200
    class T1(impl.a.b.c[1].d[2]):
201
        pass
202
203
    t = T1(1, 2)
204
    assert repr(t) == "T1(a=1, b=2, c=1, d=2)"
205
206
207
def test_factory_all_defaults(impl):
208
    class T2(impl.a[0].b[1].c[2].d[3]):
209
        pass
210
211
    t = T2()
212
    assert repr(t) == "T2(a=0, b=1, c=2, d=3)"
213
214
215
def test_factory_no_required_after_defaults(impl):
216
    raises(TypeError, getattr, impl.a[0].b, 'c')
217
218
219
def test_factory_no_required_after_defaults_2(impl):
220
    raises(TypeError, type, 'Broken', (impl.a[0].b,), {})
221
222
223
def test_factory_t3(impl):
224
    class T3(impl.a):
225
        pass
226
227
    t = T3(1)
228
    assert repr(t) == "T3(a=1)"
229
230
231
def test_factory_empty_raise(impl):
232
    raises(TypeError, type, "T5", (impl,), {})
233
234
235
@fixture
236
def CmpC(request, impl):
237
    class CmpC(impl.a.b):
238
        pass
239
240
    return CmpC
241
242
243
def test_equal(CmpC):
244
    """
245
    Equal objects are detected as equal.
246
    """
247
    assert CmpC(1, 2) == CmpC(1, 2)
248
    assert not (CmpC(1, 2) != CmpC(1, 2))
249
250
251
def test_unequal_same_class(CmpC):
252
    """
253
    Unequal objects of correct type are detected as unequal.
254
    """
255
    assert CmpC(1, 2) != CmpC(2, 1)
256
    assert not (CmpC(1, 2) == CmpC(2, 1))
257
258
259
def test_unequal_different_class(CmpC):
260
    """
261
    Unequal objects of differnt type are detected even if their attributes
262
    match.
263
    """
264
    class NotCmpC(object):
265
        a = 1
266
        b = 2
267
    assert CmpC(1, 2) != NotCmpC()
268
    assert not (CmpC(1, 2) == NotCmpC())
269
270
271
def test_lt(CmpC):
272
    """
273
    __lt__ compares objects as tuples of attribute values.
274
    """
275
    for a, b in [
276
        ((1, 2),  (2, 1)),
277
        ((1, 2),  (1, 3)),
278
        (("a", "b"), ("b", "a")),
279
    ]:
280
        assert CmpC(*a) < CmpC(*b)
281
282
283
def test_lt_unordable(CmpC):
284
    """
285
    __lt__ returns NotImplemented if classes differ.
286
    """
287
    assert NotImplemented == (CmpC(1, 2).__lt__(42))
288
289
290
def test_le(CmpC):
291
    """
292
    __le__ compares objects as tuples of attribute values.
293
    """
294
    for a, b in [
295
        ((1, 2),  (2, 1)),
296
        ((1, 2),  (1, 3)),
297
        ((1, 1),  (1, 1)),
298
        (("a", "b"), ("b", "a")),
299
        (("a", "b"), ("a", "b")),
300
    ]:
301
        assert CmpC(*a) <= CmpC(*b)
302
303
304
def test_le_unordable(CmpC):
305
    """
306
    __le__ returns NotImplemented if classes differ.
307
    """
308
    assert NotImplemented == (CmpC(1, 2).__le__(42))
309
310
311
def test_gt(CmpC):
312
    """
313
    __gt__ compares objects as tuples of attribute values.
314
    """
315
    for a, b in [
316
        ((2, 1), (1, 2)),
317
        ((1, 3), (1, 2)),
318
        (("b", "a"), ("a", "b")),
319
    ]:
320
        assert CmpC(*a) > CmpC(*b)
321
322
323
def test_gt_unordable(CmpC):
324
    """
325
    __gt__ returns NotImplemented if classes differ.
326
    """
327
    assert NotImplemented == (CmpC(1, 2).__gt__(42))
328
329
330
def test_ne_unordable(CmpC):
331
    """
332
    __gt__ returns NotImplemented if classes differ.
333
    """
334
    assert NotImplemented == (CmpC(1, 2).__ne__(42))
335
336
337
def test_ge(CmpC):
338
    """
339
    __ge__ compares objects as tuples of attribute values.
340
    """
341
    for a, b in [
342
        ((2, 1), (1, 2)),
343
        ((1, 3), (1, 2)),
344
        ((1, 1), (1, 1)),
345
        (("b", "a"), ("a", "b")),
346
        (("a", "b"), ("a", "b")),
347
    ]:
348
        assert CmpC(*a) >= CmpC(*b)
349
350
351
def test_ge_unordable(CmpC):
352
    """
353
    __ge__ returns NotImplemented if classes differ.
354
    """
355
    assert NotImplemented == (CmpC(1, 2).__ge__(42))
356
357
358
def test_hash(CmpC):
359
    """
360
    __hash__ returns different hashes for different values.
361
    """
362
    assert hash(CmpC(1, 2)) != hash(CmpC(1, 1))
363
364
365
@fixture
366
def InitC(request, impl):
367
    class InitC(impl.a.b):
368
        def __init__(self, *args, **kwargs):
369
            super(InitC, self).__init__(*args, **kwargs)
370
            if self.a == self.b:
371
                raise ValueError
372
373
    return InitC
374
375
376
def test_sets_attributes(InitC):
377
    """
378
    The attributes are initialized using the passed keywords.
379
    """
380
    obj = InitC(a=1, b=2)
381
    assert 1 == obj.a
382
    assert 2 == obj.b
383
384
385
def test_custom_init(InitC):
386
    """
387
    The class initializer is called too.
388
    """
389
    with raises(ValueError):
390
        InitC(a=1, b=1)
391
392
393
def test_passes_args(InitC):
394
    """
395
    All positional parameters are passed to the original initializer.
396
    """
397
    class InitWithArg(Fields.a):
398
        def __init__(self, arg, **kwargs):
399
            super(InitWithArg, self).__init__(**kwargs)
400
            self.arg = arg
401
402
    obj = InitWithArg(42, a=1)
403
    assert 42 == obj.arg
404
    assert 1 == obj.a
405
406
407
def test_missing_arg(InitC):
408
    """
409
    Raises `ValueError` if a value isn't passed.
410
    """
411
    with raises(TypeError):
412
        InitC(a=1)
413
414
415
def test_regex_validator():
416
    class Test(RegexValidate.value['aa+'], Fields.value):
417
        pass
418
419
    raises(ValidationError, Test, 'a')
420
    raises(ValidationError, Test, '')
421
    raises(ValidationError, Test, 'bb')
422
423
    t = Test('aa')
424
    assert t.value == 'aa'
425
426
427
def test_regex_validator_rev():
428
    class Test(Fields.value, RegexValidate.value['aa+']):
429
        pass
430
431
    raises(ValidationError, Test, 'a')
432
    raises(ValidationError, Test, '')
433
    raises(ValidationError, Test, 'bb')
434
435
    t = Test('aa')
436
    assert t.value == 'aa'
437
438
439
def test_regex_validator_incompatible_layout():
440
    def test():
441
        class Test(RegexValidate.value['aa+'].extra['x'], Fields.value.extar['2'].a[3].b[4].c[5].d[6]):
442
            pass
443
    raises(TypeError, test)
444
445
446
def test_regex_validator_incompatible_layout_2():
447
    def test():
448
        class Test(RegexValidate.value['aa+'], Fields.other):
449
            pass
450
    raises(TypeError, test)
451
452
453
def test_regex_validator_bad_declaration():
454
    def test():
455
        class Test(RegexValidate.a.b['aa+']):
456
            pass
457
    raises(TypeError, test)
458
459
460
def test_regex_validator_fail_validation():
461
    class Test(RegexValidate.a['aa+']):
462
        pass
463
    raises(ValidationError, Test, 'a')
464
465
466
def test_regex_validator_rev_incompatible_layout(impl):
467
    def test():
468
        class Test(impl.value, RegexValidate.balue['aa+']):
469
            pass
470
    raises(TypeError, test)
471
472
473
def test_init_default_args_as_positional_args(impl):
474
    class MyContainer(impl.a.b[2].c[3]):
475
        pass
476
477
    assert repr(MyContainer(0, 1, 2)) == 'MyContainer(a=0, b=1, c=2)'
478
479
480
def test_init_default_args_as_positional_misaligned(impl):
481
    class MyContainer(impl.a.b[2].c[3]):
482
        pass
483
484
    raises(TypeError, MyContainer, 0, 1, b=2)
485
486
487
def test_init_default_args_as_positional_partial(impl):
488
    class MyContainer(impl.a.b[2].c[3]):
489
        pass
490
491
    assert repr(MyContainer(0, 1, c=2)) == 'MyContainer(a=0, b=1, c=2)'
492
493
494
def test_init_unknown_kwargs(impl):
495
    class MyContainer(impl.a.b[2].c[3]):
496
        pass
497
498
    raises(TypeError, MyContainer, 0, 1, x=2)
499
500
501
def test_init_too_many_positional(impl):
502
    class MyContainer(impl.a.b[2].c[3]):
503
        pass
504
    raises(TypeError, MyContainer, 0, 1, 2, 3)
505
506
507
def test_convertible():
508
    class TestContainer(ConvertibleFields.a.b):
509
        pass
510
511
    assert TestContainer(1, 2).as_dict == dict(a=1, b=2)
512
513
514
def test_convertible_mixin():
515
    class TestContainer(BareFields.a.b.c, ConvertibleMixin.a.b):
516
        pass
517
518
    assert TestContainer(1, 2, 3).as_tuple == (1, 2)
519
520
521
def test_bad_make_init_func():
522
    exc = raises(ValueError, make_init_func, ['a', 'b', 'c'], {'b': 1})
523
    assert exc.value.args == ("Cannot have positional fields after fields with defaults."
524
                              " Field 'c' is missing a default value!",)
525
526
527
def test_multiple_inheritance():
528
    class A(InheritableFields.name):
529
        pass
530
531
    class B(InheritableFields.age):
532
        pass
533
534
    class Person(A, B):
535
        pass
536
537
    Person(name='hans', age='43')
538
    raises(TypeError, Person, name='hans', age='43', bogus='crappo')
539