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

1# Authors: Sylvain Marie <sylvain.marie@se.com> 

2# 

3# Copyright (c) Schneider Electric Industries, 2019. All right reserved. 

4import sys 

5 

6import pytest 

7 

8from pyfields import field, init_fields, inject_fields, make_init, MandatoryFieldInitError, copy_field, get_fields 

9from pyfields.core import PY36 

10 

11 

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 

33 

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 

45 

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) 

53 

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() 

65 

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 !") 

74 

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 !") 

83 

84 __init__ = make_init(post_init_fun=post_init) 

85 else: 

86 raise ValueError(init_type) 

87 

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} 

94 

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} 

101 

102 # type hints 

103 height_field = Wall.__dict__['height'] 

104 color_field = Wall.__dict__['color'] 

105 

106 if py36_style_type_hints: 

107 assert height_field.type_hint is int 

108 assert color_field.type_hint is str 

109 

110 # todo check signature of generated constructor 

111 

112 

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 

117 

118 def post_init(self, foo='bar'): 

119 print(self.height) 

120 print("post init man !") 

121 

122 __init__ = make_init(height, post_init_fun=post_init) 

123 

124 # 

125 # assert str(signature(Wall.__init__)) == "(self, height, foo='bar')" 

126 

127 # first init 

128 w = Wall(12) 

129 assert vars(w) == {'height': 12} # color not initialized yet 

130 

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 

134 

135 

136def test_init_order(): 

137 """Tests that order of initialization is the same than order of definition in the class""" 

138 

139 class C(object): 

140 y = field() 

141 x = field(default_factory=copy_field(y)) 

142 

143 @init_fields 

144 def __init__(self): 

145 pass 

146 

147 c = C(y=1) 

148 print(vars(c)) 

149 

150 

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) 

157 

158 class B(object): 

159 b = field() 

160 

161 class C(B, A): 

162 a = field(default=None) 

163 c = field(default_factory=copy_field('b')) 

164 

165 @init_fields(ancestor_fields_first=a_first) 

166 def __init__(self): 

167 pass 

168 

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'] 

175 

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} 

179 

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 } 

193 

194 

195def test_init_inheritance(): 

196 """Makes sure that the init method can be generated in inheritance case """ 

197 

198 class A(object): 

199 a = field(default='hello') 

200 

201 class B(A): 

202 b = field(default='world') 

203 

204 def a(self): 

205 """ purposedly override the base class field name """ 

206 pass 

207 

208 __init__ = make_init() 

209 

210 # make sure that the 'a' field is ok 

211 b = B(a='h', b='w') 

212 assert b.a, b.b == ('h', 'w')