Coverage for sources/accretive/__.py: 100%

64 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-06 20:59 +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''' Common constants, imports, and utilities. ''' 

22 

23# ruff: noqa: F401 

24# pylint: disable=unused-import 

25 

26 

27from abc import ABCMeta as ABCFactory 

28from collections.abc import Mapping as AbstractDictionary 

29from functools import partial as partial_function 

30from inspect import cleandoc as clean_docstring 

31from sys import modules 

32from types import ( 

33 MappingProxyType as DictionaryProxy, 

34 ModuleType as Module, 

35 SimpleNamespace, 

36) 

37 

38from . import _annotations as _a 

39 

40 

41_no_value = object( ) 

42 

43 

44class ClassConcealerExtension( type ): 

45 ''' Conceals class attributes according to some criteria. 

46 

47 By default, public attributes are displayed. 

48 ''' 

49 

50 _class_attribute_visibility_includes_: _a.Collection[ str ] = frozenset( ) 

51 

52 def __dir__( class_ ) -> _a.Tuple[ str, ... ]: 

53 return tuple( sorted( 

54 name for name in super( ).__dir__( ) 

55 if not name.startswith( '_' ) 

56 or name in class_._class_attribute_visibility_includes_ ) ) 

57 

58 

59class ConcealerExtension: 

60 ''' Conceals instance attributes according to some criteria. 

61 

62 By default, public attributes are displayed. 

63 ''' 

64 

65 _attribute_visibility_includes_: _a.Collection[ str ] = frozenset( ) 

66 

67 def __dir__( self ) -> _a.Tuple[ str, ... ]: 

68 return tuple( sorted( 

69 name for name in super( ).__dir__( ) 

70 if not name.startswith( '_' ) 

71 or name in self._attribute_visibility_includes_ ) ) 

72 

73 

74class CoreDictionary( ConcealerExtension, dict ): # type: ignore[type-arg] 

75 ''' Accretive subclass of :py:class:`dict`. 

76 

77 Can be used as an instance dictionary. 

78 

79 Prevents attempts to mutate dictionary via inherited interface. 

80 ''' 

81 

82 def __init__( 

83 self, 

84 *iterables: _a.DictionaryPositionalArgument, 

85 **entries: _a.DictionaryNominativeArgument 

86 ): 

87 super( ).__init__( ) 

88 self.update( *iterables, **entries ) 

89 

90 def __delitem__( self, key: _a.Hashable ) -> None: 

91 from .exceptions import IndelibleEntryError 

92 raise IndelibleEntryError( key ) 

93 

94 def __setitem__( self, key: _a.Hashable, value: _a.Any ) -> None: 

95 from .exceptions import IndelibleEntryError 

96 if key in self: raise IndelibleEntryError( key ) 

97 super( ).__setitem__( key, value ) 

98 

99 def clear( self ) -> _a.Never: 

100 ''' Raises exception. Cannot clear indelible entries. ''' 

101 from .exceptions import InvalidOperationError 

102 raise InvalidOperationError( 'clear' ) 

103 

104 def copy( self ) -> _a.Self: 

105 ''' Provides fresh copy of dictionary. ''' 

106 return type( self )( self ) 

107 

108 def pop( # pylint: disable=unused-argument 

109 self, key: _a.Hashable, default: _a.Any = _no_value 

110 ) -> _a.Never: 

111 ''' Raises exception. Cannot pop indelible entry. ''' 

112 from .exceptions import InvalidOperationError 

113 raise InvalidOperationError( 'pop' ) 

114 

115 def popitem( self ) -> _a.Never: 

116 ''' Raises exception. Cannot pop indelible entry. ''' 

117 from .exceptions import InvalidOperationError 

118 raise InvalidOperationError( 'popitem' ) 

119 

120 def update( # type: ignore[override] 

121 self, 

122 *iterables: _a.DictionaryPositionalArgument, 

123 **entries: _a.DictionaryNominativeArgument 

124 ) -> _a.Self: 

125 ''' Adds new entries as a batch. ''' 

126 from itertools import chain 

127 # Add values in order received, enforcing no alteration. 

128 for indicator, value in chain.from_iterable( map( 

129 lambda element: ( 

130 element.items( ) 

131 if isinstance( element, AbstractDictionary ) 

132 else element 

133 ), 

134 ( *iterables, entries ) 

135 ) ): self[ indicator ] = value 

136 return self 

137 

138 

139class Docstring( str ): 

140 ''' Dedicated docstring container. ''' 

141 

142 

143def discover_fqname( obj: _a.Any ) -> str: 

144 ''' Discovers fully-qualified name for class of instance. ''' 

145 class_ = type( obj ) 

146 return f"{class_.__module__}.{class_.__qualname__}" 

147 

148 

149def discover_public_attributes( 

150 attributes: _a.Mapping[ str, _a.Any ] 

151) -> _a.Tuple[ str, ... ]: 

152 ''' Discovers public attributes of certain types from dictionary. 

153 

154 By default, callables, including classes, are discovered. 

155 ''' 

156 return tuple( sorted( 

157 name for name, attribute in attributes.items( ) 

158 if not name.startswith( '_' ) and callable( attribute ) ) ) 

159 

160 

161def generate_docstring( 

162 *fragment_ids: _a.Union[ _a.Type, Docstring, str ] 

163) -> str: 

164 ''' Sews together docstring fragments into clean docstring. ''' 

165 from inspect import cleandoc, getdoc, isclass 

166 from ._docstrings import TABLE 

167 fragments = [ ] 

168 for fragment_id in fragment_ids: 

169 if isclass( fragment_id ): fragment = getdoc( fragment_id ) or '' 

170 elif isinstance( fragment_id, Docstring ): fragment = fragment_id 

171 else: fragment = TABLE[ fragment_id ] 

172 fragments.append( cleandoc( fragment ) ) 

173 return '\n\n'.join( fragments ) 

174 

175 

176def reclassify_modules( 

177 attributes: _a.Mapping[ str, _a.Any ], 

178 to_class: _a.Type[ Module ] 

179) -> None: 

180 ''' Reclassifies modules in dictionary with custom module type. ''' 

181 for attribute in attributes.values( ): 

182 if not isinstance( attribute, Module ): continue 

183 if isinstance( attribute, to_class ): continue 

184 attribute.__class__ = to_class 

185 

186 

187__all__ = ( )