Coverage for sources/accretive/__/dictionaries.py: 100%
30 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-01 20:09 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-01 20:09 +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''' Internal dictionary. '''
23# pylint: disable=unused-import
24# ruff: noqa: F401
27# TODO: Consider a dictionary factory to allow 'mutables' closure
28# to be referenced in the '__setitem__' and '__delitem__' methods.
31from __future__ import annotations
33from . import imports as __
34from . import immutables as _immutables
37_H = __.typx.TypeVar( '_H' )
38_V = __.typx.TypeVar( '_V' )
41class AccretiveDictionary(
42 _immutables.ConcealerExtension,
43 dict[ _H, _V ],
44 __.typx.Generic[ _H, _V ],
45):
46 ''' Accretive subclass of :py:class:`dict`.
48 Can be used as an instance dictionary.
50 Prevents attempts to mutate dictionary via inherited interface.
51 '''
53 def __init__(
54 self,
55 *iterables: __.DictionaryPositionalArgument[ _H, _V ],
56 **entries: __.DictionaryNominativeArgument[ _V ],
57 ):
58 super( ).__init__( )
59 self.update( *iterables, **entries )
61 def __delitem__( self, key: _H ) -> None:
62 from .exceptions import EntryImmutabilityError
63 raise EntryImmutabilityError( key )
65 def __setitem__( self, key: _H, value: _V ) -> None:
66 from .exceptions import EntryImmutabilityError
67 if key in self: raise EntryImmutabilityError( key )
68 super( ).__setitem__( key, value )
70 def clear( self ) -> __.typx.Never:
71 ''' Raises exception. Cannot clear immutable entries. '''
72 from .exceptions import OperationInvalidity
73 raise OperationInvalidity( 'clear' )
75 def copy( self ) -> __.typx.Self:
76 ''' Provides fresh copy of dictionary. '''
77 return type( self )( self )
79 def pop( # pylint: disable=unused-argument
80 self, key: _H, default: __.Absential[ _V ] = __.absent
81 ) -> __.typx.Never:
82 ''' Raises exception. Cannot pop immutable entry. '''
83 from .exceptions import OperationInvalidity
84 raise OperationInvalidity( 'pop' )
86 def popitem( self ) -> __.typx.Never:
87 ''' Raises exception. Cannot pop immutable entry. '''
88 from .exceptions import OperationInvalidity
89 raise OperationInvalidity( 'popitem' )
91 def update( # type: ignore
92 self,
93 *iterables: __.DictionaryPositionalArgument[ _H, _V ],
94 **entries: __.DictionaryNominativeArgument[ _V ],
95 ) -> None:
96 ''' Adds new entries as a batch. '''
97 from itertools import chain
98 # Add values in order received, enforcing no alteration.
99 for indicator, value in chain.from_iterable( map( # type: ignore
100 lambda element: ( # pyright: ignore
101 element.items( )
102 if isinstance( element, __.cabc.Mapping )
103 else element
104 ),
105 ( *iterables, entries )
106 ) ): self[ indicator ] = value # type: ignore