Coverage for sources/classcore/standard/decorators.py: 100%
132 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-08 04:17 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-08 04:17 +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''' Standard decorators. '''
22# TODO? Add attribute value transformer as standard decorator argument.
24# ruff: noqa: F401
27from .. import factories as _factories
28from .. import utilities as _utilities
29from ..decorators import (
30 decoration_by,
31 produce_class_construction_decorator,
32 produce_class_initialization_decorator,
33)
34from . import __
35from . import behaviors as _behaviors
36from . import dynadoc as _dynadoc
37from . import nomina as _nomina
40_dataclass_core = __.dcls.dataclass( kw_only = True, slots = True )
41_dynadoc_configuration = _dynadoc.produce_dynadoc_configuration( )
44def prepare_dataclass_for_instances(
45 cls: type,
46 decorators: _nomina.DecoratorsMutable[ __.U ], /, *,
47 attributes_namer: _nomina.AttributesNamer,
48) -> None:
49 ''' Annotates dataclass in support of instantiation machinery. '''
50 annotations = __.inspect.get_annotations( cls )
51 behaviors_name = attributes_namer( 'instance', 'behaviors' )
52 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name )
53 annotations[ behaviors_name_m ] = set[ str ]
54 setattr( cls, '__annotations__', annotations ) # in case of absence
55 setattr( cls, behaviors_name_m, __.dcls.field( init = False ) )
58def apply_cfc_dynadoc_configuration(
59 clscls: type[ __.T ], /,
60 attributes_namer: _nomina.AttributesNamer,
61 configuration: _nomina.DynadocConfiguration,
62) -> None:
63 ''' Stores Dynadoc configuration on metaclass. '''
64 configuration_name = attributes_namer( 'classes', 'dynadoc_configuration' )
65 setattr( clscls, configuration_name, configuration )
68def apply_cfc_constructor(
69 clscls: type[ __.T ], /,
70 attributes_namer: _nomina.AttributesNamer,
71 error_class_provider: _nomina.ErrorClassProvider,
72) -> None:
73 ''' Injects '__new__' method into metaclass. '''
74 preprocessors = (
75 _behaviors.produce_class_construction_preprocessor(
76 attributes_namer = attributes_namer ), )
77 postprocessors = (
78 _behaviors.produce_class_construction_postprocessor(
79 attributes_namer = attributes_namer,
80 error_class_provider = error_class_provider ), )
81 constructor: _nomina.ClassConstructor[ __.T ] = (
82 _factories.produce_class_constructor(
83 attributes_namer = attributes_namer,
84 preprocessors = preprocessors,
85 postprocessors = postprocessors ) )
86 decorator = produce_class_construction_decorator(
87 attributes_namer = attributes_namer, constructor = constructor )
88 decorator( clscls )
91def apply_cfc_initializer(
92 clscls: type[ __.T ], attributes_namer: _nomina.AttributesNamer
93) -> None:
94 ''' Injects '__init__' method into metaclass. '''
95 completers = (
96 _behaviors.produce_class_initialization_completer(
97 attributes_namer = attributes_namer ), )
98 initializer = (
99 _factories.produce_class_initializer(
100 attributes_namer = attributes_namer,
101 completers = completers ) )
102 decorator = produce_class_initialization_decorator(
103 attributes_namer = attributes_namer, initializer = initializer )
104 decorator( clscls )
107def apply_cfc_attributes_assigner(
108 clscls: type[ __.T ], /,
109 attributes_namer: _nomina.AttributesNamer,
110 error_class_provider: _nomina.ErrorClassProvider,
111 implementation_core: _nomina.AssignerCore,
112) -> None:
113 ''' Injects '__setattr__' method into metaclass. '''
114 decorator = produce_attributes_assignment_decorator(
115 level = 'class',
116 attributes_namer = attributes_namer,
117 error_class_provider = error_class_provider,
118 implementation_core = implementation_core )
119 decorator( clscls )
122def apply_cfc_attributes_deleter(
123 clscls: type[ __.T ], /,
124 attributes_namer: _nomina.AttributesNamer,
125 error_class_provider: _nomina.ErrorClassProvider,
126 implementation_core: _nomina.DeleterCore,
127) -> None:
128 ''' Injects '__delattr__' method into metaclass. '''
129 decorator = produce_attributes_deletion_decorator(
130 level = 'class',
131 attributes_namer = attributes_namer,
132 error_class_provider = error_class_provider,
133 implementation_core = implementation_core )
134 decorator( clscls )
137def apply_cfc_attributes_surveyor(
138 clscls: type[ __.T ],
139 attributes_namer: _nomina.AttributesNamer,
140 implementation_core: _nomina.SurveyorCore,
141) -> None:
142 ''' Injects '__dir__' method into metaclass. '''
143 decorator = produce_attributes_surveillance_decorator(
144 level = 'class',
145 attributes_namer = attributes_namer,
146 implementation_core = implementation_core )
147 decorator( clscls )
150def class_factory( # noqa: PLR0913
151 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
152 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
153 assigner_core: _nomina.AssignerCore = (
154 _behaviors.assign_attribute_if_mutable ),
155 deleter_core: _nomina.DeleterCore = (
156 _behaviors.delete_attribute_if_mutable ),
157 surveyor_core: _nomina.SurveyorCore = (
158 _behaviors.survey_visible_attributes ),
159 dynadoc_configuration: __.cabc.Mapping[ str, __.typx.Any ] = (
160 _dynadoc_configuration ),
161) -> _nomina.Decorator[ __.T ]:
162 ''' Produces decorator to apply standard behaviors to metaclass. '''
163 def decorate( clscls: type[ __.T ] ) -> type[ __.T ]:
164 apply_cfc_dynadoc_configuration(
165 clscls,
166 attributes_namer = attributes_namer,
167 configuration = dynadoc_configuration )
168 apply_cfc_constructor(
169 clscls,
170 attributes_namer = attributes_namer,
171 error_class_provider = error_class_provider )
172 apply_cfc_initializer( clscls, attributes_namer = attributes_namer )
173 apply_cfc_attributes_assigner(
174 clscls,
175 attributes_namer = attributes_namer,
176 error_class_provider = error_class_provider,
177 implementation_core = assigner_core )
178 apply_cfc_attributes_deleter(
179 clscls,
180 attributes_namer = attributes_namer,
181 error_class_provider = error_class_provider,
182 implementation_core = deleter_core )
183 apply_cfc_attributes_surveyor(
184 clscls,
185 attributes_namer = attributes_namer,
186 implementation_core = surveyor_core )
187 return clscls
189 return decorate
192def produce_instances_initialization_decorator(
193 attributes_namer: _nomina.AttributesNamer,
194 mutables: _nomina.BehaviorExclusionVerifiersOmni,
195 visibles: _nomina.BehaviorExclusionVerifiersOmni,
196) -> _nomina.Decorator[ __.U ]:
197 ''' Produces decorator to inject '__init__' method into class. '''
198 def decorate( cls: type[ __.U ] ) -> type[ __.U ]:
199 initializer_name = attributes_namer( 'instances', 'initializer' )
200 extant = getattr( cls, initializer_name, None )
201 original = getattr( cls, '__init__' )
202 if extant is original: return cls
203 behaviors: set[ str ] = set( )
204 behaviors_name = attributes_namer( 'instance', 'behaviors' )
205 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name )
206 _behaviors.record_behavior(
207 cls, attributes_namer = attributes_namer,
208 level = 'instances', basename = 'mutables',
209 label = _nomina.immutability_label, behaviors = behaviors,
210 verifiers = mutables )
211 _behaviors.record_behavior(
212 cls, attributes_namer = attributes_namer,
213 level = 'instances', basename = 'visibles',
214 label = _nomina.concealment_label, behaviors = behaviors,
215 verifiers = visibles )
217 @__.funct.wraps( original )
218 def initialize(
219 self: object, *posargs: __.typx.Any, **nomargs: __.typx.Any
220 ) -> None:
221 original( self, *posargs, **nomargs )
222 behaviors_: set[ str ] = getattr( self, behaviors_name_m, set( ) )
223 behaviors_.update( behaviors )
224 setattr( self, behaviors_name_m, frozenset( behaviors_ ) )
226 setattr( cls, initializer_name, initialize )
227 cls.__init__ = initialize
228 return cls
230 return decorate
233def produce_attributes_assignment_decorator(
234 level: str,
235 attributes_namer: _nomina.AttributesNamer,
236 error_class_provider: _nomina.ErrorClassProvider,
237 implementation_core: _nomina.AssignerCore,
238) -> _nomina.Decorator[ __.U ]:
239 ''' Produces decorator to inject '__setattr__' method into class. '''
240 def decorate( cls: type[ __.U ] ) -> type[ __.U ]:
241 assigner_name = attributes_namer( level, 'assigner' )
242 extant = getattr( cls, assigner_name, None )
243 original = getattr( cls, '__setattr__' )
244 if extant is original: return cls
246 @__.funct.wraps( original )
247 def assign( self: object, name: str, value: __.typx.Any ) -> None:
248 implementation_core(
249 self,
250 ligation = __.funct.partial( original, self ),
251 attributes_namer = attributes_namer,
252 error_class_provider = error_class_provider,
253 level = level,
254 name = name, value = value )
256 setattr( cls, assigner_name, assign )
257 cls.__setattr__ = assign
258 return cls
260 return decorate
263def produce_attributes_deletion_decorator(
264 level: str,
265 attributes_namer: _nomina.AttributesNamer,
266 error_class_provider: _nomina.ErrorClassProvider,
267 implementation_core: _nomina.DeleterCore,
268) -> _nomina.Decorator[ __.U ]:
269 ''' Produces decorator to inject '__delattr__' method into class. '''
270 def decorate( cls: type[ __.U ] ) -> type[ __.U ]:
271 deleter_name = attributes_namer( level, 'deleter' )
272 extant = getattr( cls, deleter_name, None )
273 original = getattr( cls, '__delattr__' )
274 if extant is original: return cls
276 @__.funct.wraps( original )
277 def delete( self: object, name: str ) -> None:
278 implementation_core(
279 self,
280 ligation = __.funct.partial( original, self ),
281 attributes_namer = attributes_namer,
282 error_class_provider = error_class_provider,
283 level = level,
284 name = name )
286 setattr( cls, deleter_name, delete )
287 cls.__delattr__ = delete
288 return cls
290 return decorate
293def produce_attributes_surveillance_decorator(
294 level: str,
295 attributes_namer: _nomina.AttributesNamer,
296 implementation_core: _nomina.SurveyorCore,
297) -> _nomina.Decorator[ __.U ]:
298 ''' Produces decorator to inject '__dir__' method into class. '''
299 def decorate( cls: type[ __.U ] ) -> type[ __.U ]:
300 surveyor_name = attributes_namer( level, 'surveyor' )
301 extant = getattr( cls, surveyor_name, None )
302 original = getattr( cls, '__dir__' )
303 if extant is original: return cls
305 @__.funct.wraps( original )
306 def survey( self: object ) -> __.cabc.Iterable[ str ]:
307 return implementation_core(
308 self,
309 ligation = __.funct.partial( original, self ),
310 attributes_namer = attributes_namer,
311 level = level )
313 setattr( cls, surveyor_name, survey )
314 cls.__dir__ = survey
315 return cls
317 return decorate
320@__.typx.dataclass_transform( frozen_default = True, kw_only_default = True )
321def dataclass_with_standard_behaviors( # noqa: PLR0913
322 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
323 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
324 decorators: _nomina.Decorators[ __.U ] = ( ),
325 assigner_core: _nomina.AssignerCore = (
326 _behaviors.assign_attribute_if_mutable ),
327 deleter_core: _nomina.DeleterCore = (
328 _behaviors.delete_attribute_if_mutable ),
329 surveyor_core: _nomina.SurveyorCore = (
330 _behaviors.survey_visible_attributes ),
331 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default,
332 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default,
333) -> _nomina.Decorator[ __.U ]:
334 # https://github.com/microsoft/pyright/discussions/10344
335 ''' Dataclass decorator factory. '''
336 decorators_: _nomina.Decorators[ __.U ] = (
337 _produce_instances_decorators(
338 attributes_namer = attributes_namer,
339 error_class_provider = error_class_provider,
340 assigner_core = assigner_core,
341 deleter_core = deleter_core,
342 surveyor_core = surveyor_core,
343 mutables = mutables,
344 visibles = visibles ) )
345 preparers: _nomina.DecorationPreparers[ __.U ] = (
346 _produce_instances_decoration_preparers(
347 attributes_namer = attributes_namer,
348 error_class_provider = error_class_provider,
349 class_preparer = prepare_dataclass_for_instances ) )
350 return decoration_by(
351 *decorators, _dataclass_core, *decorators_, preparers = preparers )
354def with_standard_behaviors( # noqa: PLR0913
355 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
356 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
357 decorators: _nomina.Decorators[ __.U ] = ( ),
358 assigner_core: _nomina.AssignerCore = (
359 _behaviors.assign_attribute_if_mutable ),
360 deleter_core: _nomina.DeleterCore = (
361 _behaviors.delete_attribute_if_mutable ),
362 surveyor_core: _nomina.SurveyorCore = (
363 _behaviors.survey_visible_attributes ),
364 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default,
365 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default,
366) -> _nomina.Decorator[ __.U ]:
367 ''' Class decorator factory. '''
368 decorators_: _nomina.Decorators[ __.U ] = (
369 _produce_instances_decorators(
370 attributes_namer = attributes_namer,
371 error_class_provider = error_class_provider,
372 assigner_core = assigner_core,
373 deleter_core = deleter_core,
374 surveyor_core = surveyor_core,
375 mutables = mutables,
376 visibles = visibles ) )
377 preparers: _nomina.DecorationPreparers[ __.U ] = (
378 _produce_instances_decoration_preparers(
379 attributes_namer = attributes_namer,
380 error_class_provider = error_class_provider ) )
381 return decoration_by( *decorators, *decorators_, preparers = preparers )
384def _produce_instances_decoration_preparers(
385 attributes_namer: _nomina.AttributesNamer,
386 error_class_provider: _nomina.ErrorClassProvider,
387 class_preparer: __.typx.Optional[ _nomina.ClassPreparer ] = None,
388) -> _nomina.DecorationPreparers[ __.U ]:
389 ''' Produces processors for standard decorators. '''
390 preprocessors: list[ _nomina.DecorationPreparer[ __.U ] ] = [ ]
391 if class_preparer is not None:
392 preprocessors.append(
393 __.funct.partial(
394 class_preparer, attributes_namer = attributes_namer ) )
395 return tuple( preprocessors )
398def _produce_instances_decorators( # noqa: PLR0913
399 attributes_namer: _nomina.AttributesNamer,
400 error_class_provider: _nomina.ErrorClassProvider,
401 assigner_core: _nomina.AssignerCore,
402 deleter_core: _nomina.DeleterCore,
403 surveyor_core: _nomina.SurveyorCore,
404 mutables: _nomina.BehaviorExclusionVerifiersOmni,
405 visibles: _nomina.BehaviorExclusionVerifiersOmni,
406) -> _nomina.Decorators[ __.U ]:
407 ''' Produces standard decorators. '''
408 decorators: list[ _nomina.Decorator[ __.U ] ] = [ ]
409 decorators.append(
410 produce_instances_initialization_decorator(
411 attributes_namer = attributes_namer,
412 mutables = mutables, visibles = visibles ) )
413 if mutables != '*':
414 decorators.append(
415 produce_attributes_assignment_decorator(
416 level = 'instances',
417 attributes_namer = attributes_namer,
418 error_class_provider = error_class_provider,
419 implementation_core = assigner_core ) )
420 decorators.append(
421 produce_attributes_deletion_decorator(
422 level = 'instances',
423 attributes_namer = attributes_namer,
424 error_class_provider = error_class_provider,
425 implementation_core = deleter_core ) )
426 if visibles != '*':
427 decorators.append(
428 produce_attributes_surveillance_decorator(
429 level = 'instances',
430 attributes_namer = attributes_namer,
431 implementation_core = surveyor_core ) )
432 return decorators