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

45 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-07-02 03:53 +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_module( 

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 ''' Reclassifies modules to have attributes concealment and immutability. 

112 

113 Can operate on individual modules or entire package hierarchies. 

114 

115 Only converts modules within the same package to prevent unintended 

116 modifications to external modules. 

117 

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

119 values if they belong to the same package. 

120 

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 ) 

128 

129 

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. 

151 

152 Reclassifies modules to have attributes concealment and immutability. 

153 Can operate on individual modules or entire package hierarchies. 

154 

155 Only converts modules within the same package to prevent unintended 

156 modifications to external modules. 

157 

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

159 values if they belong to the same package. 

160 

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 ) 

185 

186 

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 )