Coverage for tests/test_000_frigid/test_100_classes.py: 100%
133 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 03:47 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 03:47 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
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#============================================================================#
21''' Assert correct function of class factory classes. '''
23# mypy: ignore-errors
24# pylint: disable=magic-value-comparison,protected-access
27import pytest
29from itertools import product
30from platform import python_implementation
32from . import (
33 MODULES_QNAMES,
34 PACKAGE_NAME,
35 cache_import_module,
36)
39THESE_MODULE_QNAMES = tuple(
40 name for name in MODULES_QNAMES if name.endswith( '.classes' ) )
41THESE_CLASSES_NAMES = ( 'Class', 'ABCFactory', 'ProtocolClass' )
43base = cache_import_module( f"{PACKAGE_NAME}.__" )
44exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" )
46pypy_skip_mark = pytest.mark.skipif(
47 'PyPy' == python_implementation( ),
48 reason = "PyPy handles class cell updates differently"
49)
52@pytest.mark.parametrize(
53 'module_qname, class_name',
54 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
55)
56def test_100_instantiation( module_qname, class_name ):
57 ''' Class instantiates. '''
58 module = cache_import_module( module_qname )
59 class_factory_class = getattr( module, class_name )
61 class Object( metaclass = class_factory_class ):
62 ''' test '''
64 assert isinstance( Object, class_factory_class )
67@pytest.mark.parametrize(
68 'module_qname, class_name',
69 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
70)
71def test_101_immutability( module_qname, class_name ):
72 ''' Class attributes are immutable. '''
73 module = cache_import_module( module_qname )
74 class_factory_class = getattr( module, class_name )
76 class Object( metaclass = class_factory_class ):
77 ''' test '''
78 attr = 42
80 with pytest.raises( exceptions.AttributeImmutabilityError ):
81 Object.attr = -1
82 assert 42 == Object.attr
83 with pytest.raises( exceptions.AttributeImmutabilityError ):
84 del Object.attr
85 assert 42 == Object.attr
86 with pytest.raises( exceptions.AttributeImmutabilityError ):
87 Object.new_attr = 'foo'
90@pytest.mark.parametrize(
91 'module_qname, class_name',
92 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
93)
94def test_110_class_decorators( module_qname, class_name ):
95 ''' Class accepts and applies decorators correctly. '''
96 module = cache_import_module( module_qname )
97 class_factory_class = getattr( module, class_name )
98 decorator_calls = [ ]
100 def test_decorator1( cls ):
101 decorator_calls.append( 'decorator1' )
102 cls.decorator1_attr = 'value1'
103 return cls
105 def test_decorator2( cls ):
106 decorator_calls.append( 'decorator2' )
107 cls.decorator2_attr = 'value2'
108 return cls
110 class Object(
111 metaclass = class_factory_class,
112 decorators = ( test_decorator1, test_decorator2 )
113 ):
114 ''' test '''
115 attr = 42
117 _class_behaviors_ = { 'foo' }
119 assert [ 'decorator1', 'decorator2' ] == decorator_calls
120 assert 'value1' == Object.decorator1_attr
121 assert 'value2' == Object.decorator2_attr
122 with pytest.raises( exceptions.AttributeImmutabilityError ):
123 Object.decorator1_attr = 'new_value'
124 with pytest.raises( exceptions.AttributeImmutabilityError ):
125 Object.decorator2_attr = 'new_value'
128@pypy_skip_mark
129@pytest.mark.parametrize(
130 'module_qname, class_name',
131 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
132)
133def test_111_class_decorator_reproduction_method( module_qname, class_name ):
134 ''' Class handles decorator reproduction with super() method. '''
135 module = cache_import_module( module_qname )
136 class_factory_class = getattr( module, class_name )
137 from dataclasses import dataclass
139 class Object(
140 metaclass = class_factory_class,
141 decorators = ( dataclass( slots = True ), )
142 ):
143 ''' test '''
144 value: str = 'test'
146 def method_with_super( self ):
147 ''' References class cell on CPython. '''
148 super( ).__init__( )
149 return self.__class__.__name__
151 def other_method_with_super( self ):
152 ''' References class cell on CPython. '''
153 super( ).__init__( )
154 return 'other'
156 # Verify class was properly reproduced and both methods work
157 obj = Object( )
158 assert 'Object' == obj.method_with_super( )
159 assert 'other' == obj.other_method_with_super( )
162@pypy_skip_mark
163@pytest.mark.parametrize(
164 'module_qname, class_name',
165 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
166)
167def test_112_class_decorator_reproduction_property( module_qname, class_name ):
168 ''' Class handles decorator reproduction with dotted access property. '''
169 module = cache_import_module( module_qname )
170 class_factory_class = getattr( module, class_name )
171 from dataclasses import dataclass
173 class Object(
174 metaclass = class_factory_class,
175 decorators = ( dataclass( slots = True ), )
176 ):
177 ''' test '''
178 value: str = 'test'
180 @property
181 def prop_with_class( self ):
182 ''' References class cell on CPython. '''
183 return self.__class__.__name__
185 @property
186 def other_prop_with_class( self ):
187 ''' References class cell on CPython. '''
188 return f"other_{self.__class__.__name__}"
190 # Verify class was properly reproduced and both properties work
191 obj = Object( )
192 assert 'Object' == obj.prop_with_class
193 assert 'other_Object' == obj.other_prop_with_class
196@pypy_skip_mark
197@pytest.mark.parametrize(
198 'module_qname, class_name',
199 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
200)
201def test_113_class_decorator_reproduction_no_cell( module_qname, class_name ):
202 ''' Class handles decorator reproduction with no class cell. '''
203 module = cache_import_module( module_qname )
204 class_factory_class = getattr( module, class_name )
205 from dataclasses import dataclass
207 class Object(
208 metaclass = class_factory_class,
209 decorators = ( dataclass( slots = True ), )
210 ):
211 ''' test '''
212 value: str = 'test'
214 def method_without_cell( self ): # pylint: disable=no-self-use
215 ''' Operates without class cell on CPython. '''
216 return 'no_cell'
218 # Verify class was properly reproduced
219 obj = Object( )
220 assert 'no_cell' == obj.method_without_cell( )
223@pytest.mark.parametrize(
224 'module_qname, class_name',
225 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
226)
227def test_114_decorator_error_handling( module_qname, class_name ):
228 ''' Class handles decorator errors appropriately. '''
229 module = cache_import_module( module_qname )
230 class_factory_class = ( # pylint: disable=unused-variable
231 getattr( module, class_name ) )
233 def failing_decorator( cls ):
234 raise ValueError( "Decorator failure" ) # noqa
236 with pytest.raises( ValueError, match = "Decorator failure" ):
237 class Object( # pylint: disable=unused-variable
238 metaclass = class_factory_class,
239 decorators = ( failing_decorator, )
240 ):
241 ''' test '''
244@pytest.mark.parametrize(
245 'module_qname, class_name',
246 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
247)
248def test_120_docstring_assignment( module_qname, class_name ):
249 ''' Class has dynamically-assigned docstring. '''
250 module = cache_import_module( module_qname )
251 class_factory_class = getattr( module, class_name )
253 class Object( metaclass = class_factory_class, docstring = 'dynamic' ):
254 ''' test '''
255 attr = 42
257 assert 'test' != Object.__doc__
258 assert 'dynamic' == Object.__doc__
261@pytest.mark.parametrize(
262 'module_qname, class_name',
263 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
264)
265def test_900_docstring_sanity( module_qname, class_name ):
266 ''' Class has valid docstring. '''
267 module = cache_import_module( module_qname )
268 class_factory_class = getattr( module, class_name )
269 assert hasattr( class_factory_class, '__doc__' )
270 assert isinstance( class_factory_class.__doc__, str )
271 assert class_factory_class.__doc__
274@pytest.mark.parametrize(
275 'module_qname, class_name',
276 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
277)
278def test_901_docstring_describes_cfc( module_qname, class_name ):
279 ''' Class docstring describes class factory class. '''
280 module = cache_import_module( module_qname )
281 class_factory_class = getattr( module, class_name )
282 fragment = base.generate_docstring( 'description of class factory class' )
283 assert fragment in class_factory_class.__doc__
286@pytest.mark.parametrize(
287 'module_qname, class_name',
288 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
289)
290def test_902_docstring_mentions_immutability( module_qname, class_name ):
291 ''' Class docstring mentions immutability. '''
292 module = cache_import_module( module_qname )
293 class_factory_class = getattr( module, class_name )
294 fragment = base.generate_docstring( 'class attributes immutability' )
295 assert fragment in class_factory_class.__doc__