Coverage for sources/mimeogram/__/dictedits.py: 98%
40 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-22 03:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-22 03:16 +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''' Support for edits on nested dictionaries. '''
22# TODO: Independent package.
23# TODO: Copying edits, not just in-place edits.
26from . import imports as __
27from . import exceptions as _exceptions
30class Edit( # pylint: disable=invalid-metaclass
31 __.typx.Protocol,
32 metaclass = __.ImmutableStandardProtocolDataclass,
33 decorators = ( __.standard_dataclass, __.typx.runtime_checkable ),
34):
35 ''' Base representation of an edit to configuration. '''
37 address: __.cabc.Sequence[ str ]
39 @__.abc.abstractmethod
40 def __call__( self, configuration: __.NominativeDictionary ) -> None:
41 ''' Performs edit. '''
42 raise NotImplementedError
44 def dereference(
45 self, configuration: __.NominativeDictionary
46 ) -> __.typx.Any:
47 ''' Dereferences value at address in configuration. '''
48 configuration_ = configuration
49 for part in self.address:
50 if part not in configuration_:
51 raise (
52 _exceptions.AddressLocateFailure(
53 'configuration dictionary', self.address, part ) )
54 configuration_ = configuration_[ part ]
55 return configuration_
57 def inject(
58 self, configuration: __.NominativeDictionary, value: __.typx.Any
59 ) -> None:
60 ''' Injects value at address in configuration. '''
61 configuration_ = configuration
62 for part in self.address[ : -1 ]:
63 if part not in configuration_: configuration_[ part ] = { }
64 configuration_ = configuration_[ part ]
65 configuration_[ self.address[ -1 ] ] = value
68class ElementsEntryEdit( Edit, decorators = ( __.standard_dataclass, ) ):
69 ''' Applies entry edit to every matching dictionary in array. '''
71 editee: tuple[ str, __.typx.Any ]
72 identifier: __.typx.Optional[ tuple[ str, __.typx.Any ] ] = None
74 def __call__( self, configuration: __.NominativeDictionary ) -> None:
75 array = self.dereference( configuration )
76 if self.identifier:
77 iname, ivalue = self.identifier # pylint: disable=unpacking-non-sequence
78 else: iname, ivalue = None, None
79 ename, evalue = self.editee
80 for element in array:
81 if iname:
82 if iname not in element:
83 raise _exceptions.EntryAssertionFailure(
84 'configuration array element', iname )
85 if ivalue != element[ iname ]: continue
86 element[ ename ] = evalue
89class SimpleEdit( Edit, decorators = ( __.standard_dataclass, ) ):
90 ''' Applies edit to single entity. '''
92 value: __.typx.Any
94 def __call__( self, configuration: __.NominativeDictionary ) -> None:
95 self.inject( configuration, self.value )
98Edits: __.typx.TypeAlias = __.cabc.Iterable[ Edit ]