Coverage for sources/classcore/standard/modules.py: 100%
45 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 23:42 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 23:42 +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 module classes and reclassifers. '''
24from .. import utilities as _utilities
25from . import __
26from . import classes as _classes
27from . import dynadoc as _dynadoc
28from . import nomina as _nomina
31class Module( _classes.Object, __.types.ModuleType ):
32 ''' Modules with attributes immutability and concealment. '''
35def finalize_module( # noqa: PLR0913
36 module: __.typx.Annotated[
37 str | __.types.ModuleType,
38 __.ddoc.Doc( ''' Module or module name to finalize. ''' ),
39 ], /,
40 *fragments: __.ddoc.interfaces.Fragment,
41 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname,
42 dynadoc_introspection: _nomina.DynadocIntrospectionArgument = (
43 _dynadoc.dynadoc_introspection_on_package ),
44 dynadoc_table: _nomina.DynadocTableArgument = __.dictproxy_empty,
45 recursive: __.typx.Annotated[
46 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' )
47 ] = False,
48 replacement_class: __.typx.Annotated[
49 type[ __.types.ModuleType ],
50 __.ddoc.Doc( ''' New class for module. ''' ),
51 ] = Module,
52) -> None:
53 ''' Combines Dynadoc docstring assignment and module reclassification.
55 Applies module docstring generation via Dynadoc introspection,
56 then reclassifies modules for immutability and concealment.
58 When recursive is False, automatically excludes module targets from
59 dynadoc introspection to document only the provided module. When
60 recursive is True, automatically includes module targets so Dynadoc
61 can recursively document all modules.
62 '''
63 module_target = __.ddoc.IntrospectionTargets.Module
64 if recursive:
65 if not ( dynadoc_introspection.targets & module_target ):
66 targets = dynadoc_introspection.targets | module_target
67 introspection = __.ddoc.IntrospectionControl(
68 enable = dynadoc_introspection.enable,
69 class_control = dynadoc_introspection.class_control,
70 module_control = dynadoc_introspection.module_control,
71 limiters = dynadoc_introspection.limiters,
72 targets = targets )
73 else: introspection = dynadoc_introspection
74 elif dynadoc_introspection.targets & module_target:
75 limit = __.ddoc.IntrospectionLimit(
76 targets_exclusions = module_target )
77 introspection = dynadoc_introspection.with_limit( limit )
78 else: introspection = dynadoc_introspection
79 _dynadoc.assign_module_docstring(
80 module,
81 *fragments,
82 introspection = introspection,
83 table = dynadoc_table )
84 _reclassify_module(
85 module,
86 attributes_namer = attributes_namer,
87 recursive = recursive,
88 replacement_class = replacement_class )
91@__.typx.deprecated( "Use 'finalize_module' instead." )
92def reclassify_modules(
93 attributes: __.typx.Annotated[
94 __.cabc.Mapping[ str, __.typx.Any ] | __.types.ModuleType | str,
95 __.ddoc.Doc(
96 ''' Module, module name, or dictionary of object attributes. ''' ),
97 ], /, *,
98 attributes_namer: __.typx.Annotated[
99 _nomina.AttributesNamer,
100 __.ddoc.Doc(
101 ''' Attributes namer function with which to seal class. ''' ),
102 ] = __.calculate_attrname,
103 recursive: __.typx.Annotated[
104 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' )
105 ] = False,
106 replacement_class: __.typx.Annotated[
107 type[ __.types.ModuleType ],
108 __.ddoc.Doc( ''' New class for module. ''' ),
109 ] = Module,
110) -> None:
111 ''' Reclassifies modules to have attributes concealment and immutability.
113 Can operate on individual modules or entire package hierarchies.
115 Only converts modules within the same package to prevent unintended
116 modifications to external modules.
118 When used with a dictionary, converts any module objects found as
119 values if they belong to the same package.
121 Has no effect on already-reclassified modules.
122 '''
123 _reclassify_module(
124 attributes,
125 attributes_namer = attributes_namer,
126 recursive = recursive,
127 replacement_class = replacement_class )
130def _reclassify_module(
131 attributes: __.typx.Annotated[
132 __.cabc.Mapping[ str, __.typx.Any ] | __.types.ModuleType | str,
133 __.ddoc.Doc(
134 ''' Module, module name, or dictionary of object attributes. ''' ),
135 ], /, *,
136 attributes_namer: __.typx.Annotated[
137 _nomina.AttributesNamer,
138 __.ddoc.Doc(
139 ''' Attributes namer function with which to seal class. ''' ),
140 ] = __.calculate_attrname,
141 recursive: __.typx.Annotated[
142 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' )
143 ] = False,
144 replacement_class: __.typx.Annotated[
145 type[ __.types.ModuleType ],
146 __.ddoc.Doc( ''' New class for module. ''' ),
147 ] = Module,
148) -> None:
149 # TODO? Ensure correct operation with namespace packages.
150 ''' Core implementation for module reclassification.
152 Reclassifies modules to have attributes concealment and immutability.
153 Can operate on individual modules or entire package hierarchies.
155 Only converts modules within the same package to prevent unintended
156 modifications to external modules.
158 When used with a dictionary, converts any module objects found as
159 values if they belong to the same package.
161 Has no effect on already-reclassified modules.
162 '''
163 if isinstance( attributes, str ):
164 attributes = __.sys.modules[ attributes ]
165 if isinstance( attributes, __.types.ModuleType ):
166 module = attributes
167 attributes = attributes.__dict__
168 else: module = None
169 package_name = (
170 attributes.get( '__package__' ) or attributes.get( '__name__' ) )
171 if not package_name: return
172 for value in attributes.values( ):
173 if not __.inspect.ismodule( value ): continue
174 if not value.__name__.startswith( f"{package_name}." ): continue
175 if recursive:
176 _reclassify_module(
177 value,
178 attributes_namer = attributes_namer,
179 recursive = True,
180 replacement_class = replacement_class )
181 if isinstance( value, replacement_class ): continue
182 _seal_module( value, attributes_namer, replacement_class )
183 if module and not isinstance( module, replacement_class ):
184 _seal_module( module, attributes_namer, replacement_class )
187def _seal_module(
188 module: __.types.ModuleType,
189 attributes_namer: _nomina.AttributesNamer,
190 replacement_class: type[ __.types.ModuleType ],
191) -> None:
192 behaviors = { _nomina.concealment_label, _nomina.immutability_label }
193 behaviors_name = attributes_namer( 'instance', 'behaviors' )
194 module.__class__ = replacement_class
195 _utilities.setattr0( module, behaviors_name, behaviors )