Coverage for sources/frigid/__/dictionaries.py: 100%
35 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-05 03:33 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-05 03:33 +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 ImmutableDictionary(
42 _immutables.ConcealerExtension,
43 dict[ _H, _V ],
44 __.typx.Generic[ _H, _V ],
45):
46 ''' Immutable 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 self._behaviors_: set[ str ] = set( )
59 super( ).__init__( )
60 from itertools import chain
61 # Add values in order received, enforcing no alteration.
62 for indicator, value in chain.from_iterable( map( # type: ignore
63 lambda element: ( # type: ignore
64 element.items( )
65 if isinstance( element, __.cabc.Mapping )
66 else element
67 ),
68 ( *iterables, entries )
69 ) ): self[ indicator ] = value # type: ignore
70 self._behaviors_.add( _immutables.behavior_label )
72 def __delitem__( self, key: _H ) -> None:
73 from .exceptions import EntryImmutabilityError
74 raise EntryImmutabilityError( key )
76 def __setitem__( self, key: _H, value: _V ) -> None:
77 from .exceptions import EntryImmutabilityError
78 default: set[ str ] = set( )
79 if _immutables.behavior_label in getattr(
80 self, '_behaviors_', default
81 ): raise EntryImmutabilityError( key )
82 if key in self: raise EntryImmutabilityError( key )
83 super( ).__setitem__( key, value )
85 def clear( self ) -> __.typx.Never:
86 ''' Raises exception. Cannot clear immutable entries. '''
87 from .exceptions import OperationInvalidity
88 raise OperationInvalidity( 'clear' )
90 def copy( self ) -> __.typx.Self:
91 ''' Provides fresh copy of dictionary. '''
92 return type( self )( self )
94 def pop( # pylint: disable=unused-argument
95 self, key: _H, default: __.Absential[ _V ] = __.absent
96 ) -> __.typx.Never:
97 ''' Raises exception. Cannot pop immutable entry. '''
98 from .exceptions import OperationInvalidity
99 raise OperationInvalidity( 'pop' )
101 def popitem( self ) -> __.typx.Never:
102 ''' Raises exception. Cannot pop immutable entry. '''
103 from .exceptions import OperationInvalidity
104 raise OperationInvalidity( 'popitem' )
106 def update( # type: ignore
107 self, # pylint: disable=unused-argument
108 *iterables: __.DictionaryPositionalArgument[ _H, _V ],
109 **entries: __.DictionaryNominativeArgument[ _V ],
110 ) -> None:
111 ''' Raises exception. Cannot perform mass update. '''
112 from .exceptions import OperationInvalidity
113 raise OperationInvalidity( 'update' )