classcore 1.0rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classcore/__/__init__.py +25 -0
- classcore/__/imports.py +42 -0
- classcore/__/nomina.py +37 -0
- classcore/__init__.py +38 -0
- classcore/decorators.py +130 -0
- classcore/exceptions.py +59 -0
- classcore/factories.py +92 -0
- classcore/nomina.py +162 -0
- classcore/py.typed +0 -0
- classcore/standard/__.py +46 -0
- classcore/standard/__init__.py +28 -0
- classcore/standard/behaviors.py +325 -0
- classcore/standard/classes.py +92 -0
- classcore/standard/decorators.py +350 -0
- classcore/standard/modules.py +89 -0
- classcore/standard/nomina.py +100 -0
- classcore/utilities.py +84 -0
- classcore-1.0rc0.dist-info/METADATA +282 -0
- classcore-1.0rc0.dist-info/RECORD +21 -0
- classcore-1.0rc0.dist-info/WHEEL +4 -0
- classcore-1.0rc0.dist-info/licenses/LICENSE.txt +202 -0
|
@@ -0,0 +1,325 @@
|
|
|
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
|
+
''' Implementations for standard behaviors. '''
|
|
22
|
+
# TODO? Support introspection of PEP 593 annotations for behavior exclusions.
|
|
23
|
+
# Maybe enum for mutability and visibility.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from .. import utilities as _utilities
|
|
29
|
+
from . import __
|
|
30
|
+
from . import nomina as _nomina
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
concealment_label = 'concealment'
|
|
34
|
+
immutability_label = 'immutability'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def assign_attribute_if_mutable( # noqa: PLR0913
|
|
38
|
+
obj: object, /, *,
|
|
39
|
+
ligation: _nomina.AssignerLigation,
|
|
40
|
+
attributes_namer: _nomina.AttributesNamer,
|
|
41
|
+
error_class_provider: _nomina.ErrorClassProvider,
|
|
42
|
+
level: str,
|
|
43
|
+
name: str,
|
|
44
|
+
value: __.typx.Any,
|
|
45
|
+
) -> None:
|
|
46
|
+
leveli = 'instance' if level == 'instances' else level
|
|
47
|
+
behaviors_name = attributes_namer( leveli, 'behaviors' )
|
|
48
|
+
behaviors = _utilities.getattr0( obj, behaviors_name, frozenset( ) )
|
|
49
|
+
if immutability_label not in behaviors:
|
|
50
|
+
ligation( name, value )
|
|
51
|
+
return
|
|
52
|
+
names_name = attributes_namer( level, 'mutables_names' )
|
|
53
|
+
names: _nomina.BehaviorExclusionNamesOmni = (
|
|
54
|
+
getattr( obj, names_name, frozenset( ) ) )
|
|
55
|
+
if names == '*' or name in names:
|
|
56
|
+
ligation( name, value )
|
|
57
|
+
return
|
|
58
|
+
predicates_name = attributes_namer( level, 'mutables_predicates' )
|
|
59
|
+
predicates: _nomina.BehaviorExclusionPredicates = (
|
|
60
|
+
getattr( obj, predicates_name, ( ) ) )
|
|
61
|
+
for predicate in predicates:
|
|
62
|
+
if predicate( name ):
|
|
63
|
+
# TODO? Cache predicate hit.
|
|
64
|
+
ligation( name, value )
|
|
65
|
+
return
|
|
66
|
+
regexes_name = attributes_namer( level, 'mutables_regexes' )
|
|
67
|
+
regexes: _nomina.BehaviorExclusionRegexes = (
|
|
68
|
+
getattr( obj, regexes_name, ( ) ) )
|
|
69
|
+
for regex in regexes:
|
|
70
|
+
if regex.fullmatch( name ):
|
|
71
|
+
# TODO? Cache regex hit.
|
|
72
|
+
ligation( name, value )
|
|
73
|
+
return
|
|
74
|
+
target = _utilities.describe_object( obj )
|
|
75
|
+
raise error_class_provider( 'AttributeImmutability' )( name, target )
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def delete_attribute_if_mutable( # noqa: PLR0913
|
|
79
|
+
obj: object, /, *,
|
|
80
|
+
ligation: _nomina.DeleterLigation,
|
|
81
|
+
attributes_namer: _nomina.AttributesNamer,
|
|
82
|
+
error_class_provider: _nomina.ErrorClassProvider,
|
|
83
|
+
level: str,
|
|
84
|
+
name: str,
|
|
85
|
+
) -> None:
|
|
86
|
+
leveli = 'instance' if level == 'instances' else level
|
|
87
|
+
behaviors_name = attributes_namer( leveli, 'behaviors' )
|
|
88
|
+
behaviors = _utilities.getattr0( obj, behaviors_name, frozenset( ) )
|
|
89
|
+
if immutability_label not in behaviors:
|
|
90
|
+
ligation( name )
|
|
91
|
+
return
|
|
92
|
+
names_name = attributes_namer( level, 'mutables_names' )
|
|
93
|
+
names: _nomina.BehaviorExclusionNamesOmni = (
|
|
94
|
+
getattr( obj, names_name, frozenset( ) ) )
|
|
95
|
+
if names == '*' or name in names:
|
|
96
|
+
ligation( name )
|
|
97
|
+
return
|
|
98
|
+
predicates_name = attributes_namer( level, 'mutables_predicates' )
|
|
99
|
+
predicates: _nomina.BehaviorExclusionPredicates = (
|
|
100
|
+
getattr( obj, predicates_name, ( ) ) )
|
|
101
|
+
for predicate in predicates:
|
|
102
|
+
if predicate( name ):
|
|
103
|
+
# TODO? Cache predicate hit.
|
|
104
|
+
ligation( name )
|
|
105
|
+
return
|
|
106
|
+
regexes_name = attributes_namer( level, 'mutables_regexes' )
|
|
107
|
+
regexes: _nomina.BehaviorExclusionRegexes = (
|
|
108
|
+
getattr( obj, regexes_name, ( ) ) )
|
|
109
|
+
for regex in regexes:
|
|
110
|
+
if regex.fullmatch( name ):
|
|
111
|
+
# TODO? Cache regex hit.
|
|
112
|
+
ligation( name )
|
|
113
|
+
return
|
|
114
|
+
target = _utilities.describe_object( obj )
|
|
115
|
+
raise error_class_provider( 'AttributeImmutability' )( name, target )
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def survey_visible_attributes(
|
|
119
|
+
obj: object, /, *,
|
|
120
|
+
ligation: _nomina.SurveyorLigation,
|
|
121
|
+
attributes_namer: _nomina.AttributesNamer,
|
|
122
|
+
level: str,
|
|
123
|
+
) -> __.cabc.Iterable[ str ]:
|
|
124
|
+
names_base = ligation( )
|
|
125
|
+
leveli = 'instance' if level == 'instances' else level
|
|
126
|
+
behaviors_name = attributes_namer( leveli, 'behaviors' )
|
|
127
|
+
behaviors = _utilities.getattr0( obj, behaviors_name, frozenset( ) )
|
|
128
|
+
if concealment_label not in behaviors: return names_base
|
|
129
|
+
names_name = attributes_namer( level, 'visibles_names' )
|
|
130
|
+
names: _nomina.BehaviorExclusionNamesOmni = (
|
|
131
|
+
getattr( obj, names_name, frozenset( ) ) )
|
|
132
|
+
if names == '*': return names_base # pragma: no branch
|
|
133
|
+
regexes_name = attributes_namer( level, 'visibles_regexes' )
|
|
134
|
+
regexes: _nomina.BehaviorExclusionRegexes = (
|
|
135
|
+
getattr( obj, regexes_name, ( ) ) )
|
|
136
|
+
predicates_name = attributes_namer( level, 'visibles_predicates' )
|
|
137
|
+
predicates: _nomina.BehaviorExclusionPredicates = (
|
|
138
|
+
getattr( obj, predicates_name, ( ) ) )
|
|
139
|
+
names_: list[ str ] = [ ]
|
|
140
|
+
for name in names_base:
|
|
141
|
+
if name in names:
|
|
142
|
+
names_.append( name )
|
|
143
|
+
continue
|
|
144
|
+
for predicate in predicates:
|
|
145
|
+
if predicate( name ):
|
|
146
|
+
# TODO? Cache predicate hit.
|
|
147
|
+
names_.append( name )
|
|
148
|
+
continue
|
|
149
|
+
for regex in regexes:
|
|
150
|
+
if regex.fullmatch( name ):
|
|
151
|
+
# TODO? Cache regex hit.
|
|
152
|
+
names_.append( name )
|
|
153
|
+
continue
|
|
154
|
+
return names_
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def classify_behavior_exclusion_verifiers(
|
|
158
|
+
verifiers: _nomina.BehaviorExclusionVerifiers
|
|
159
|
+
) -> tuple[
|
|
160
|
+
_nomina.BehaviorExclusionNames,
|
|
161
|
+
_nomina.BehaviorExclusionRegexes,
|
|
162
|
+
_nomina.BehaviorExclusionPredicates,
|
|
163
|
+
]:
|
|
164
|
+
names: set[ str ] = set( )
|
|
165
|
+
regexes: list[ __.re.Pattern[ str ] ] = [ ]
|
|
166
|
+
predicates: list[ __.cabc.Callable[ ..., bool ] ] = [ ]
|
|
167
|
+
for verifier in verifiers:
|
|
168
|
+
if isinstance( verifier, str ):
|
|
169
|
+
names.add( verifier )
|
|
170
|
+
elif isinstance( verifier, __.re.Pattern ):
|
|
171
|
+
regexes.append( verifier )
|
|
172
|
+
elif callable( verifier ):
|
|
173
|
+
predicates.append( verifier )
|
|
174
|
+
else:
|
|
175
|
+
from ..exceptions import BehaviorExclusionInvalidity
|
|
176
|
+
raise BehaviorExclusionInvalidity( verifier )
|
|
177
|
+
return frozenset( names ), tuple( regexes ), tuple( predicates )
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def produce_class_construction_preprocessor(
|
|
181
|
+
attributes_namer: _nomina.AttributesNamer
|
|
182
|
+
) -> _nomina.ClassConstructionPreprocessor:
|
|
183
|
+
|
|
184
|
+
def preprocess( # noqa: PLR0913
|
|
185
|
+
clscls: type,
|
|
186
|
+
name: str,
|
|
187
|
+
bases: list[ type ],
|
|
188
|
+
namespace: dict[ str, __.typx.Any ],
|
|
189
|
+
arguments: dict[ str, __.typx.Any ],
|
|
190
|
+
decorators: _nomina.DecoratorsMutable,
|
|
191
|
+
) -> None:
|
|
192
|
+
record_class_construction_arguments(
|
|
193
|
+
attributes_namer, namespace, arguments )
|
|
194
|
+
|
|
195
|
+
return preprocess
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def produce_class_construction_postprocessor(
|
|
199
|
+
attributes_namer: _nomina.AttributesNamer
|
|
200
|
+
) -> _nomina.ClassConstructionPostprocessor:
|
|
201
|
+
arguments_name = attributes_namer( 'class', 'construction_arguments' )
|
|
202
|
+
|
|
203
|
+
def postprocess(
|
|
204
|
+
cls: type, decorators: _nomina.DecoratorsMutable
|
|
205
|
+
) -> None:
|
|
206
|
+
arguments = getattr( cls, arguments_name, { } )
|
|
207
|
+
dcls_spec = getattr( cls, '__dataclass_transform__', None )
|
|
208
|
+
if not dcls_spec: # either base class or metaclass may be marked
|
|
209
|
+
clscls = type( cls )
|
|
210
|
+
dcls_spec = getattr( clscls, '__dataclass_transform__', None )
|
|
211
|
+
instances_mutables = arguments.get(
|
|
212
|
+
'instances_mutables', __.mutables_default )
|
|
213
|
+
instances_visibles = arguments.get(
|
|
214
|
+
'instances_visibles', __.visibles_default )
|
|
215
|
+
if dcls_spec and dcls_spec.get( 'kw_only_default', False ):
|
|
216
|
+
from .decorators import dataclass_with_standard_behaviors
|
|
217
|
+
decorator_factory = dataclass_with_standard_behaviors
|
|
218
|
+
if not dcls_spec.get( 'frozen_default', True ):
|
|
219
|
+
instances_mutables = instances_mutables or '*'
|
|
220
|
+
else:
|
|
221
|
+
from .decorators import with_standard_behaviors
|
|
222
|
+
decorator_factory = with_standard_behaviors
|
|
223
|
+
decorator = decorator_factory(
|
|
224
|
+
mutables = instances_mutables, visibles = instances_visibles )
|
|
225
|
+
decorators.append( decorator )
|
|
226
|
+
|
|
227
|
+
return postprocess
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def produce_class_initialization_completer(
|
|
231
|
+
attributes_namer: _nomina.AttributesNamer
|
|
232
|
+
) -> _nomina.ClassInitializationCompleter:
|
|
233
|
+
arguments_name = attributes_namer( 'class', 'construction_arguments' )
|
|
234
|
+
|
|
235
|
+
def complete( cls: type ) -> None:
|
|
236
|
+
arguments: __.typx.Optional[ dict[ str, __.typx.Any ] ] = (
|
|
237
|
+
getattr( cls, arguments_name, None ) )
|
|
238
|
+
if arguments is not None: delattr( cls, arguments_name )
|
|
239
|
+
arguments = arguments or { }
|
|
240
|
+
mutables = arguments.get( 'class_mutables', __.mutables_default )
|
|
241
|
+
visibles = arguments.get( 'class_visibles', __.visibles_default )
|
|
242
|
+
behaviors: set[ str ] = set( )
|
|
243
|
+
record_behavior(
|
|
244
|
+
cls, attributes_namer = attributes_namer,
|
|
245
|
+
level = 'class', basename = 'mutables',
|
|
246
|
+
label = immutability_label, behaviors = behaviors,
|
|
247
|
+
verifiers = mutables )
|
|
248
|
+
record_behavior(
|
|
249
|
+
cls, attributes_namer = attributes_namer,
|
|
250
|
+
level = 'class', basename = 'visibles',
|
|
251
|
+
label = concealment_label, behaviors = behaviors,
|
|
252
|
+
verifiers = visibles )
|
|
253
|
+
# Set behaviors attribute last since it enables enforcement.
|
|
254
|
+
setattr( cls, attributes_namer( 'class', 'behaviors' ), behaviors )
|
|
255
|
+
|
|
256
|
+
return complete
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def record_behavior( # noqa: PLR0913
|
|
260
|
+
cls: type, /, *,
|
|
261
|
+
attributes_namer: _nomina.AttributesNamer,
|
|
262
|
+
level: str,
|
|
263
|
+
basename: str,
|
|
264
|
+
label: str,
|
|
265
|
+
behaviors: set[ str ],
|
|
266
|
+
verifiers: _nomina.BehaviorExclusionVerifiersOmni,
|
|
267
|
+
) -> None:
|
|
268
|
+
names_name = attributes_namer( level, f"{basename}_names" )
|
|
269
|
+
if verifiers == '*':
|
|
270
|
+
setattr( cls, names_name, '*' )
|
|
271
|
+
return
|
|
272
|
+
names_omni: _nomina.BehaviorExclusionNamesOmni = (
|
|
273
|
+
getattr( cls, names_name, frozenset( ) ) )
|
|
274
|
+
if names_omni == '*': return
|
|
275
|
+
names, regexes, predicates = (
|
|
276
|
+
classify_behavior_exclusion_verifiers( verifiers ) )
|
|
277
|
+
regexes_name = attributes_namer( level, f"{basename}_regexes" )
|
|
278
|
+
predicates_name = attributes_namer( level, f"{basename}_predicates" )
|
|
279
|
+
names_: _nomina.BehaviorExclusionNames = (
|
|
280
|
+
frozenset( { *names, *names_omni } ) )
|
|
281
|
+
regexes_: _nomina.BehaviorExclusionRegexes = (
|
|
282
|
+
_deduplicate_merge_sequences(
|
|
283
|
+
regexes, getattr( cls, regexes_name, ( ) ) ) )
|
|
284
|
+
predicates_: _nomina.BehaviorExclusionPredicates = (
|
|
285
|
+
_deduplicate_merge_sequences(
|
|
286
|
+
predicates, getattr( cls, predicates_name, ( ) ) ) )
|
|
287
|
+
setattr( cls, names_name, names_ )
|
|
288
|
+
setattr( cls, regexes_name, regexes_ )
|
|
289
|
+
setattr( cls, predicates_name, predicates_ )
|
|
290
|
+
# TODO? Add regexes match cache.
|
|
291
|
+
# TODO? Add predicates match cache.
|
|
292
|
+
behaviors.add( label )
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def record_class_construction_arguments(
|
|
296
|
+
attributes_namer: _nomina.AttributesNamer,
|
|
297
|
+
namespace: dict[ str, __.typx.Any ],
|
|
298
|
+
arguments: dict[ str, __.typx.Any ],
|
|
299
|
+
) -> None:
|
|
300
|
+
arguments_name = attributes_namer( 'class', 'construction_arguments' )
|
|
301
|
+
arguments_ = namespace.get( arguments_name, { } )
|
|
302
|
+
# Decorators, which replace classes, will cause construction of the
|
|
303
|
+
# replacements without arguments. If we had previously recorded them in
|
|
304
|
+
# the class namespace, then we do not want to clobber them.
|
|
305
|
+
if arguments_: return
|
|
306
|
+
arguments_ = { }
|
|
307
|
+
for name in (
|
|
308
|
+
'class_mutables', 'class_visibles',
|
|
309
|
+
'instances_mutables', 'instances_visibles',
|
|
310
|
+
):
|
|
311
|
+
if name not in arguments: continue
|
|
312
|
+
arguments_[ name ] = arguments.pop( name )
|
|
313
|
+
namespace[ arguments_name ] = arguments_
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _deduplicate_merge_sequences(
|
|
317
|
+
addends: __.cabc.Sequence[ __.typx.Any ],
|
|
318
|
+
augends: __.cabc.Sequence[ __.typx.Any ],
|
|
319
|
+
) -> __.cabc.Sequence[ __.typx.Any ]:
|
|
320
|
+
result = list( augends )
|
|
321
|
+
augends_ = set( augends )
|
|
322
|
+
for addend in addends:
|
|
323
|
+
if addend in augends_: continue
|
|
324
|
+
result.append( addend )
|
|
325
|
+
return tuple( result )
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
''' Standard classes and class factories. '''
|
|
22
|
+
# TODO? ClassMutable and ProtocolClassMutable
|
|
23
|
+
# Need inheritance of omnimutability and omnivisibility.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from . import __
|
|
29
|
+
from . import decorators as _decorators
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
33
|
+
class Class( type ): pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
37
|
+
@__.typx.dataclass_transform( frozen_default = True, kw_only_default = True )
|
|
38
|
+
class Dataclass( type ): pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
42
|
+
@__.typx.dataclass_transform( kw_only_default = True )
|
|
43
|
+
class DataclassMutable( type ): pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
47
|
+
class ProtocolClass( type( __.typx.Protocol ) ): pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
51
|
+
@__.typx.dataclass_transform( frozen_default = True, kw_only_default = True )
|
|
52
|
+
class ProtocolDataclass( type( __.typx.Protocol ) ): pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@_decorators.decoration_by( *_decorators.class_factory_decorators )
|
|
56
|
+
@__.typx.dataclass_transform( kw_only_default = True )
|
|
57
|
+
class ProtocolDataclassMutable( type( __.typx.Protocol ) ): pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Object( metaclass = Class ): pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ObjectMutable( # pyright: ignore[reportGeneralTypeIssues]
|
|
64
|
+
metaclass = Class,
|
|
65
|
+
instances_mutables = '*', # pyright: ignore[reportCallIssue]
|
|
66
|
+
): pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DataclassObject( metaclass = Dataclass ): pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DataclassObjectMutable( metaclass = DataclassMutable ): pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Protocol( __.typx.Protocol, metaclass = ProtocolClass ): pass
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ProtocolMutable( # pyright: ignore[reportGeneralTypeIssues]
|
|
79
|
+
__.typx.Protocol,
|
|
80
|
+
metaclass = ProtocolClass,
|
|
81
|
+
instances_mutables = '*', # pyright: ignore[reportCallIssue]
|
|
82
|
+
): pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DataclassProtocol(
|
|
86
|
+
__.typx.Protocol, metaclass = ProtocolDataclass,
|
|
87
|
+
): pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DataclassProtocolMutable(
|
|
91
|
+
__.typx.Protocol, metaclass = ProtocolDataclassMutable,
|
|
92
|
+
): pass
|