Coverage for sources/classcore/standard/modules.py: 100%
28 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-11 02:31 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-11 02:31 +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 nomina as _nomina
30class Module( __.types.ModuleType, _classes.Object ):
31 ''' Modules with attributes immutability and concealment. '''
34def reclassify_modules(
35 attributes: __.typx.Annotated[
36 __.cabc.Mapping[ str, __.typx.Any ] | __.types.ModuleType | str,
37 __.dynadoc.Doc(
38 ''' Module, module name, or dictionary of object attributes. ''' ),
39 ], /, *,
40 attributes_namer: __.typx.Annotated[
41 _nomina.AttributesNamer,
42 __.dynadoc.Doc(
43 ''' Attributes namer function with which to seal class. ''' ),
44 ] = __.calculate_attrname,
45 recursive: __.typx.Annotated[
46 bool, __.dynadoc.Doc( ''' Recursively reclassify package modules? ''' )
47 ] = False,
48 replacement_class: __.typx.Annotated[
49 type[ __.types.ModuleType ],
50 __.dynadoc.Doc( ''' New class for module. ''' ),
51 ] = Module,
52) -> None:
53 # TODO? Ensure correct operation with namespace packages.
54 ''' Reclassifies modules to have attributes concealment and immutability.
56 Can operate on individual modules or entire package hierarchies.
58 Only converts modules within the same package to prevent unintended
59 modifications to external modules.
61 When used with a dictionary, converts any module objects found as
62 values if they belong to the same package.
64 Has no effect on already-reclassified modules.
65 '''
66 if isinstance( attributes, str ):
67 attributes = __.sys.modules[ attributes ]
68 if isinstance( attributes, __.types.ModuleType ):
69 module = attributes
70 attributes = attributes.__dict__
71 else: module = None
72 package_name = (
73 attributes.get( '__package__' ) or attributes.get( '__name__' ) )
74 if not package_name: return
75 for value in attributes.values( ):
76 if not __.inspect.ismodule( value ): continue
77 if not value.__name__.startswith( f"{package_name}." ): continue
78 if recursive:
79 reclassify_modules(
80 value,
81 recursive = True,
82 replacement_class = replacement_class )
83 if isinstance( value, replacement_class ): continue
84 _seal_module( value, attributes_namer, replacement_class )
85 if module and not isinstance( module, replacement_class ):
86 _seal_module( module, attributes_namer, replacement_class )
89def _seal_module(
90 module: __.types.ModuleType,
91 attributes_namer: _nomina.AttributesNamer,
92 replacement_class: type[ __.types.ModuleType ],
93) -> None:
94 behaviors = { _nomina.concealment_label, _nomina.immutability_label }
95 behaviors_name = attributes_namer( 'instance', 'behaviors' )
96 _utilities.setattr0( module, behaviors_name, behaviors )
97 module.__class__ = replacement_class