Coverage for sources/classcore/standard/decorators.py: 100%
124 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-01 21:58 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-01 21:58 +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 __future__ import annotations
29from .. import factories as _factories
30from .. import utilities as _utilities
31from ..decorators import (
32 decoration_by,
33 produce_class_construction_decorator,
34 produce_class_initialization_decorator,
35)
36from . import __
37from . import behaviors as _behaviors
38from . import nomina as _nomina
41_U = __.typx.TypeVar( '_U' )
44_dataclass_core = __.dcls.dataclass( kw_only = True, slots = True )
47def _produce_class_factory_core(
48 attributes_namer: _nomina.AttributesNamer,
49 error_class_provider: _nomina.ErrorClassProvider,
50) -> tuple[ _nomina.ClassConstructor, _nomina.ClassInitializer ]:
51 preprocessors = (
52 _behaviors.produce_class_construction_preprocessor(
53 attributes_namer = attributes_namer ), )
54 postprocessors = (
55 _behaviors.produce_class_construction_postprocessor(
56 attributes_namer = attributes_namer ), )
57 completers = (
58 _behaviors.produce_class_initialization_completer(
59 attributes_namer = attributes_namer ), )
60 constructor = (
61 _factories.produce_class_constructor(
62 attributes_namer = attributes_namer,
63 preprocessors = preprocessors,
64 postprocessors = postprocessors ) )
65 initializer = (
66 _factories.produce_class_initializer(
67 attributes_namer = attributes_namer,
68 completers = completers ) )
69 return constructor, initializer
72def prepare_dataclass_for_instances(
73 cls: type,
74 decorators: _nomina.DecoratorsMutable, /, *,
75 attributes_namer: _nomina.AttributesNamer,
76) -> None:
77 ''' Annotates dataclass in support of instantiation machinery. '''
78 annotations = __.inspect.get_annotations( cls )
79 behaviors_name = attributes_namer( 'instance', 'behaviors' )
80 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name )
81 annotations[ behaviors_name_m ] = set[ str ]
82 setattr( cls, '__annotations__', annotations ) # in case of absence
83 setattr( cls, behaviors_name_m, __.dcls.field( init = False ) )
86def produce_class_factory_decorators(
87 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
88 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
89 assigner_core: _nomina.AssignerCore = (
90 _behaviors.assign_attribute_if_mutable ),
91 deleter_core: _nomina.DeleterCore = (
92 _behaviors.delete_attribute_if_mutable ),
93 surveyor_core: _nomina.SurveyorCore = (
94 _behaviors.survey_visible_attributes ),
95) -> _nomina.Decorators:
96 decorators: list[ _nomina.Decorator ] = [ ]
97 constructor, initializer = (
98 _produce_class_factory_core(
99 attributes_namer = attributes_namer,
100 error_class_provider = error_class_provider ) )
101 decorators.append(
102 produce_class_construction_decorator(
103 attributes_namer = attributes_namer,
104 constructor = constructor ) )
105 decorators.append(
106 produce_class_initialization_decorator(
107 attributes_namer = attributes_namer,
108 initializer = initializer ) )
109 decorators.append(
110 produce_attributes_assignment_decorator(
111 level = 'class',
112 attributes_namer = attributes_namer,
113 error_class_provider = error_class_provider,
114 implementation_core = assigner_core ) )
115 decorators.append(
116 produce_attributes_deletion_decorator(
117 level = 'class',
118 attributes_namer = attributes_namer,
119 error_class_provider = error_class_provider,
120 implementation_core = deleter_core ) )
121 decorators.append(
122 produce_attributes_surveillance_decorator(
123 level = 'class',
124 attributes_namer = attributes_namer,
125 implementation_core = surveyor_core ) )
126 return decorators
129def produce_instances_initialization_decorator(
130 attributes_namer: _nomina.AttributesNamer,
131 mutables: _nomina.BehaviorExclusionVerifiersOmni,
132 visibles: _nomina.BehaviorExclusionVerifiersOmni,
133) -> _nomina.Decorator:
134 def decorate( cls: type[ _U ] ) -> type[ _U ]:
135 initializer_name = attributes_namer( 'instances', 'initializer' )
136 extant = getattr( cls, initializer_name, None )
137 original = getattr( cls, '__init__' )
138 if extant is original: return cls
139 behaviors: set[ str ] = set( )
140 behaviors_name = attributes_namer( 'instance', 'behaviors' )
141 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name )
142 _behaviors.record_behavior(
143 cls, attributes_namer = attributes_namer,
144 level = 'instances', basename = 'mutables',
145 label = _behaviors.immutability_label, behaviors = behaviors,
146 verifiers = mutables )
147 _behaviors.record_behavior(
148 cls, attributes_namer = attributes_namer,
149 level = 'instances', basename = 'visibles',
150 label = _behaviors.concealment_label, behaviors = behaviors,
151 verifiers = visibles )
153 @__.funct.wraps( original )
154 def initialize(
155 self: object, *posargs: __.typx.Any, **nomargs: __.typx.Any
156 ) -> None:
157 original( self, *posargs, **nomargs )
158 behaviors_: set[ str ] = getattr( self, behaviors_name_m, set( ) )
159 behaviors_.update( behaviors )
160 setattr( self, behaviors_name_m, frozenset( behaviors_ ) )
162 setattr( cls, initializer_name, initialize )
163 cls.__init__ = initialize
164 return cls
166 return decorate
169def produce_attributes_assignment_decorator(
170 level: str,
171 attributes_namer: _nomina.AttributesNamer,
172 error_class_provider: _nomina.ErrorClassProvider,
173 implementation_core: _nomina.AssignerCore,
174) -> _nomina.Decorator:
175 def decorate( cls: type[ _U ] ) -> type[ _U ]:
176 assigner_name = attributes_namer( level, 'assigner' )
177 extant = getattr( cls, assigner_name, None )
178 original = getattr( cls, '__setattr__' )
179 if extant is original: return cls
181 @__.funct.wraps( original )
182 def assign( self: object, name: str, value: __.typx.Any ) -> None:
183 implementation_core(
184 self,
185 ligation = __.funct.partial( original, self ),
186 attributes_namer = attributes_namer,
187 error_class_provider = error_class_provider,
188 level = level,
189 name = name, value = value )
191 setattr( cls, assigner_name, assign )
192 cls.__setattr__ = assign
193 return cls
195 return decorate
198def produce_attributes_deletion_decorator(
199 level: str,
200 attributes_namer: _nomina.AttributesNamer,
201 error_class_provider: _nomina.ErrorClassProvider,
202 implementation_core: _nomina.DeleterCore,
203) -> _nomina.Decorator:
204 def decorate( cls: type[ _U ] ) -> type[ _U ]:
205 deleter_name = attributes_namer( level, 'deleter' )
206 extant = getattr( cls, deleter_name, None )
207 original = getattr( cls, '__delattr__' )
208 if extant is original: return cls
210 @__.funct.wraps( original )
211 def delete( self: object, name: str ) -> None:
212 implementation_core(
213 self,
214 ligation = __.funct.partial( original, self ),
215 attributes_namer = attributes_namer,
216 error_class_provider = error_class_provider,
217 level = level,
218 name = name )
220 setattr( cls, deleter_name, delete )
221 cls.__delattr__ = delete
222 return cls
224 return decorate
227def produce_attributes_surveillance_decorator(
228 level: str,
229 attributes_namer: _nomina.AttributesNamer,
230 implementation_core: _nomina.SurveyorCore,
231) -> _nomina.Decorator:
232 def decorate( cls: type[ _U ] ) -> type[ _U ]:
233 surveyor_name = attributes_namer( level, 'surveyor' )
234 extant = getattr( cls, surveyor_name, None )
235 original = getattr( cls, '__dir__' )
236 if extant is original: return cls
238 @__.funct.wraps( original )
239 def survey( self: object ) -> __.cabc.Iterable[ str ]:
240 return implementation_core(
241 self,
242 ligation = __.funct.partial( original, self ),
243 attributes_namer = attributes_namer,
244 level = level )
246 setattr( cls, surveyor_name, survey )
247 cls.__dir__ = survey
248 return cls
250 return decorate
253def produce_decorators_factory( # noqa: PLR0913
254 level: str,
255 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
256 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
257 assigner_core: _nomina.AssignerCore = (
258 _behaviors.assign_attribute_if_mutable ),
259 deleter_core: _nomina.DeleterCore = (
260 _behaviors.delete_attribute_if_mutable ),
261 surveyor_core: _nomina.SurveyorCore = (
262 _behaviors.survey_visible_attributes ),
263) -> __.cabc.Callable[
264 [
265 _nomina.BehaviorExclusionVerifiersOmni,
266 _nomina.BehaviorExclusionVerifiersOmni
267 ],
268 _nomina.Decorators
269]:
270 def produce(
271 mutables: _nomina.BehaviorExclusionVerifiersOmni,
272 visibles: _nomina.BehaviorExclusionVerifiersOmni,
273 ) -> _nomina.Decorators:
274 ''' Produces standard decorators. '''
275 decorators: list[ _nomina.Decorator ] = [ ]
276 decorators.append(
277 produce_instances_initialization_decorator(
278 attributes_namer = attributes_namer,
279 mutables = mutables, visibles = visibles ) )
280 if mutables != '*':
281 decorators.append(
282 produce_attributes_assignment_decorator(
283 level = level,
284 attributes_namer = attributes_namer,
285 error_class_provider = error_class_provider,
286 implementation_core = assigner_core ) )
287 decorators.append(
288 produce_attributes_deletion_decorator(
289 level = level,
290 attributes_namer = attributes_namer,
291 error_class_provider = error_class_provider,
292 implementation_core = deleter_core ) )
293 if visibles != '*':
294 decorators.append(
295 produce_attributes_surveillance_decorator(
296 level = level,
297 attributes_namer = attributes_namer,
298 implementation_core = surveyor_core ) )
299 return decorators
301 return produce
304def produce_decoration_preparers_factory(
305 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
306 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class,
307 class_preparer: __.typx.Optional[ _nomina.ClassPreparer ] = None,
308) -> __.cabc.Callable[ [ ], _nomina.DecorationPreparers ]:
309 def produce( ) -> _nomina.DecorationPreparers:
310 ''' Produces processors for standard decorators. '''
311 preprocessors: list[ _nomina.DecorationPreparer ] = [ ]
312 if class_preparer is not None:
313 preprocessors.append(
314 __.funct.partial(
315 class_preparer,
316 attributes_namer = attributes_namer ) )
317 return tuple( preprocessors )
319 return produce
322class_factory_decorators = produce_class_factory_decorators( )
325@__.typx.dataclass_transform( frozen_default = True, kw_only_default = True )
326def dataclass_with_standard_behaviors(
327 decorators: _nomina.Decorators = ( ),
328 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default,
329 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default,
330) -> _nomina.Decorator:
331 # https://github.com/microsoft/pyright/discussions/10344
332 ''' Dataclass decorator factory. '''
333 decorators_factory = produce_decorators_factory( level = 'instances' )
334 decorators_ = decorators_factory( mutables, visibles )
335 preparers_factory = produce_decoration_preparers_factory(
336 class_preparer = prepare_dataclass_for_instances )
337 preparers = preparers_factory( )
338 return decoration_by(
339 *decorators, _dataclass_core, *decorators_, preparers = preparers )
342def with_standard_behaviors(
343 decorators: _nomina.Decorators = ( ),
344 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default,
345 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default,
346) -> _nomina.Decorator:
347 ''' Class decorator factory. '''
348 decorators_factory = produce_decorators_factory( level = 'instances' )
349 decorators_ = decorators_factory( mutables, visibles )
350 preparers_factory = produce_decoration_preparers_factory( )
351 preparers = preparers_factory( )
352 return decoration_by( *decorators, *decorators_, preparers = preparers )