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

29 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-01 21:58 +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 __future__ import annotations 

25 

26from .. import utilities as _utilities 

27from . import __ 

28from . import behaviors as _behaviors 

29from . import classes as _classes 

30from . import nomina as _nomina 

31 

32 

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

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

35 

36 

37def reclassify_modules( 

38 attributes: __.typx.Annotated[ 

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

40 __.typx.Doc( 

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

42 ], /, *, 

43 attributes_namer: __.typx.Annotated[ 

44 _nomina.AttributesNamer, 

45 __.typx.Doc( 

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

47 ] = __.calculate_attrname, 

48 recursive: __.typx.Annotated[ 

49 bool, __.typx.Doc( 'Recursively reclassify package modules?' ) 

50 ] = False, 

51) -> None: 

52 # TODO? Ensure correct operation with namespace packages. 

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

54 

55 Can operate on individual modules or entire package hierarchies. 

56 

57 Notes 

58 ----- 

59 * Only converts modules within the same package to prevent unintended 

60 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. 

63 * Has no effect on already-reclassified modules. 

64 ''' 

65 if isinstance( attributes, str ): 

66 attributes = __.sys.modules[ attributes ] 

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

68 module = attributes 

69 attributes = attributes.__dict__ 

70 else: module = None 

71 package_name = ( 

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

73 if not package_name: return 

74 for value in attributes.values( ): 

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

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

77 if recursive: reclassify_modules( value, recursive = True ) 

78 if isinstance( value, Module ): continue 

79 _seal_module( value, attributes_namer ) 

80 if module and not isinstance( module, Module ): 

81 _seal_module( module, attributes_namer ) 

82 

83 

84def _seal_module( 

85 module: __.types.ModuleType, attributes_namer: _nomina.AttributesNamer 

86) -> None: 

87 behaviors = { _behaviors.concealment_label, _behaviors.immutability_label } 

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

89 _utilities.setattr0( module, behaviors_name, behaviors ) 

90 module.__class__ = Module