Coverage for pyfields/tests/test_autofields.py: 95%

119 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 autofields, field, FieldTypeError, Field, get_fields, autoclass 

9from pyfields.core import NativeField 

10 

11 

12@pytest.mark.parametrize("with_type_hints,type_check", [(False, False), (True, False), (True, True)]) 

13def test_autofields_basic(with_type_hints, type_check): 

14 """tests that basic functionality of @autofields is ok """ 

15 

16 if with_type_hints: 

17 if sys.version_info < (3, 6): 17 ↛ 18line 17 didn't jump to line 18, because the condition on line 17 was never true

18 pytest.skip("Type annotations are not supported in python < 3.6") 

19 

20 from ._test_py36 import _test_autofields 

21 Foo = _test_autofields(type_check) 

22 

23 # test it 

24 assert isinstance(Foo.__dict__['barcls'], NativeField) 

25 assert isinstance(Foo.__dict__['barfunc'], Field) 

26 assert not isinstance(Foo.__dict__['fct'], Field) 

27 assert not isinstance(Foo.__dict__['cls'], Field) 

28 

29 f = Foo(foo=1, barbar='yo', barfunc=lambda x: 2, barcls=str) 

30 if type_check: 

31 with pytest.raises(FieldTypeError): 

32 f.foo = 'ha' 

33 else: 

34 f.foo = 'ha' 

35 

36 assert f.bar == 0 

37 assert f.fct() == 1 

38 assert f.barfunc(1) == 2 

39 assert f.barcls == str 

40 

41 else: 

42 # retrocompatbility mode for python < 3.6 

43 # note: we also use this opportunity to test with parenthesis 

44 @autofields(check_types=type_check) 

45 class Foo(object): 

46 CONSTANT = 's' 

47 __a__ = 0 

48 

49 foo = field() 

50 bar = 0 # type: int 

51 barcls = float 

52 barfunc = lambda x: x 52 ↛ exitline 52 didn't run the lambda on line 52

53 barbar = 0 # type: str 

54 

55 class cls: 

56 pass 

57 

58 def fct(self): 

59 return 1 

60 

61 # test it 

62 assert isinstance(Foo.__dict__['barcls'], Field) 

63 assert isinstance(Foo.__dict__['barfunc'], Field) 

64 assert not isinstance(Foo.__dict__['fct'], Field) 

65 assert not isinstance(Foo.__dict__['cls'], Field) 

66 

67 f = Foo(foo=1, barfunc=lambda x: 2, barcls=str) 

68 assert f.bar == 0 

69 assert f.fct() == 1 

70 assert f.barfunc(1) == 2 

71 assert f.barcls == str 

72 

73 

74def test_autofields_property_descriptors(): 

75 """Checks that properties and descriptors are correctly ignored by autofields""" 

76 

77 @autofields 

78 class Foo(object): 

79 foo = 1 

80 @property 

81 def bar(self): 

82 return 2 

83 

84 class MyDesc(): 

85 def __get__(self): 

86 return 1 

87 

88 class MyDesc2(): 

89 def __get__(self): 

90 return 0 

91 def __set__(self, instance, value): 

92 return 

93 

94 m = MyDesc() 

95 p = MyDesc2() 

96 

97 fields = get_fields(Foo) 

98 assert len(fields) == 1 

99 assert fields[0].name == 'foo' 

100 

101 

102@pytest.mark.skipif(sys.version_info < (3, 6), reason="Annotations not supported in python < 3.6") 

103def test_issue_74(): 

104 """test associated with the non-issue 74""" 

105 from ._test_py36 import test_issue_74 

106 City = test_issue_74() 

107 c = City(name=None) 

108 assert c.name is None 

109 assert c.buildings == [] 

110 

111 

112@pytest.mark.skipif(sys.version_info < (3, 6), reason="Annotations not supported in python < 3.6") 

113def test_issue_76(): 

114 """ order issue 76 and 77 are fixed """ 

115 from ._test_py36 import test_issue_76 

116 Foo = test_issue_76() 

117 assert [f.name for f in get_fields(Foo)] == ['c', 'b', 'a'] 

118 

119 

120def test_issue_76_bis(): 

121 """ another order issue with @autofields """ 

122 

123 @autofields 

124 class Foo(object): 

125 msg = field(type_hint=str) 

126 age = field(default=12, type_hint=int) 

127 

128 assert [f.name for f in get_fields(Foo)] == ['msg', 'age'] 

129 

130 

131def test_autoclass(): 

132 """""" 

133 

134 @autoclass 

135 class Foo(object): 

136 msg = field(type_hint=str) 

137 age = field(default=12, type_hint=int) 

138 

139 f = Foo('hey') 

140 

141 # str repr 

142 assert repr(f) == "Foo(msg='hey', age=12)" 

143 assert str(f) == repr(f) 

144 

145 # dict and eq 

146 assert f.to_dict() == {'msg': 'hey', 'age': 12} 

147 

148 same_dict = {'msg': 'hey', 'age': 12} 

149 assert f == same_dict 

150 assert f == Foo.from_dict(same_dict) 

151 

152 diff_dict = {'age': 13, 'msg': 'hey'} 

153 assert f != diff_dict 

154 assert f != Foo.from_dict(diff_dict) 

155 

156 assert f == Foo.from_dict(f.to_dict()) 

157 

158 # hash 

159 my_set = {f, f} 

160 assert my_set == {f} 

161 assert Foo('hey') in my_set 

162 my_set.remove(Foo('hey')) 

163 assert len(my_set) == 0 

164 

165 # subclass A 

166 class Bar(Foo): 

167 pass 

168 

169 b = Bar(msg='hey') 

170 assert str(b) == "Bar(msg='hey', age=12)" 

171 assert b == f 

172 assert f == b 

173 

174 # hash 

175 my_set = {f, b} 

176 assert len(my_set) == 1 # yes: since the subclass does not define additional attributes. 

177 assert my_set == {f} 

178 

179 # subclass B 

180 @autoclass 

181 class Bar2(Foo): 

182 ho = 3 

183 

184 b2 = Bar2('hey') 

185 assert str(b2) == "Bar2(msg='hey', age=12, ho=3)" 

186 assert b2 != f 

187 assert f != b2 

188 

189 # hash 

190 my_set = {b2, b} 

191 assert Bar2('hey') in my_set