Coverage for tests/test_000_frigid/test_400_modules.py: 100%

147 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-24 04:09 +0000

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

4#============================================================================# 

5# # 

6# Licensed under the Apache License, Version 2.0 (the "License"); # 

7# you may not use this file except in compliance with the License. # 

8# You may obtain a copy of the License at # 

9# # 

10# http://www.apache.org/licenses/LICENSE-2.0 # 

11# # 

12# Unless required by applicable law or agreed to in writing, software # 

13# distributed under the License is distributed on an "AS IS" BASIS, # 

14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

15# See the License for the specific language governing permissions and # 

16# limitations under the License. # 

17# # 

18#============================================================================# 

19 

20 

21''' Assert correct function of modules. ''' 

22 

23# mypy: ignore-errors 

24# pylint: disable=attribute-defined-outside-init 

25# pylint: disable=invalid-name,magic-value-comparison,no-member 

26# pylint: disable=protected-access 

27 

28 

29import pytest 

30 

31from itertools import product 

32 

33from . import ( 

34 MODULES_QNAMES, 

35 PACKAGE_NAME, 

36 cache_import_module, 

37) 

38 

39 

40THESE_MODULE_QNAMES = tuple( 

41 name for name in MODULES_QNAMES if name.endswith( '.modules' ) ) 

42THESE_CLASSES_NAMES = ( 'Module', ) 

43 

44base = cache_import_module( f"{PACKAGE_NAME}.__" ) 

45exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" ) 

46 

47 

48@pytest.mark.parametrize( 

49 'module_qname, class_name', 

50 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

51) 

52def test_100_instantiation( module_qname, class_name ): 

53 ''' Class instantiates with name. ''' 

54 module = cache_import_module( module_qname ) 

55 Module = getattr( module, class_name ) 

56 obj = Module( 'foo' ) 

57 assert isinstance( obj, Module ) 

58 assert 'foo' == obj.__name__ 

59 

60 

61@pytest.mark.parametrize( 

62 'module_qname, class_name', 

63 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

64) 

65def test_101_immutability( module_qname, class_name ): 

66 ''' Module prevents attribute modification. ''' 

67 module = cache_import_module( module_qname ) 

68 Module = getattr( module, class_name ) 

69 obj = Module( 'foo' ) 

70 with pytest.raises( exceptions.AttributeImmutabilityError ): 

71 obj.attr = 42 

72 with pytest.raises( exceptions.AttributeImmutabilityError ): 

73 obj.__name__ = 'bar' 

74 assert 'foo' == obj.__name__ 

75 with pytest.raises( exceptions.AttributeImmutabilityError ): 

76 del obj.__name__ 

77 

78 

79@pytest.mark.parametrize( 

80 'module_qname, class_name', 

81 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

82) 

83def test_500_module_reclassification_by_dict( module_qname, class_name ): 

84 ''' Modules are correctly reclassified as immutable from dictionary. ''' 

85 module = cache_import_module( module_qname ) 

86 Module = getattr( module, class_name ) 

87 from types import ModuleType 

88 m1 = ModuleType( f"{PACKAGE_NAME}.test1" ) 

89 m2 = ModuleType( f"{PACKAGE_NAME}.test2" ) 

90 m3 = ModuleType( "other.module" ) 

91 attrs = { 

92 '__package__': PACKAGE_NAME, 

93 'module1': m1, 

94 'module2': m2, 

95 'external': m3, 

96 'other': 42, 

97 } 

98 assert not isinstance( m1, Module ) 

99 assert not isinstance( m2, Module ) 

100 assert not isinstance( m3, Module ) 

101 module.reclassify_modules( attrs ) 

102 assert isinstance( m1, Module ) 

103 assert isinstance( m2, Module ) 

104 assert not isinstance( m3, Module ) 

105 with pytest.raises( exceptions.AttributeImmutabilityError ): 

106 m1.new_attr = 42 

107 with pytest.raises( exceptions.AttributeImmutabilityError ): 

108 m2.new_attr = 42 

109 m3.new_attr = 42 # Should work 

110 

111 

112@pytest.mark.parametrize( 

113 'module_qname, class_name', 

114 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

115) 

116def test_501_module_reclassification_by_name( module_qname, class_name ): 

117 ''' Modules are correctly reclassified as immutable from name. ''' 

118 module = cache_import_module( module_qname ) 

119 Module = getattr( module, class_name ) 

120 from types import ModuleType 

121 from sys import modules 

122 test_module = ModuleType( f"{PACKAGE_NAME}.test" ) 

123 test_module.__package__ = PACKAGE_NAME 

124 modules[ test_module.__name__ ] = test_module 

125 assert not isinstance( test_module, Module ) 

126 module.reclassify_modules( test_module.__name__ ) 

127 assert isinstance( test_module, Module ) 

128 with pytest.raises( exceptions.AttributeImmutabilityError ): 

129 test_module.new_attr = 42 

130 modules.pop( test_module.__name__ ) # Cleanup 

131 

132 

133@pytest.mark.parametrize( 

134 'module_qname, class_name', 

135 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

136) 

137def test_502_module_reclassification_by_object( module_qname, class_name ): 

138 ''' Modules are correctly reclassified as immutable from object. ''' 

139 module = cache_import_module( module_qname ) 

140 Module = getattr( module, class_name ) 

141 from types import ModuleType 

142 test_module = ModuleType( f"{PACKAGE_NAME}.test" ) 

143 test_module.__package__ = PACKAGE_NAME 

144 assert not isinstance( test_module, Module ) 

145 module.reclassify_modules( test_module ) 

146 assert isinstance( test_module, Module ) 

147 with pytest.raises( exceptions.AttributeImmutabilityError ): 

148 test_module.new_attr = 42 

149 

150 

151@pytest.mark.parametrize( 

152 'module_qname, class_name', 

153 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

154) 

155def test_503_recursive_module_reclassification( module_qname, class_name ): 

156 ''' Recursive module reclassification works correctly. ''' 

157 module = cache_import_module( module_qname ) 

158 Module = getattr( module, class_name ) 

159 from types import ModuleType 

160 root = ModuleType( f"{PACKAGE_NAME}.test" ) 

161 root.__package__ = PACKAGE_NAME 

162 sub1 = ModuleType( f"{PACKAGE_NAME}.test.sub1" ) 

163 sub2 = ModuleType( f"{PACKAGE_NAME}.test.sub2" ) 

164 root.sub1 = sub1 

165 root.sub2 = sub2 

166 assert not isinstance( root, Module ) 

167 assert not isinstance( sub1, Module ) 

168 assert not isinstance( sub2, Module ) 

169 module.reclassify_modules( root, recursive = True ) 

170 assert isinstance( root, Module ) 

171 assert isinstance( sub1, Module ) 

172 assert isinstance( sub2, Module ) 

173 with pytest.raises( exceptions.AttributeImmutabilityError ): 

174 root.new_attr = 42 

175 with pytest.raises( exceptions.AttributeImmutabilityError ): 

176 sub1.new_attr = 42 

177 with pytest.raises( exceptions.AttributeImmutabilityError ): 

178 sub2.new_attr = 42 

179 

180 

181@pytest.mark.parametrize( 

182 'module_qname, class_name', 

183 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

184) 

185def test_504_module_reclassification_respects_package( 

186 module_qname, class_name 

187): 

188 ''' Module reclassification only affects package modules. ''' 

189 module = cache_import_module( module_qname ) 

190 Module = getattr( module, class_name ) 

191 from types import ModuleType 

192 root = ModuleType( f"{PACKAGE_NAME}.test" ) 

193 root.__package__ = PACKAGE_NAME 

194 external = ModuleType( "other_package.module" ) 

195 root.external = external 

196 assert not isinstance( root, Module ) 

197 assert not isinstance( external, Module ) 

198 module.reclassify_modules( root ) 

199 assert isinstance( root, Module ) 

200 assert not isinstance( external, Module ) 

201 with pytest.raises( exceptions.AttributeImmutabilityError ): 

202 root.new_attr = 42 

203 external.new_attr = 42 # Should work 

204 assert 42 == external.new_attr 

205 

206 

207@pytest.mark.parametrize( 

208 'module_qname, class_name', 

209 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

210) 

211def test_505_module_reclassification_requires_package( 

212 module_qname, class_name 

213): 

214 ''' Module reclassification requires package name. ''' 

215 module = cache_import_module( module_qname ) 

216 Module = getattr( module, class_name ) 

217 from types import ModuleType 

218 m1 = ModuleType( f"{PACKAGE_NAME}.test1" ) 

219 attrs = { 'module1': m1 } # Missing __package__ or __name__ 

220 assert not isinstance( m1, Module ) 

221 module.reclassify_modules( attrs ) 

222 assert not isinstance( m1, Module ) 

223 m1.new_attr = 42 # Should work 

224 assert 42 == m1.new_attr 

225 

226 

227@pytest.mark.parametrize( 

228 'module_qname, class_name', 

229 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

230) 

231def test_900_docstring_sanity( module_qname, class_name ): 

232 ''' Class has valid docstring. ''' 

233 module = cache_import_module( module_qname ) 

234 Object = getattr( module, class_name ) 

235 assert hasattr( Object, '__doc__' ) 

236 assert isinstance( Object.__doc__, str ) 

237 assert Object.__doc__ 

238 

239 

240@pytest.mark.parametrize( 

241 'module_qname, class_name', 

242 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

243) 

244def test_901_docstring_describes_module( module_qname, class_name ): 

245 ''' Class docstring describes module. ''' 

246 module = cache_import_module( module_qname ) 

247 Object = getattr( module, class_name ) 

248 fragment = base.generate_docstring( 'description of module' ) 

249 assert fragment in Object.__doc__ 

250 

251 

252@pytest.mark.parametrize( 

253 'module_qname, class_name', 

254 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

255) 

256def test_902_docstring_mentions_immutability( module_qname, class_name ): 

257 ''' Class docstring mentions immutability. ''' 

258 module = cache_import_module( module_qname ) 

259 Object = getattr( module, class_name ) 

260 fragment = base.generate_docstring( 'module attributes immutability' ) 

261 assert fragment in Object.__doc__