Coverage for sources/classcore/standard/modules.py: 100%

43 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-07-01 19:09 +0000

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 module classes and reclassifers. ''' 

22 

23 

24from .. import utilities as _utilities 

25from . import __ 

26from . import classes as _classes 

27from . import dynadoc as _dynadoc 

28from . import nomina as _nomina 

29 

30 

31class Module( _classes.Object, __.types.ModuleType ): 

32 ''' Modules with attributes immutability and concealment. ''' 

33 

34 

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. 

54 

55 Applies module docstring generation via Dynadoc introspection, 

56 then reclassifies modules for immutability and concealment. 

57 

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_modules( 

85 module, 

86 attributes_namer = attributes_namer, 

87 recursive = recursive, 

88 replacement_class = replacement_class ) 

89 

90 

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 # TODO? Ensure correct operation with namespace packages. 

112 ''' Reclassifies modules to have attributes concealment and immutability. 

113 

114 Can operate on individual modules or entire package hierarchies. 

115 

116 Only converts modules within the same package to prevent unintended 

117 modifications to external modules. 

118 

119 When used with a dictionary, converts any module objects found as 

120 values if they belong to the same package. 

121 

122 Has no effect on already-reclassified modules. 

123 ''' 

124 if isinstance( attributes, str ): 

125 attributes = __.sys.modules[ attributes ] 

126 if isinstance( attributes, __.types.ModuleType ): 

127 module = attributes 

128 attributes = attributes.__dict__ 

129 else: module = None 

130 package_name = ( 

131 attributes.get( '__package__' ) or attributes.get( '__name__' ) ) 

132 if not package_name: return 

133 for value in attributes.values( ): 

134 if not __.inspect.ismodule( value ): continue 

135 if not value.__name__.startswith( f"{package_name}." ): continue 

136 if recursive: 

137 reclassify_modules( 

138 value, 

139 attributes_namer = attributes_namer, 

140 recursive = True, 

141 replacement_class = replacement_class ) 

142 if isinstance( value, replacement_class ): continue 

143 _seal_module( value, attributes_namer, replacement_class ) 

144 if module and not isinstance( module, replacement_class ): 

145 _seal_module( module, attributes_namer, replacement_class ) 

146 

147 

148def _seal_module( 

149 module: __.types.ModuleType, 

150 attributes_namer: _nomina.AttributesNamer, 

151 replacement_class: type[ __.types.ModuleType ], 

152) -> None: 

153 behaviors = { _nomina.concealment_label, _nomina.immutability_label } 

154 behaviors_name = attributes_namer( 'instance', 'behaviors' ) 

155 module.__class__ = replacement_class 

156 _utilities.setattr0( module, behaviors_name, behaviors )