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

47 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-23 13: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 excludes: __.typx.Annotated[ 

46 __.typx.Optional[ __.cabc.MutableSet[ __.types.ModuleType ] ], 

47 __.ddoc.Doc( ''' Modules to exclude from reclassification. ''' ), 

48 ] = None, 

49 recursive: __.typx.Annotated[ 

50 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' ) 

51 ] = False, 

52 replacement_class: __.typx.Annotated[ 

53 type[ __.types.ModuleType ], 

54 __.ddoc.Doc( ''' New class for module. ''' ), 

55 ] = Module, 

56) -> None: 

57 ''' Combines Dynadoc docstring assignment and module reclassification. 

58 

59 Applies module docstring generation via Dynadoc introspection, 

60 then reclassifies modules for immutability and concealment. 

61 

62 When recursive is False, automatically excludes module targets from 

63 dynadoc introspection to document only the provided module. When 

64 recursive is True, automatically includes module targets so Dynadoc 

65 can recursively document all modules. 

66 ''' 

67 module_target = __.ddoc.IntrospectionTargets.Module 

68 if recursive: 

69 if not ( dynadoc_introspection.targets & module_target ): 

70 targets = dynadoc_introspection.targets | module_target 

71 introspection = __.ddoc.IntrospectionControl( 

72 enable = dynadoc_introspection.enable, 

73 class_control = dynadoc_introspection.class_control, 

74 module_control = dynadoc_introspection.module_control, 

75 limiters = dynadoc_introspection.limiters, 

76 targets = targets ) 

77 else: introspection = dynadoc_introspection 

78 elif dynadoc_introspection.targets & module_target: 

79 limit = __.ddoc.IntrospectionLimit( 

80 targets_exclusions = module_target ) 

81 introspection = dynadoc_introspection.with_limit( limit ) 

82 else: introspection = dynadoc_introspection 

83 _dynadoc.assign_module_docstring( 

84 module, 

85 *fragments, 

86 introspection = introspection, 

87 table = dynadoc_table ) 

88 _reclassify_module( 

89 module, 

90 attributes_namer = attributes_namer, 

91 excludes = excludes, recursive = recursive, 

92 replacement_class = replacement_class ) 

93 

94 

95@__.typx.deprecated( "Use 'finalize_module' instead." ) 

96def reclassify_modules( 

97 attributes: __.typx.Annotated[ 

98 __.cabc.Mapping[ str, __.typx.Any ] | __.types.ModuleType | str, 

99 __.ddoc.Doc( 

100 ''' Module, module name, or dictionary of object attributes. ''' ), 

101 ], /, *, 

102 attributes_namer: __.typx.Annotated[ 

103 _nomina.AttributesNamer, 

104 __.ddoc.Doc( 

105 ''' Attributes namer function with which to seal class. ''' ), 

106 ] = __.calculate_attrname, 

107 excludes: __.typx.Annotated[ 

108 __.typx.Optional[ __.cabc.MutableSet[ __.types.ModuleType ] ], 

109 __.ddoc.Doc( ''' Modules to exclude from reclassification. ''' ), 

110 ] = None, 

111 recursive: __.typx.Annotated[ 

112 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' ) 

113 ] = False, 

114 replacement_class: __.typx.Annotated[ 

115 type[ __.types.ModuleType ], 

116 __.ddoc.Doc( ''' New class for module. ''' ), 

117 ] = Module, 

118) -> None: 

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

120 

121 Can operate on individual modules or entire package hierarchies. 

122 

123 Only converts modules within the same package to prevent unintended 

124 modifications to external modules. 

125 

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

127 values if they belong to the same package. 

128 

129 Has no effect on already-reclassified modules. 

130 ''' 

131 _reclassify_module( 

132 attributes, 

133 attributes_namer = attributes_namer, 

134 excludes = excludes, recursive = recursive, 

135 replacement_class = replacement_class ) 

136 

137 

138def _reclassify_module( # noqa: C901,PLR0912 

139 attributes: __.typx.Annotated[ 

140 __.cabc.Mapping[ str, __.typx.Any ] | __.types.ModuleType | str, 

141 __.ddoc.Doc( 

142 ''' Module, module name, or dictionary of object attributes. ''' ), 

143 ], /, *, 

144 attributes_namer: __.typx.Annotated[ 

145 _nomina.AttributesNamer, 

146 __.ddoc.Doc( 

147 ''' Attributes namer function with which to seal class. ''' ), 

148 ] = __.calculate_attrname, 

149 excludes: __.typx.Annotated[ 

150 __.typx.Optional[ __.cabc.MutableSet[ __.types.ModuleType ] ], 

151 __.ddoc.Doc( ''' Modules to exclude from reclassification. ''' ), 

152 ] = None, 

153 recursive: __.typx.Annotated[ 

154 bool, __.ddoc.Doc( ''' Recursively reclassify package modules? ''' ) 

155 ] = False, 

156 replacement_class: __.typx.Annotated[ 

157 type[ __.types.ModuleType ], 

158 __.ddoc.Doc( ''' New class for module. ''' ), 

159 ] = Module, 

160) -> None: 

161 # TODO? Ensure correct operation with namespace packages. 

162 ''' Core implementation for module reclassification. 

163 

164 Reclassifies modules to have attributes concealment and immutability. 

165 Can operate on individual modules or entire package hierarchies. 

166 

167 Only converts modules within the same package to prevent unintended 

168 modifications to external modules. 

169 

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

171 values if they belong to the same package. 

172 

173 Has no effect on already-reclassified modules. 

174 ''' 

175 if isinstance( attributes, str ): 

176 attributes = __.sys.modules[ attributes ] 

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

178 module = attributes 

179 if excludes and module in excludes: return 

180 attributes = module.__dict__ 

181 else: module = None 

182 if excludes is None: excludes = set( ) 

183 if module: excludes.add( module ) 

184 package_name = ( 

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

186 if not package_name: return 

187 for value in attributes.values( ): 

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

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

190 if isinstance( value, replacement_class ): continue 

191 if recursive: 

192 _reclassify_module( 

193 value, 

194 attributes_namer = attributes_namer, 

195 excludes = excludes, recursive = True, 

196 replacement_class = replacement_class ) 

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

198 _seal_module( module, attributes_namer, replacement_class ) 

199 

200 

201def _seal_module( 

202 module: __.types.ModuleType, 

203 attributes_namer: _nomina.AttributesNamer, 

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

205) -> None: 

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

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

208 module.__class__ = replacement_class 

209 _utilities.setattr0( module, behaviors_name, behaviors )