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

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# pylint: disable=unused-import 

24# ruff: noqa: F401 

25 

26 

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

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

29 

30 

31from __future__ import annotations 

32 

33from . import imports as __ 

34from . import immutables as _immutables 

35 

36 

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

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

39 

40 

41class ImmutableDictionary( 

42 _immutables.ConcealerExtension, 

43 dict[ _H, _V ], 

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

45): 

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

47 

48 Can be used as an instance dictionary. 

49 

50 Prevents attempts to mutate dictionary via inherited interface. 

51 ''' 

52 

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 ) 

71 

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

73 from .exceptions import EntryImmutabilityError 

74 raise EntryImmutabilityError( key ) 

75 

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 ) 

84 

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

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

87 from .exceptions import OperationInvalidity 

88 raise OperationInvalidity( 'clear' ) 

89 

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

91 ''' Provides fresh copy of dictionary. ''' 

92 return type( self )( self ) 

93 

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' ) 

100 

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

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

103 from .exceptions import OperationInvalidity 

104 raise OperationInvalidity( 'popitem' ) 

105 

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' )