Coverage for pyfields/tests/test_init.py: 96%
124 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-11-06 16:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-11-06 16:35 +0000
1# Authors: Sylvain Marie <sylvain.marie@se.com>
2#
3# Copyright (c) Schneider Electric Industries, 2019. All right reserved.
4import sys
6import pytest
8from pyfields import field, init_fields, inject_fields, make_init, MandatoryFieldInitError, copy_field, get_fields
9from pyfields.core import PY36
12@pytest.mark.parametrize("native", [False, True], ids="native={}".format)
13@pytest.mark.parametrize("init_type", ['inject_fields', 'make_init', 'make_init_with_postinit'],
14 ids="init_type={}".format)
15@pytest.mark.parametrize("explicit_fields_list", [False, True], ids="explicit_list={}".format)
16@pytest.mark.parametrize("py36_style_type_hints", [False, True], ids="py36_style_type_hints={}".format)
17def test_init_all_methods(py36_style_type_hints, explicit_fields_list, init_type, native):
18 """Test of @inject_fields with selected fields """
19 if py36_style_type_hints:
20 if sys.version_info < (3, 6): 20 ↛ 21line 20 didn't jump to line 21, because the condition on line 20 was never true
21 pytest.skip()
22 Wall = None
23 else:
24 # import the test that uses python 3.6 type annotations
25 from ._test_py36 import _test_readme_constructor
26 Wall = _test_readme_constructor(explicit_fields_list, init_type, native)
27 else:
28 if init_type == 'inject_fields':
29 if explicit_fields_list:
30 class Wall(object):
31 height = field(doc="Height of the wall in mm.", native=native) # type: int
32 color = field(default='white', doc="Color of the wall.", native=native) # type: str
34 @inject_fields(height, color)
35 def __init__(self, fields):
36 with pytest.raises(MandatoryFieldInitError):
37 print(self.height)
38 # initialize all fields received
39 fields.init(self)
40 print(self.height)
41 else:
42 class Wall(object):
43 height = field(doc="Height of the wall in mm.", native=native) # type: int
44 color = field(default='white', doc="Color of the wall.", native=native) # type: str
46 @inject_fields
47 def __init__(self, fields):
48 with pytest.raises(MandatoryFieldInitError):
49 print(self.height)
50 # initialize all fields received
51 fields.init(self)
52 print(self.height)
54 elif init_type == 'make_init':
55 if explicit_fields_list:
56 class Wall(object):
57 height = field(doc="Height of the wall in mm.", native=native) # type: int
58 color = field(default='white', doc="Color of the wall.", native=native) # type: str
59 __init__ = make_init(height, color)
60 else:
61 class Wall(object):
62 height = field(doc="Height of the wall in mm.", native=native) # type: int
63 color = field(default='white', doc="Color of the wall.", native=native) # type: str
64 __init__ = make_init()
66 elif init_type == 'make_init_with_postinit': 66 ↛ 86line 66 didn't jump to line 86, because the condition on line 66 was never false
67 if explicit_fields_list:
68 class Wall(object):
69 height = field(doc="Height of the wall in mm.", native=native) # type: int
70 color = field(default='white', doc="Color of the wall.", native=native) # type: str
71 def post_init(self, foo='bar'):
72 self.height
73 print("post init man !")
75 __init__ = make_init(height, color, post_init_fun=post_init)
76 else:
77 class Wall(object):
78 height = field(doc="Height of the wall in mm.", native=native) # type: int
79 color = field(default='white', doc="Color of the wall.", native=native) # type: str
80 def post_init(self, foo='bar'):
81 self.height
82 print("post init man !")
84 __init__ = make_init(post_init_fun=post_init)
85 else:
86 raise ValueError(init_type)
88 # first init
89 w = Wall(height=12)
90 if native:
91 assert vars(w) == {'color': 'white', 'height': 12}
92 else:
93 assert vars(w) == {'_color': 'white', '_height': 12}
95 # make sure this can be done a second time (since we replaced the __init__ method now)
96 w = Wall(color='blue', height=1)
97 if native:
98 assert vars(w) == {'color': 'blue', 'height': 1}
99 else:
100 assert vars(w) == {'_color': 'blue', '_height': 1}
102 # type hints
103 height_field = Wall.__dict__['height']
104 color_field = Wall.__dict__['color']
106 if py36_style_type_hints:
107 assert height_field.type_hint is int
108 assert color_field.type_hint is str
110 # todo check signature of generated constructor
113def test_init_partial_fields():
114 class Wall(object):
115 height = field(doc="Height of the wall in mm.") # type: int
116 color = field(default='white', doc="Color of the wall.") # type: str
118 def post_init(self, foo='bar'):
119 print(self.height)
120 print("post init man !")
122 __init__ = make_init(height, post_init_fun=post_init)
124 #
125 # assert str(signature(Wall.__init__)) == "(self, height, foo='bar')"
127 # first init
128 w = Wall(12)
129 assert vars(w) == {'height': 12} # color not initialized yet
131 # make sure this can be done a second time (since we replaced the __init__ method now)
132 w = Wall(foo='blue', height=1)
133 assert vars(w) == {'height': 1} # color not initialized yet
136def test_init_order():
137 """Tests that order of initialization is the same than order of definition in the class"""
139 class C(object):
140 y = field()
141 x = field(default_factory=copy_field(y))
143 @init_fields
144 def __init__(self):
145 pass
147 c = C(y=1)
148 print(vars(c))
151@pytest.mark.parametrize("a_first", [False, True], ids="ancestor_first={}".format)
152def test_init_order2(a_first):
153 """"""
154 class A(object):
155 a = field()
156 d = field(default=5)
158 class B(object):
159 b = field()
161 class C(B, A):
162 a = field(default=None)
163 c = field(default_factory=copy_field('b'))
165 @init_fields(ancestor_fields_first=a_first)
166 def __init__(self):
167 pass
169 fields = get_fields(C, include_inherited=True, ancestors_first=a_first, _auto_fix_fields=not PY36)
170 field_names = [f.name for f in fields]
171 if a_first:
172 assert field_names == ['a', 'd', 'b', 'c']
173 else:
174 assert field_names == ['a', 'c', 'b', 'd']
176 # make sure that a and c have default values and therefore just passing b is ok.
177 c = C(1)
178 assert vars(c) == {'b': 1, 'c': 1, 'a': None, 'd': 5}
180 c = C(1, 2, 3)
181 if a_first is None or a_first:
182 assert vars(c) == {'b': 1, # 1st arg
183 'c': 1, # default: copy of b
184 'a': 2, # 2d arg
185 'd': 3 # 3d arg
186 }
187 else:
188 assert vars(c) == {'b': 1, # 1st arg
189 'c': 3, # 3d arg
190 'a': 2, # 2d arg
191 'd': 5 # default: 5
192 }
195def test_init_inheritance():
196 """Makes sure that the init method can be generated in inheritance case """
198 class A(object):
199 a = field(default='hello')
201 class B(A):
202 b = field(default='world')
204 def a(self):
205 """ purposedly override the base class field name """
206 pass
208 __init__ = make_init()
210 # make sure that the 'a' field is ok
211 b = B(a='h', b='w')
212 assert b.a, b.b == ('h', 'w')