classcore 1.11__tar.gz → 1.13__tar.gz
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-1.11 → classcore-1.13}/PKG-INFO +4 -6
- {classcore-1.11 → classcore-1.13}/pyproject.toml +13 -8
- {classcore-1.11 → classcore-1.13}/sources/classcore/README.md +27 -7
- {classcore-1.11 → classcore-1.13}/sources/classcore/__/imports.py +1 -0
- classcore-1.13/sources/classcore/__/nomina.py +95 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/__init__.py +1 -1
- {classcore-1.11 → classcore-1.13}/sources/classcore/decorators.py +23 -5
- {classcore-1.11 → classcore-1.13}/sources/classcore/factories.py +15 -6
- {classcore-1.11 → classcore-1.13}/sources/classcore/nomina.py +36 -13
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/README.md +31 -17
- classcore-1.13/sources/classcore/standard/__.py +106 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/__init__.py +1 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/behaviors.py +159 -65
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/classes.py +202 -16
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/decorators.py +37 -7
- classcore-1.13/sources/classcore/standard/explanations.py +253 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/nomina.py +4 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/utilities.py +28 -3
- classcore-1.11/sources/classcore/__/nomina.py +0 -36
- classcore-1.11/sources/classcore/standard/__.py +0 -42
- {classcore-1.11 → classcore-1.13}/.gitignore +0 -0
- {classcore-1.11 → classcore-1.13}/LICENSE.txt +0 -0
- {classcore-1.11 → classcore-1.13}/README.rst +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/__/__init__.py +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/__/doctab.py +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/exceptions.py +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/py.typed +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/dynadoc.py +0 -0
- {classcore-1.11 → classcore-1.13}/sources/classcore/standard/modules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: classcore
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.13
|
|
4
4
|
Summary: Foundational class factories and decorators.
|
|
5
5
|
Project-URL: Homepage, https://github.com/emcd/python-classcore
|
|
6
6
|
Project-URL: Documentation, https://emcd.github.io/python-classcore
|
|
@@ -15,15 +15,13 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
23
|
Classifier: Topic :: Software Development
|
|
26
|
-
Requires-Python: >=3.
|
|
24
|
+
Requires-Python: >=3.12
|
|
27
25
|
Requires-Dist: dynadoc~=1.4
|
|
28
26
|
Requires-Dist: typing-extensions
|
|
29
27
|
Description-Content-Type: text/x-rst
|
|
@@ -13,7 +13,7 @@ description = 'Foundational class factories and decorators.'
|
|
|
13
13
|
dynamic = [ 'version' ]
|
|
14
14
|
license = 'Apache-2.0'
|
|
15
15
|
readme = { 'file' = 'README.rst', 'content-type' = 'text/x-rst' }
|
|
16
|
-
requires-python = '>= 3.
|
|
16
|
+
requires-python = '>= 3.12'
|
|
17
17
|
dependencies = [
|
|
18
18
|
'dynadoc ~= 1.4',
|
|
19
19
|
'typing-extensions',
|
|
@@ -26,13 +26,11 @@ classifiers = [ # https://pypi.org/classifiers
|
|
|
26
26
|
'License :: OSI Approved :: Apache Software License',
|
|
27
27
|
'Programming Language :: Python :: 3 :: Only',
|
|
28
28
|
# --- BEGIN: Injected by Copier ---
|
|
29
|
-
'Programming Language :: Python :: 3.10',
|
|
30
|
-
'Programming Language :: Python :: 3.11',
|
|
31
29
|
'Programming Language :: Python :: 3.12',
|
|
32
30
|
'Programming Language :: Python :: 3.13',
|
|
33
31
|
'Programming Language :: Python :: 3.14',
|
|
32
|
+
'Programming Language :: Python :: 3.15',
|
|
34
33
|
'Programming Language :: Python :: Implementation :: CPython',
|
|
35
|
-
'Programming Language :: Python :: Implementation :: PyPy',
|
|
36
34
|
# --- END: Injected by Copier ---
|
|
37
35
|
'Topic :: Software Development',
|
|
38
36
|
]
|
|
@@ -91,7 +89,7 @@ strict-naming = false
|
|
|
91
89
|
# --- BEGIN: Injected by Copier ---
|
|
92
90
|
# --- END: Injected by Copier ---
|
|
93
91
|
[tool.hatch.envs.default]
|
|
94
|
-
python = '3.
|
|
92
|
+
python = '3.12'
|
|
95
93
|
[tool.hatch.envs.develop]
|
|
96
94
|
description = ''' Development environment. '''
|
|
97
95
|
builder = true
|
|
@@ -127,6 +125,12 @@ docsgen = [
|
|
|
127
125
|
"""sphinx-build -a -d .auxiliary/caches/sphinx --quiet \
|
|
128
126
|
documentation .auxiliary/artifacts/sphinx-html""",
|
|
129
127
|
]
|
|
128
|
+
fragments = [
|
|
129
|
+
"""python .auxiliary/scripts/validate-fragments.py""",
|
|
130
|
+
]
|
|
131
|
+
module-sizes = [
|
|
132
|
+
"""python .auxiliary/scripts/validate-module-sizes.py""",
|
|
133
|
+
]
|
|
130
134
|
linters = [
|
|
131
135
|
"""ruff check --quiet sources documentation tests""",
|
|
132
136
|
# """vibelinter check""",
|
|
@@ -134,6 +138,8 @@ linters = [
|
|
|
134
138
|
# --- END: Injected by Copier ---
|
|
135
139
|
"""isort --check-only --diff sources tests""",
|
|
136
140
|
"""pyright sources""",
|
|
141
|
+
'fragments',
|
|
142
|
+
'module-sizes',
|
|
137
143
|
]
|
|
138
144
|
packagers = [
|
|
139
145
|
"""hatch build""",
|
|
@@ -170,12 +176,10 @@ description = ''' Quality assurance environment. '''
|
|
|
170
176
|
template = 'develop'
|
|
171
177
|
[[tool.hatch.envs.qa.matrix]]
|
|
172
178
|
python = [
|
|
173
|
-
'3.10',
|
|
174
|
-
'3.11',
|
|
175
179
|
'3.12',
|
|
176
180
|
'3.13',
|
|
177
181
|
'3.14',
|
|
178
|
-
'
|
|
182
|
+
'3.15',
|
|
179
183
|
]
|
|
180
184
|
# [tool.hatch.metadata]
|
|
181
185
|
# allow-direct-references = true
|
|
@@ -255,6 +259,7 @@ cache_dir = '.auxiliary/caches/pytest'
|
|
|
255
259
|
markers = [
|
|
256
260
|
"slow: long-running tests",
|
|
257
261
|
]
|
|
262
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
258
263
|
|
|
259
264
|
[tool.ruff]
|
|
260
265
|
#builtins = [ 'ic' ]
|
|
@@ -83,19 +83,20 @@ Pre-configured implementations for common use cases:
|
|
|
83
83
|
**Base Classes:**
|
|
84
84
|
- `Object`: Standard class with immutability and concealment
|
|
85
85
|
- `DataclassObject`: Automatic dataclass conversion with standard behaviors
|
|
86
|
-
- `
|
|
86
|
+
- `Protocol`: Protocol base class with immutability and concealment
|
|
87
|
+
- `AbstractObject`: Abstract class with ABC machinery and behaviors
|
|
87
88
|
- Variants for selective mutability (`ObjectMutable`, etc.)
|
|
88
89
|
|
|
89
90
|
**Metaclasses:**
|
|
90
91
|
- `Class`: Metaclass for standard classes
|
|
91
92
|
- `Dataclass`: Metaclass that auto-applies dataclass decorator
|
|
92
|
-
- `
|
|
93
|
+
- `AbstractClass`: Metaclass combining `Class` with `abc.ABCMeta`
|
|
94
|
+
- `ProtocolClass`: Metaclass for protocol classes
|
|
93
95
|
- Mutable variants of each
|
|
94
96
|
|
|
95
97
|
**Decorators:**
|
|
96
|
-
- `
|
|
98
|
+
- `with_standard_behaviors`: Add behaviors to existing classes
|
|
97
99
|
- `dataclass_with_standard_behaviors`: Dataclass creation with behaviors
|
|
98
|
-
- `protocol_with_standard_behaviors`: Protocol creation with behaviors
|
|
99
100
|
|
|
100
101
|
**Module Utilities:**
|
|
101
102
|
- `reclassify_module`: Apply immutability and concealment to module objects
|
|
@@ -296,6 +297,7 @@ sources/
|
|
|
296
297
|
├── classes.py # Standard metaclasses and base classes
|
|
297
298
|
├── decorators.py # Standard class decorators
|
|
298
299
|
├── dynadoc.py # Documentation configuration
|
|
300
|
+
├── explanations.py # Attribute behavior explanations
|
|
299
301
|
├── modules.py # Module reclassification utilities
|
|
300
302
|
└── nomina.py # Standard-specific naming types
|
|
301
303
|
```
|
|
@@ -377,20 +379,38 @@ sources/
|
|
|
377
379
|
- `survey_visible_attributes`: Filter attributes for `dir()`
|
|
378
380
|
- Verifier processing for names, regexes, and predicates
|
|
379
381
|
|
|
382
|
+
`explanations.py`
|
|
383
|
+
: Explanations of attribute behavior decisions:
|
|
384
|
+
|
|
385
|
+
- `explain_attribute`: Decision trace for one attribute of one target
|
|
386
|
+
- `AttributeExplanation` and the `Verdict` records (`AssignVerdict`,
|
|
387
|
+
`DeleteVerdict`, `SurveyVerdict`) with derived permissibility
|
|
388
|
+
- The closed `Decision` hierarchy: `PermitByInapplicability`,
|
|
389
|
+
`PermitByOmni`, `PermitByNames`, `PermitByPredicate`,
|
|
390
|
+
`PermitByRegex`, `Prohibit`, with typed payloads
|
|
391
|
+
- Internal marking via metaclass-carried contributions: the namer
|
|
392
|
+
detector for framework names, the abstract-base wiring for stdlib
|
|
393
|
+
ABC machinery (only where that machinery operates), and downstream
|
|
394
|
+
factory namers' detectors for downstream names
|
|
395
|
+
|
|
380
396
|
`classes.py`
|
|
381
397
|
: Standard metaclasses and base classes:
|
|
382
398
|
|
|
383
399
|
- `Class` / `Object`: Standard class with full behaviors
|
|
400
|
+
- `AbstractClass` / `AbstractObject`: Abstract class with ABC machinery
|
|
401
|
+
(abstract method enforcement, virtual subclass registration); mixes
|
|
402
|
+
with external `abc.ABCMeta`-based classes
|
|
384
403
|
- `Dataclass` / `DataclassObject`: Auto-dataclass with behaviors
|
|
385
|
-
- `
|
|
404
|
+
- `ProtocolClass` / `Protocol`, `ProtocolMutable`,
|
|
405
|
+
`DataclassProtocol`, `DataclassProtocolMutable`: Protocol classes
|
|
406
|
+
with behaviors
|
|
386
407
|
- Mutable variants for each
|
|
387
408
|
|
|
388
409
|
`decorators.py`
|
|
389
410
|
: Standard class decorators:
|
|
390
411
|
|
|
391
|
-
- `
|
|
412
|
+
- `with_standard_behaviors`: Apply behaviors to existing class
|
|
392
413
|
- `dataclass_with_standard_behaviors`: Create dataclass with behaviors
|
|
393
|
-
- `protocol_with_standard_behaviors`: Create protocol with behaviors
|
|
394
414
|
- `class_factory`: Generic factory for creating custom metaclasses
|
|
395
415
|
|
|
396
416
|
`dynadoc.py`
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
''' Common names and type aliases. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from . import imports as __
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ComparisonResult: __.typx.TypeAlias = bool | __.types.NotImplementedType
|
|
28
|
+
NominativeArguments: __.typx.TypeAlias = __.cabc.Mapping[ str, __.typx.Any ]
|
|
29
|
+
PositionalArguments: __.typx.TypeAlias = __.cabc.Sequence[ __.typx.Any ]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
package_name = __name__.split( '.', maxsplit = 1 )[ 0 ]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_levels = frozenset( ( 'class', 'classes', 'instance', 'instances' ) )
|
|
36
|
+
_cores = frozenset( (
|
|
37
|
+
'behaviors',
|
|
38
|
+
'construction_arguments',
|
|
39
|
+
'dynadoc_configuration',
|
|
40
|
+
'in_progress',
|
|
41
|
+
'mutables_names', 'mutables_predicates', 'mutables_regexes',
|
|
42
|
+
'visibles_names', 'visibles_predicates', 'visibles_regexes',
|
|
43
|
+
'assigner_core', 'deleter_core', 'surveyor_core' ) )
|
|
44
|
+
_hex_digits = frozenset( '0123456789abcdef' )
|
|
45
|
+
_digest_length = 64
|
|
46
|
+
_mangled_cores = frozenset( ( 'class_behaviors', 'class_in_progress' ) )
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AttrnameCalculator:
|
|
50
|
+
''' Calculates framework attribute names from level and core.
|
|
51
|
+
|
|
52
|
+
Reference implementation of the attribute namer convention:
|
|
53
|
+
call instances to produce names, and consult ``is_internal_name``
|
|
54
|
+
to detect names this framework generated. Downstream packages
|
|
55
|
+
may subclass (or otherwise provide callables exposing the same
|
|
56
|
+
optional detector) to contribute their own internal names.
|
|
57
|
+
'''
|
|
58
|
+
|
|
59
|
+
def is_internal_name( self, name: str ) -> bool:
|
|
60
|
+
prefix = f"_{package_name}_"
|
|
61
|
+
if not name.startswith( prefix ):
|
|
62
|
+
return False
|
|
63
|
+
body = name[ len( prefix ): ]
|
|
64
|
+
# Static stems: _{package}_{level}_{core}_, trailing underscore.
|
|
65
|
+
# The class-level behaviors and in_progress stems are stored
|
|
66
|
+
# mangled only, so they are excluded from static acceptance.
|
|
67
|
+
if body.endswith( '_' ):
|
|
68
|
+
level, _, core = body[ :-1 ].partition( '_' )
|
|
69
|
+
if level in _levels and core in _cores:
|
|
70
|
+
return not (
|
|
71
|
+
'class' == level
|
|
72
|
+
and f"{level}_{core}" in _mangled_cores )
|
|
73
|
+
return False
|
|
74
|
+
# Mangled forms: one of the mangled cores + 64 lowercase hex,
|
|
75
|
+
# no trailing underscore. The mangling accessors append digests
|
|
76
|
+
# to these stems only.
|
|
77
|
+
for mangled_core in _mangled_cores:
|
|
78
|
+
mangled_prefix = f"{mangled_core}_"
|
|
79
|
+
if body.startswith( mangled_prefix ):
|
|
80
|
+
suffix = body[ len( mangled_prefix ): ]
|
|
81
|
+
return (
|
|
82
|
+
_digest_length == len( suffix )
|
|
83
|
+
and all( c in _hex_digits for c in suffix ) )
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
def __call__( self, level: str, core: str ) -> str:
|
|
87
|
+
return f"_{package_name}_{level}_{core}_"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
calculate_attrname = AttrnameCalculator( )
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def calculate_contribution_name( ) -> str:
|
|
94
|
+
''' Returns metaclass attribute name holding internal-name detectors. '''
|
|
95
|
+
return f"_{package_name}_internal_names_"
|
|
@@ -89,8 +89,14 @@ def produce_class_construction_decorator(
|
|
|
89
89
|
decorators: _nomina.Decorators[ __.T ] = ( ),
|
|
90
90
|
**arguments: __.typx.Any,
|
|
91
91
|
) -> type[ object ]:
|
|
92
|
-
superf =
|
|
93
|
-
|
|
92
|
+
superf = __.typx.cast(
|
|
93
|
+
_nomina.ClassConstructorLigation,
|
|
94
|
+
super( clscls, clscls_ ).__new__ )
|
|
95
|
+
if clscls is not clscls_:
|
|
96
|
+
# Parent metaclass within a hierarchy: delegate without
|
|
97
|
+
# duplicating preprocessing or decoration.
|
|
98
|
+
return superf(
|
|
99
|
+
clscls_, name, bases, namespace, **arguments )
|
|
94
100
|
return constructor(
|
|
95
101
|
clscls_, superf,
|
|
96
102
|
name, bases, namespace, arguments, decorators )
|
|
@@ -107,7 +113,11 @@ def produce_class_construction_decorator(
|
|
|
107
113
|
decorators: _nomina.Decorators[ __.T ] = ( ),
|
|
108
114
|
**arguments: __.typx.Any,
|
|
109
115
|
) -> type[ object ]:
|
|
110
|
-
|
|
116
|
+
if clscls is not clscls_:
|
|
117
|
+
# Parent metaclass within a hierarchy: delegate without
|
|
118
|
+
# duplicating preprocessing or decoration.
|
|
119
|
+
return original(
|
|
120
|
+
clscls_, name, bases, namespace, **arguments )
|
|
111
121
|
return constructor(
|
|
112
122
|
clscls_, original,
|
|
113
123
|
name, bases, namespace, arguments, decorators )
|
|
@@ -138,7 +148,11 @@ def produce_class_initialization_decorator(
|
|
|
138
148
|
cls: type, *posargs: __.typx.Any, **nomargs: __.typx.Any
|
|
139
149
|
) -> None:
|
|
140
150
|
ligation = super( clscls, cls ).__init__
|
|
141
|
-
|
|
151
|
+
if clscls is not type( cls ):
|
|
152
|
+
# Parent metaclass within a hierarchy: delegate without
|
|
153
|
+
# duplicating completion logic.
|
|
154
|
+
ligation( *posargs, **nomargs )
|
|
155
|
+
return
|
|
142
156
|
initializer( cls, ligation, posargs, nomargs )
|
|
143
157
|
|
|
144
158
|
clscls.__init__ = initialize_with_super
|
|
@@ -149,8 +163,12 @@ def produce_class_initialization_decorator(
|
|
|
149
163
|
def initialize_with_original(
|
|
150
164
|
cls: type, *posargs: __.typx.Any, **nomargs: __.typx.Any
|
|
151
165
|
) -> None:
|
|
166
|
+
if clscls is not type( cls ):
|
|
167
|
+
# Parent metaclass within a hierarchy: delegate without
|
|
168
|
+
# duplicating completion logic.
|
|
169
|
+
original( cls, *posargs, **nomargs )
|
|
170
|
+
return
|
|
152
171
|
ligation = __.funct.partial( original, cls )
|
|
153
|
-
# TODO? Short-circuit if not at start of MRO.
|
|
154
172
|
initializer( cls, ligation, posargs, nomargs )
|
|
155
173
|
|
|
156
174
|
clscls.__init__ = initialize_with_original
|
|
@@ -34,7 +34,7 @@ def produce_class_constructor(
|
|
|
34
34
|
) -> _nomina.ClassConstructor[ __.T ]:
|
|
35
35
|
''' Produces constructors for classes. '''
|
|
36
36
|
|
|
37
|
-
def construct( # noqa: PLR0913
|
|
37
|
+
def construct( # noqa: PLR0913, PLR0917
|
|
38
38
|
clscls: type[ __.T ],
|
|
39
39
|
superf: _nomina.ClassConstructorLigation,
|
|
40
40
|
name: str,
|
|
@@ -54,12 +54,20 @@ def produce_class_constructor(
|
|
|
54
54
|
# Some decorators create new classes, which invokes this method again.
|
|
55
55
|
# Short-circuit to prevent recursive decoration and other tangles.
|
|
56
56
|
progress_name = attributes_namer( 'class', 'in_progress' )
|
|
57
|
-
|
|
58
|
-
in_progress =
|
|
57
|
+
progress_names = _utilities.survey_mangled_names( cls, progress_name )
|
|
58
|
+
in_progress = any(
|
|
59
|
+
getattr( cls, key, False ) for key in progress_names )
|
|
59
60
|
if in_progress: return cls
|
|
61
|
+
progress_name_m = _utilities.mangle_name( cls, progress_name )
|
|
60
62
|
setattr( cls, progress_name_m, True )
|
|
61
63
|
for postprocessor in postprocessors: postprocessor( cls, decorators_ )
|
|
62
64
|
cls = _decorators.apply_decorators( cls, decorators_ )
|
|
65
|
+
# Decoration may have replaced the class. Clear every marker
|
|
66
|
+
# variant, including ones inherited from the original namespace,
|
|
67
|
+
# so initialization completes regardless of qualname restoration.
|
|
68
|
+
progress_names = _utilities.survey_mangled_names( cls, progress_name )
|
|
69
|
+
for progress_name_ in progress_names:
|
|
70
|
+
setattr( cls, progress_name_, False )
|
|
63
71
|
setattr( cls, progress_name_m, False )
|
|
64
72
|
return cls
|
|
65
73
|
|
|
@@ -81,10 +89,11 @@ def produce_class_initializer(
|
|
|
81
89
|
''' Initializes class, applying hooks. '''
|
|
82
90
|
superf( *posargs, **nomargs )
|
|
83
91
|
progress_name = attributes_namer( 'class', 'in_progress' )
|
|
84
|
-
|
|
85
|
-
in_progress =
|
|
92
|
+
progress_names = _utilities.survey_mangled_names( cls, progress_name )
|
|
93
|
+
in_progress = any(
|
|
94
|
+
getattr( cls, key, False ) for key in progress_names )
|
|
86
95
|
if in_progress: return # If non-empty, then not top-level.
|
|
87
|
-
delattr( cls, progress_name_m )
|
|
96
|
+
for progress_name_m in progress_names: delattr( cls, progress_name_m )
|
|
88
97
|
for completer in completers: completer( cls )
|
|
89
98
|
|
|
90
99
|
return initialize
|
|
@@ -214,29 +214,52 @@ ClassInitializationCompleters: __.typx.TypeAlias = __.typx.Annotated[
|
|
|
214
214
|
ClassConstructor: __.typx.TypeAlias = __.typx.Annotated[
|
|
215
215
|
__.cabc.Callable[
|
|
216
216
|
[
|
|
217
|
-
type,
|
|
218
|
-
ClassConstructorLigation,
|
|
219
|
-
str,
|
|
220
|
-
tuple[ type, ... ],
|
|
221
|
-
dict[ str, __.typx.Any ],
|
|
222
|
-
__.NominativeArguments,
|
|
223
|
-
Decorators[ __.U ],
|
|
217
|
+
type, # metaclass
|
|
218
|
+
ClassConstructorLigation, # next constructor in metaclass MRO
|
|
219
|
+
str, # class name
|
|
220
|
+
tuple[ type, ... ], # bases
|
|
221
|
+
dict[ str, __.typx.Any ], # namespace
|
|
222
|
+
__.NominativeArguments, # metaclass arguments
|
|
223
|
+
Decorators[ __.U ], # decorators to apply after
|
|
224
224
|
],
|
|
225
225
|
type
|
|
226
226
|
],
|
|
227
|
-
__.ddoc.Doc(
|
|
227
|
+
__.ddoc.Doc(
|
|
228
|
+
''' Constructs class, then applies hooks.
|
|
229
|
+
|
|
230
|
+
Receives the metaclass ('clscls'), the next constructor in
|
|
231
|
+
the metaclass MRO ('superf'), the class name, bases,
|
|
232
|
+
namespace, metaclass arguments, and decorators to apply
|
|
233
|
+
after construction. Calls 'superf' to create the class,
|
|
234
|
+
then applies postprocessors and decorators.
|
|
235
|
+
|
|
236
|
+
Invoked only at the most-derived metaclass of a class.
|
|
237
|
+
Class factory wrappers on parent metaclasses delegate
|
|
238
|
+
directly, so construction hooks execute exactly once per
|
|
239
|
+
class.
|
|
240
|
+
''' ),
|
|
228
241
|
]
|
|
229
242
|
ClassInitializer: __.typx.TypeAlias = __.typx.Annotated[
|
|
230
243
|
__.cabc.Callable[
|
|
231
244
|
[
|
|
232
|
-
type,
|
|
233
|
-
InitializerLigation,
|
|
234
|
-
__.PositionalArguments,
|
|
235
|
-
__.NominativeArguments,
|
|
245
|
+
type, # class
|
|
246
|
+
InitializerLigation, # next initializer in metaclass MRO
|
|
247
|
+
__.PositionalArguments, # positional arguments
|
|
248
|
+
__.NominativeArguments, # nominative arguments
|
|
236
249
|
],
|
|
237
250
|
None
|
|
238
251
|
],
|
|
239
|
-
__.ddoc.Doc(
|
|
252
|
+
__.ddoc.Doc(
|
|
253
|
+
''' Initializes class, then applies hooks.
|
|
254
|
+
|
|
255
|
+
Receives the class ('cls'), the next initializer in the
|
|
256
|
+
metaclass MRO ('superf'), positional arguments, and
|
|
257
|
+
nominative arguments. Calls 'superf' first, then applies
|
|
258
|
+
completers, which finalize class behaviors (e.g., enable
|
|
259
|
+
immutability once all other initialization has occurred).
|
|
260
|
+
|
|
261
|
+
Invoked only at the most-derived metaclass of a class.
|
|
262
|
+
''' ),
|
|
240
263
|
]
|
|
241
264
|
|
|
242
265
|
|
|
@@ -40,17 +40,27 @@ class Point2d(DataclassObject):
|
|
|
40
40
|
self.hypotenuse = sqrt(x*x + y*y)
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
### `
|
|
43
|
+
### `Protocol`
|
|
44
|
+
|
|
45
|
+
Protocol base class with immutability and concealment. Combines
|
|
46
|
+
`typing.Protocol` with standard behaviors. Immutability and concealment
|
|
47
|
+
are inherited by subclassed implementations. Dataclass protocol bases
|
|
48
|
+
(`DataclassProtocol`, `DataclassProtocolMutable`) additionally provide
|
|
49
|
+
automatic dataclass conversion.
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
### `AbstractObject`
|
|
52
|
+
|
|
53
|
+
Abstract base class with immutability and concealment. Combines standard
|
|
54
|
+
behaviors with `abc.ABCMeta` machinery: abstract method enforcement and
|
|
55
|
+
virtual subclass registration. Mixes with external ABC-based classes
|
|
56
|
+
without metaclass conflicts.
|
|
48
57
|
|
|
49
58
|
### Mutable Variants
|
|
50
59
|
|
|
51
60
|
- `ObjectMutable`: Object with concealment only (no immutability)
|
|
52
61
|
- `DataclassObjectMutable`: DataclassObject with concealment only
|
|
53
|
-
- `
|
|
62
|
+
- `ProtocolMutable`: Protocol with concealment only
|
|
63
|
+
- `DataclassProtocolMutable`: DataclassProtocol with concealment only
|
|
54
64
|
|
|
55
65
|
## Metaclasses
|
|
56
66
|
|
|
@@ -64,27 +74,35 @@ any class that uses this metaclass.
|
|
|
64
74
|
Metaclass that automatically applies `dataclasses.dataclass` during class
|
|
65
75
|
construction. Configurable for `frozen`, `slots`, etc.
|
|
66
76
|
|
|
67
|
-
### `
|
|
77
|
+
### `AbstractClass`
|
|
78
|
+
|
|
79
|
+
Metaclass combining `Class` with `abc.ABCMeta`. Supports abstract method
|
|
80
|
+
enforcement and virtual subclass registration for classes that must mix
|
|
81
|
+
with external ABC-based classes.
|
|
82
|
+
|
|
83
|
+
### `ProtocolClass`
|
|
68
84
|
|
|
69
85
|
Metaclass for protocol classes. Combines `typing.Protocol` with standard
|
|
70
|
-
behaviors.
|
|
86
|
+
behaviors. Descends from `AbstractClass`, unifying the standard metaclass
|
|
87
|
+
taxonomy under `Class`. Dataclass protocol metaclasses
|
|
88
|
+
(`ProtocolDataclass`, `ProtocolDataclassMutable`) additionally apply
|
|
89
|
+
dataclass conversion.
|
|
71
90
|
|
|
72
91
|
### Mutable Variants
|
|
73
92
|
|
|
74
|
-
- `
|
|
75
|
-
|
|
76
|
-
- `ProtocolMutable`: Protocol metaclass with concealment only
|
|
93
|
+
- `DataclassMutable`: Dataclass metaclass whose instances have mutable
|
|
94
|
+
attributes (concealment only)
|
|
77
95
|
|
|
78
96
|
## Decorators
|
|
79
97
|
|
|
80
|
-
### `
|
|
98
|
+
### `with_standard_behaviors`
|
|
81
99
|
|
|
82
100
|
Apply standard behaviors to an existing class.
|
|
83
101
|
|
|
84
102
|
```python
|
|
85
|
-
from classcore.standard import
|
|
103
|
+
from classcore.standard import with_standard_behaviors
|
|
86
104
|
|
|
87
|
-
@
|
|
105
|
+
@with_standard_behaviors()
|
|
88
106
|
class ExistingClass:
|
|
89
107
|
x: int = 10
|
|
90
108
|
```
|
|
@@ -102,10 +120,6 @@ class Point:
|
|
|
102
120
|
y: float
|
|
103
121
|
```
|
|
104
122
|
|
|
105
|
-
### `protocol_with_standard_behaviors`
|
|
106
|
-
|
|
107
|
-
Create a protocol with standard behaviors applied.
|
|
108
|
-
|
|
109
123
|
### `class_factory`
|
|
110
124
|
|
|
111
125
|
Generic factory for creating custom metaclasses with standard behaviors.
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
''' Common constants, imports, and utilities. '''
|
|
22
|
+
|
|
23
|
+
# ruff: noqa: F403, F405
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
from ..__ import *
|
|
27
|
+
from ..nomina import is_public_identifier
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def provide_error_class( name: str ) -> type[ Exception ]:
|
|
31
|
+
''' Produces error class for this package. '''
|
|
32
|
+
match name:
|
|
33
|
+
case 'AttributeImmutability':
|
|
34
|
+
from ..exceptions import AttributeImmutability as error
|
|
35
|
+
case _:
|
|
36
|
+
from ..exceptions import ErrorProvideFailure
|
|
37
|
+
raise ErrorProvideFailure( name, reason = 'Does not exist.' )
|
|
38
|
+
return error
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
mutables_default = ( )
|
|
42
|
+
visibles_default = ( is_public_identifier, )
|
|
43
|
+
|
|
44
|
+
# Standard library ABC machinery attribute names which the class-level
|
|
45
|
+
# immutability behavior permits. The C _abc implementation writes these
|
|
46
|
+
# cache attributes through direct dict access, so they must remain
|
|
47
|
+
# assignable on standard classes. Also used to mark such names as
|
|
48
|
+
# internal in attribute explanations.
|
|
49
|
+
abc_class_mutables = (
|
|
50
|
+
'_abc_cache',
|
|
51
|
+
'_abc_negative_cache',
|
|
52
|
+
'_abc_negative_cache_version',
|
|
53
|
+
'_abc_registry',
|
|
54
|
+
)
|
|
55
|
+
# Standard library typing machinery attribute names which the
|
|
56
|
+
# class-level immutability behavior permits on protocol classes.
|
|
57
|
+
# 'typing' sets these protocol bookkeeping attributes during class
|
|
58
|
+
# decoration: '_is_runtime_protocol' and '__non_callable_proto_members__'
|
|
59
|
+
# in the 'runtime_checkable' decorator, and
|
|
60
|
+
# '__typing_is_deprecated_inherited_runtime_protocol__' (CPython 3.15+)
|
|
61
|
+
# during class decoration and in 'runtime_checkable'. Without this
|
|
62
|
+
# exemption, decorating a runtime protocol which inherits runtime
|
|
63
|
+
# checkability fails. Also used to mark such names as internal in
|
|
64
|
+
# attribute explanations.
|
|
65
|
+
typing_class_mutables = (
|
|
66
|
+
'_is_runtime_protocol',
|
|
67
|
+
'__non_callable_proto_members__',
|
|
68
|
+
'__typing_is_deprecated_inherited_runtime_protocol__', # Python 3.15+
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def is_same_detector(
|
|
73
|
+
detector: cabc.Callable[ [ str ], bool ],
|
|
74
|
+
other: cabc.Callable[ [ str ], bool ], /,
|
|
75
|
+
) -> bool:
|
|
76
|
+
''' Returns whether two detector references are the same detector.
|
|
77
|
+
|
|
78
|
+
Bound methods are the same detector only when both the
|
|
79
|
+
underlying function and the bound instance are identical, so
|
|
80
|
+
distinct namer instances with distinct configured behavior keep
|
|
81
|
+
separate contributions. Plain callables compare by identity.
|
|
82
|
+
'''
|
|
83
|
+
if detector is other: return True
|
|
84
|
+
function = getattr( detector, '__func__', None )
|
|
85
|
+
other_function = getattr( other, '__func__', None )
|
|
86
|
+
if function is None or other_function is None: return False
|
|
87
|
+
return (
|
|
88
|
+
function is other_function
|
|
89
|
+
and getattr( detector, '__self__', None )
|
|
90
|
+
is getattr( other, '__self__', None ) )
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def augment_internal_names(
|
|
94
|
+
cls: type, /, detector: cabc.Callable[ [ str ], bool ]
|
|
95
|
+
) -> None:
|
|
96
|
+
''' Adds an internal-name detector to existing contributions.
|
|
97
|
+
|
|
98
|
+
Private wiring point for machinery mixins: the abstract-base
|
|
99
|
+
metaclass adds the stdlib machinery set after factory
|
|
100
|
+
decoration, without expanding public factory arguments.
|
|
101
|
+
'''
|
|
102
|
+
detectors = getattr( cls, calculate_contribution_name( ), ( ) )
|
|
103
|
+
if any( is_same_detector( detector, d ) for d in detectors ): return
|
|
104
|
+
setattr(
|
|
105
|
+
cls, calculate_contribution_name( ),
|
|
106
|
+
( *detectors, detector ) )
|