Coverage for sources/frigid/__/dictionaries.py: 100%

35 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-07-02 16:24 +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''' Internal dictionary. ''' 

22 

23 

24# TODO: Consider a dictionary factory to allow 'mutables' closure 

25# to be referenced in the '__setitem__' and '__delitem__' methods. 

26 

27 

28from . import imports as __ 

29from . import nomina as _nomina 

30 

31 

32_H = __.typx.TypeVar( '_H' ) 

33_V = __.typx.TypeVar( '_V' ) 

34 

35 

36_immutability_label = 'immutability' 

37 

38 

39class ImmutableDictionary( 

40 dict[ _H, _V ], 

41 __.ccstd.Object, 

42 __.typx.Generic[ _H, _V ], 

43): 

44 ''' Immutable subclass of :py:class:`dict`. 

45 

46 Can be used as an instance dictionary. 

47 

48 Prevents attempts to mutate dictionary via inherited interface. 

49 ''' 

50 

51 def __init__( 

52 self, 

53 *iterables: _nomina.DictionaryPositionalArgument[ _H, _V ], 

54 **entries: _nomina.DictionaryNominativeArgument[ _V ], 

55 ): 

56 self._behaviors_: set[ str ] = set( ) 

57 super( ).__init__( ) 

58 from itertools import chain 

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

60 for indicator, value in chain.from_iterable( map( # pyright: ignore 

61 lambda element: ( # pyright: ignore 

62 element.items( ) 

63 if isinstance( element, __.cabc.Mapping ) 

64 else element 

65 ), 

66 ( *iterables, entries ) 

67 ) ): self[ indicator ] = value # pyright: ignore 

68 self._behaviors_.add( _immutability_label ) 

69 

70 def __delitem__( self, key: _H ) -> None: 

71 from .exceptions import EntryImmutability 

72 raise EntryImmutability( key ) 

73 

74 def __setitem__( self, key: _H, value: _V ) -> None: 

75 from .exceptions import EntryImmutability 

76 default: set[ str ] = set( ) 

77 if _immutability_label in getattr( 

78 self, '_behaviors_', default 

79 ): raise EntryImmutability( key ) 

80 if key in self: raise EntryImmutability( key ) 

81 super( ).__setitem__( key, value ) 

82 

83 def clear( self ) -> __.typx.Never: 

84 ''' Raises exception. Cannot clear immutable entries. ''' 

85 from .exceptions import OperationInvalidity 

86 raise OperationInvalidity( 'clear' ) 

87 

88 def copy( self ) -> __.typx.Self: 

89 ''' Provides fresh copy of dictionary. ''' 

90 return type( self )( self ) 

91 

92 def pop( # pyright: ignore 

93 self, key: _H, default: __.Absential[ _V ] = __.absent 

94 ) -> __.typx.Never: 

95 ''' Raises exception. Cannot pop immutable entry. ''' 

96 from .exceptions import OperationInvalidity 

97 raise OperationInvalidity( 'pop' ) 

98 

99 def popitem( self ) -> __.typx.Never: 

100 ''' Raises exception. Cannot pop immutable entry. ''' 

101 from .exceptions import OperationInvalidity 

102 raise OperationInvalidity( 'popitem' ) 

103 

104 def update( # pyright: ignore 

105 self, 

106 *iterables: _nomina.DictionaryPositionalArgument[ _H, _V ], 

107 **entries: _nomina.DictionaryNominativeArgument[ _V ], 

108 ) -> None: 

109 ''' Raises exception. Cannot perform mass update. ''' 

110 from .exceptions import OperationInvalidity 

111 raise OperationInvalidity( 'update' )