Coverage for sources/accretive/__.py: 100%
64 statements
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-07 15:48 +0000
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-07 15:48 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
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#============================================================================#
21''' Common constants, imports, and utilities. '''
23# ruff: noqa: F401
24# pylint: disable=unused-import
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)
38from . import _annotations as _a
41_no_value = object( )
44class ClassConcealerExtension( type ):
45 ''' Conceals class attributes according to some criteria.
47 By default, public attributes are displayed.
48 '''
50 _class_attribute_visibility_includes_: _a.Collection[ str ] = frozenset( )
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_ ) )
59class ConcealerExtension:
60 ''' Conceals instance attributes according to some criteria.
62 By default, public attributes are displayed.
63 '''
65 _attribute_visibility_includes_: _a.Collection[ str ] = frozenset( )
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_ ) )
74class CoreDictionary( ConcealerExtension, dict ): # type: ignore[type-arg]
75 ''' Accretive subclass of :py:class:`dict`.
77 Can be used as an instance dictionary.
79 Prevents attempts to mutate dictionary via inherited interface.
80 '''
82 def __init__(
83 self,
84 *iterables: _a.DictionaryPositionalArgument,
85 **entries: _a.DictionaryNominativeArgument
86 ):
87 super( ).__init__( )
88 self.update( *iterables, **entries )
90 def __delitem__( self, key: _a.Hashable ) -> None:
91 from .exceptions import IndelibleEntryError
92 raise IndelibleEntryError( key )
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 )
99 def clear( self ) -> _a.Never:
100 ''' Raises exception. Cannot clear indelible entries. '''
101 from .exceptions import InvalidOperationError
102 raise InvalidOperationError( 'clear' )
104 def copy( self ) -> _a.Self:
105 ''' Provides fresh copy of dictionary. '''
106 return type( self )( self )
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' )
115 def popitem( self ) -> _a.Never:
116 ''' Raises exception. Cannot pop indelible entry. '''
117 from .exceptions import InvalidOperationError
118 raise InvalidOperationError( 'popitem' )
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
139class Docstring( str ):
140 ''' Dedicated docstring container. '''
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__}"
149def discover_public_attributes(
150 attributes: _a.Mapping[ str, _a.Any ]
151) -> _a.Tuple[ str, ... ]:
152 ''' Discovers public attributes of certain types from dictionary.
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 ) ) )
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 )
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
187__all__ = ( )