Coverage for sources/accretive/classes.py: 100%
78 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-10 22:24 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-10 22:24 +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''' Accretive classes. '''
24from __future__ import annotations
26from . import __
29ClassDecorators: __.a.TypeAlias = (
30 __.cabc.Iterable[ __.cabc.Callable[ [ type ], type ] ] )
33_behavior = 'accretion'
36class Class( type ):
37 ''' Accretive class factory. '''
39 def __new__( # pylint: disable=too-many-arguments
40 factory: type[ type ],
41 name: str,
42 bases: tuple[ type, ... ],
43 namespace: dict[ str, __.a.Any ], *,
44 decorators: ClassDecorators = ( ),
45 docstring: __.Optional[ __.a.Nullable[ str ] ] = __.absent,
46 **args: __.a.Any
47 ) -> Class:
48 class_ = type.__new__(
49 factory, name, bases, namespace, **args )
50 return _class__new__( # type: ignore
51 class_, decorators = decorators, docstring = docstring )
53 def __init__( selfclass, *posargs: __.a.Any, **nomargs: __.a.Any ):
54 super( ).__init__( *posargs, **nomargs )
55 _class__init__( selfclass )
57 def __delattr__( selfclass, name: str ) -> None:
58 if not _class__delattr__( selfclass, name ):
59 super( ).__delattr__( name )
61 def __setattr__( selfclass, name: str, value: __.a.Any ) -> None:
62 if not _class__setattr__( selfclass, name ):
63 super( ).__setattr__( name, value )
65Class.__doc__ = __.generate_docstring(
66 Class,
67 'description of class factory class',
68 'class attributes accretion'
69)
72class ABCFactory( __.ABCFactory ): # type: ignore
73 ''' Accretive abstract base class factory. '''
75 def __new__( # pylint: disable=too-many-arguments
76 factory: type[ type ],
77 name: str,
78 bases: tuple[ type, ... ],
79 namespace: dict[ str, __.a.Any ], *,
80 decorators: ClassDecorators = ( ),
81 docstring: __.Optional[ __.a.Nullable[ str ] ] = __.absent,
82 **args: __.a.Any
83 ) -> ABCFactory:
84 class_ = __.ABCFactory.__new__(
85 factory, name, bases, namespace, **args )
86 return _class__new__( # type: ignore
87 class_, decorators = decorators, docstring = docstring )
89 def __init__( selfclass, *posargs: __.a.Any, **nomargs: __.a.Any ):
90 super( ).__init__( *posargs, **nomargs )
91 _class__init__( selfclass )
93 def __delattr__( selfclass, name: str ) -> None:
94 if not _class__delattr__( selfclass, name ):
95 super( ).__delattr__( name )
97 def __setattr__( selfclass, name: str, value: __.a.Any ) -> None:
98 if not _class__setattr__( selfclass, name ):
99 super( ).__setattr__( name, value )
101ABCFactory.__doc__ = __.generate_docstring(
102 ABCFactory,
103 'description of class factory class',
104 'class attributes accretion'
105)
108class ProtocolClass( __.a.Protocol.__class__ ): # type: ignore
109 ''' Accretive protocol class factory. '''
111 def __new__( # pylint: disable=too-many-arguments
112 factory: type[ type ],
113 name: str,
114 bases: tuple[ type, ... ],
115 namespace: dict[ str, __.a.Any ], *,
116 decorators: ClassDecorators = ( ),
117 docstring: __.Optional[ __.a.Nullable[ str ] ] = __.absent,
118 **args: __.a.Any
119 ) -> ProtocolClass:
120 class_ = __.a.Protocol.__class__.__new__(
121 factory, name, bases, namespace, **args )
122 return _class__new__( # type: ignore
123 class_, decorators = decorators, docstring = docstring )
125 def __init__( selfclass, *posargs: __.a.Any, **nomargs: __.a.Any ):
126 super( ).__init__( *posargs, **nomargs )
127 _class__init__( selfclass )
129 def __delattr__( selfclass, name: str ) -> None:
130 if not _class__delattr__( selfclass, name ):
131 super( ).__delattr__( name )
133 def __setattr__( selfclass, name: str, value: __.a.Any ) -> None:
134 if not _class__setattr__( selfclass, name ):
135 super( ).__setattr__( name, value )
137ProtocolClass.__doc__ = __.generate_docstring(
138 ProtocolClass,
139 'description of class factory class',
140 'class attributes accretion'
141)
144def _class__new__(
145 original: type,
146 decorators: ClassDecorators = ( ),
147 docstring: __.Optional[ __.a.Nullable[ str ] ] = __.absent,
148) -> type:
149 # Handle decorators similar to immutable implementation.
150 # Some decorators create new classes, which invokes this method again.
151 # Short-circuit to prevent recursive decoration and other tangles.
152 class_decorators_ = original.__dict__.get( '_class_decorators_', [ ] )
153 if class_decorators_: return original
154 if not __.is_absent( docstring ): original.__doc__ = docstring
155 setattr( original, '_class_decorators_', class_decorators_ )
156 reproduction = original
157 for decorator in decorators:
158 class_decorators_.append( decorator )
159 reproduction = decorator( original )
160 if original is not reproduction:
161 __.repair_class_reproduction( original, reproduction )
162 original = reproduction
163 class_decorators_.clear( ) # Flag '__init__' to enable accretion
164 return reproduction
167def _class__init__( class_: type ) -> None:
168 # Some metaclasses add class attributes in '__init__' method.
169 # So, we wait until last possible moment to set accretion.
170 if class_.__dict__.get( '_class_decorators_' ): return
171 del class_._class_decorators_
172 if ( class_behaviors := class_.__dict__.get( '_class_behaviors_' ) ):
173 class_behaviors.add( _behavior )
174 else: setattr( class_, '_class_behaviors_', { _behavior } )
177def _class__delattr__( class_: type, name: str ) -> bool:
178 # Consult class attributes dictionary to ignore accretive base classes.
179 if _behavior not in class_.__dict__.get( '_class_behaviors_', ( ) ):
180 return False
181 from .exceptions import IndelibleAttributeError
182 raise IndelibleAttributeError( name )
185def _class__setattr__( class_: type, name: str ) -> bool:
186 # Consult class attributes dictionary to ignore accretive base classes.
187 if _behavior not in class_.__dict__.get( '_class_behaviors_', ( ) ):
188 return False
189 if hasattr( class_, name ):
190 from .exceptions import IndelibleAttributeError
191 raise IndelibleAttributeError( name )
192 return False # Allow setting new attributes