Coverage for sources/accretive/dictionaries.py: 100%
72 statements
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-06 17:17 +0000
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-06 17:17 +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''' Accretive dictionaries. '''
24from . import __
25from . import _annotations as _a
26from . import classes as _classes
27from . import objects as _objects
30class _Dictionary( # type: ignore
31 __.CoreDictionary, metaclass = _classes.Class
32): pass
35_no_value: object = object( )
38class Dictionary( _objects.Object ): # pylint: disable=eq-without-hash
39 ''' Accretive dictionary. '''
41 __slots__ = ( '_data_', )
43 _data_: _Dictionary
45 def __init__(
46 self,
47 *iterables: _a.DictionaryPositionalArgument,
48 **entries: _a.DictionaryNominativeArgument,
49 ) -> None:
50 self._data_ = _Dictionary( *iterables, **entries )
51 super( ).__init__( )
53 def __iter__( self ) -> _a.Iterator[ _a.Hashable ]:
54 return iter( self._data_ )
56 def __len__( self ) -> int:
57 return len( self._data_ )
59 def __repr__( self ) -> str:
60 return "{fqname}( {contents} )".format(
61 fqname = __.discover_fqname( self ),
62 contents = str( self._data_ ) )
64 def __str__( self ) -> str:
65 return str( self._data_ )
67 def __contains__( self, key: _a.Hashable ) -> bool:
68 return key in self._data_
70 def __delitem__( self, key: _a.Hashable ) -> None:
71 from .exceptions import IndelibleEntryError
72 raise IndelibleEntryError( key )
74 def __getitem__( self, key: _a.Hashable ) -> _a.Any:
75 return self._data_[ key ]
77 def __setitem__( self, key: _a.Hashable, value: _a.Any ) -> None:
78 self._data_[ key ] = value
80 def __eq__( self, other: _a.Any ) -> _a.ComparisonResult:
81 if isinstance( other, __.AbstractDictionary ):
82 return self._data_ == other
83 return NotImplemented
85 def __ne__( self, other: _a.Any ) -> _a.ComparisonResult:
86 if isinstance( other, __.AbstractDictionary ):
87 return self._data_ != other
88 return NotImplemented
90 def copy( self ) -> _a.Self:
91 ''' Provides fresh copy of dictionary. '''
92 return type( self )( self )
94 def get(
95 self, key: _a.Hashable, default: _a.Any = _no_value
96 ) -> _a.Annotation[
97 _a.Any,
98 _a.Doc(
99 'Value of entry, if it exists. '
100 'Else, supplied default value or ``None``.' )
101 ]:
102 ''' Retrieves entry associated with key, if it exists. '''
103 if _no_value is default: return self._data_.get( key )
104 return self._data_.get( key, default )
106 def update(
107 self,
108 *iterables: _a.DictionaryPositionalArgument,
109 **entries: _a.DictionaryNominativeArgument,
110 ) -> _a.Self:
111 ''' Adds new entries as a batch. '''
112 self._data_.update( *iterables, **entries )
113 return self
115 def keys( self ) -> _a.KeysView[ _a.Hashable ]:
116 ''' Provides iterable view over dictionary keys. '''
117 return self._data_.keys( )
119 def items( self ) -> _a.ItemsView[ _a.Hashable, _a.Any ]:
120 ''' Provides iterable view over dictionary items. '''
121 return self._data_.items( )
123 def values( self ) -> _a.ValuesView[ _a.Any ]:
124 ''' Provides iterable view over dictionary values. '''
125 return self._data_.values( )
127Dictionary.__doc__ = __.generate_docstring(
128 Dictionary,
129 'dictionary entries accretion',
130 'instance attributes accretion',
131)
132# Register as subclass of AbstractDictionary rather than use it as mixin.
133# We directly implement, for the sake of efficiency, the methods which the
134# mixin would provide.
135__.AbstractDictionary.register( Dictionary )
138class ProducerDictionary( Dictionary ):
139 ''' Accretive dictionary with default value for missing entries. '''
141 __slots__ = ( '_producer_', )
143 _producer_: _a.DictionaryProducer
145 def __init__(
146 self,
147 producer: _a.DictionaryProducer,
148 /,
149 *iterables: _a.DictionaryPositionalArgument,
150 **entries: _a.DictionaryNominativeArgument
151 ):
152 # TODO: Validate producer argument.
153 self._producer_ = producer
154 super( ).__init__( *iterables, **entries )
156 def __repr__( self ) -> str:
157 return "{fqname}( {producer}, {contents} )".format(
158 fqname = __.discover_fqname( self ),
159 producer = self._producer_,
160 contents = str( self._data_ ) )
162 def __getitem__( self, key: _a.Hashable ) -> _a.Any:
163 if key not in self:
164 value = self._producer_( )
165 self[ key ] = value
166 else: value = super( ).__getitem__( key )
167 return value
169 def copy( self ) -> _a.Self:
170 ''' Provides fresh copy of dictionary. '''
171 dictionary = type( self )( self._producer_ )
172 return dictionary.update( self )
174ProducerDictionary.__doc__ = __.generate_docstring(
175 ProducerDictionary,
176 'dictionary entries accretion',
177 'dictionary entries production',
178 'instance attributes accretion',
179)
182__all__ = __.discover_public_attributes( globals( ) )