Source code for spinn_utilities.ranged.abstract_dict

# Copyright (c) 2017 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import (
    Dict, FrozenSet, Iterable, Iterator, MutableSequence, Optional, Sequence,
    Set, Tuple, Union,
    Generic, TypeVar, overload)
from typing_extensions import TypeAlias
from spinn_utilities.abstract_base import AbstractBase, abstractmethod
#: :meta private:
T = TypeVar("T")
# Can't be Iterable[str] or Sequence[str] because that includes str itself
_StrSeq: TypeAlias = Union[
    MutableSequence[str], Tuple[str, ...], FrozenSet[str], Set[str]]
_Keys: TypeAlias = Optional[Union[str, _StrSeq]]


[docs] class AbstractDict(Generic[T], metaclass=AbstractBase): """ Base class for the :py:class:`RangeDictionary` and *all* views. This allows the users to not have to worry if they have a view. """ __slots__ = () @overload def get_value(self, key: str) -> T: ... @overload def get_value(self, key: Optional[_StrSeq]) -> Dict[str, T]: ...
[docs] @abstractmethod def get_value(self, key: _Keys) -> Union[T, Dict[str, T]]: """ Gets a single shared value for all IDs covered by this view. :param key: The key or keys to get the value of. Use `None` for all :type key: str or iterable(str) or None :return: If key is a str, this returns the single object. If key is iterable (list, tuple, set, etc.) of str (or `None`), returns a dictionary object :raises ~spinn_utilities.ranged.MultipleValuesException: If even one of the keys has multiple values set. But not if other keys not asked for have multiple values """ raise NotImplementedError
[docs] @abstractmethod def keys(self) -> Iterable[str]: """ Returns the keys in the dictionary :return: keys in the dict """ raise NotImplementedError
[docs] @abstractmethod def set_value( self, key: str, value: T, use_list_as_value: bool = False): """ Sets a already existing key to the new value. All IDs in the whole range or view will have this key set. .. warning:: This method does not allow adding keys. Using `dict[str] =` will add a new key, but it is not supported for views. .. warning:: If a View is created over multiple ranges this method would raise a `KeyError` if any the ranges does not have the key. (Currently multiple ranges not yet supported.) :param key: key to value being set :type key: str :param value: any object :param use_list_as_value: True if the value *is* a list :raise KeyError: If a new key is being used. """ raise NotImplementedError
[docs] @abstractmethod def ids(self) -> Sequence[int]: """ Returns the IDs in range or view. If the view is setup with IDs out of numerical order the order used to create the view is maintained. .. note:: If indexing into a view, you are picking the Nth ID. So if the IDs are `[2,3,4,5]` the `view[2]` will be the data for ID `4` and not `2` :return: list of IDs :rtype: list(int) """ raise NotImplementedError
@overload def iter_all_values(self, key: str, update_safe=False) -> Iterator[T]: ... @overload def iter_all_values(self, key: Optional[_StrSeq], update_safe: bool = False) -> Iterator[Dict[str, T]]: ...
[docs] @abstractmethod def iter_all_values(self, key: _Keys, update_safe: bool = False): """ Iterates over the value(s) for all IDs covered by this view. There will be one yield for each ID even if values are repeated. :param key: The key or keys to get the value of. Use `None` for all keys :type key: str or iterable(str) or None :param update_safe: If set True the iteration will work even if values are updated during iteration. If left False the iterator may be faster but behaviour is *undefined* and *unchecked* if *any* values are changed during iteration. :return: If key is a str, this yields single objects. If key is iterable (list, tuple, set, etc.) of str (or `None`), yields dictionary objects """ raise NotImplementedError
@overload def get_ranges(self, key: None = None) -> Sequence[Tuple[ int, int, Dict[str, T]]]: ... @overload def get_ranges(self, key: str) -> Sequence[Tuple[int, int, T]]: ... @overload def get_ranges(self, key: _StrSeq) -> Sequence[Tuple[ int, int, Dict[str, T]]]: ...
[docs] def get_ranges(self, key: _Keys = None) -> Sequence[Tuple[ int, int, Union[T, Dict[str, T]]]]: """ Lists the ranges(s) for all IDs covered by this view. There will be one yield for each range which may cover one or more IDs. .. note:: As the data is created in a single call this is not affected by any updates. :param key: The key or keys to get the value of. Use `None` for all :type key: str or iterable(str) or None :return: List of tuples of (`start`, `stop`, `value`). `start` is *inclusive* so is the first ID in the range. `stop` is *exclusive* so is the last ID in the range + 1. If `key` is a str, `value` is a single object. If `key` is iterable (list, tuple, set, etc.) of str (or `None`) `value` is a dictionary object """ return list(self.iter_ranges(key=key))
@overload def iter_ranges(self, key: str) -> Iterator[Tuple[int, int, T]]: ... @overload def iter_ranges(self, key: Optional[_StrSeq]) -> Iterator[Tuple[ int, int, Dict[str, T]]]: ...
[docs] @abstractmethod def iter_ranges(self, key: _Keys = None ) -> Union[Iterator[Tuple[int, int, T]], Iterator[Tuple[int, int, Dict[str, T]]]]: """ Iterates over the ranges(s) for all IDs covered by this view. There will be one yield for each range which may cover one or more IDs. .. warning:: This iterator is *not* update safe! Behaviour is *undefined* and *unchecked* if *any* values are changed during iteration. :param key: The key or keys to get the value of. Use `None` for all :type key: str or iterable(str) or None :return: yields tuples of (`start`, `stop`, `value`). `start` is *inclusive* so is the first ID in the range. `stop` is *exclusive* so is the last ID in the range + 1. If `key` is a str, `value` is a single object. If `key` is iterable (list, tuple, set, etc.) of str (or `None`), `value` is a dictionary object """ raise NotImplementedError
[docs] @abstractmethod def get_default(self, key: str) -> Optional[T]: """ Gets the default value for a single key. Unless changed, the default is the original value. .. note:: Does not change any values but only changes what ``reset_value`` would do :param key: Existing dict key :type key: str :return: default for this key. """ raise NotImplementedError
[docs] def items(self) -> Sequence[Tuple[str, T]]: """ Returns a list of (``key``, ``value``) tuples. Works only if the whole ranges/view has single values. If the ``key`` is a str, the ``value``\\s are single objects. If the ``key`` is iterable (list, tuple, set, etc.) of str (or `None`), the ``value``\\s are dictionary objects. :return: List of (``key``, ``value``) tuples :rtype: list(tuple) :raises ~spinn_utilities.ranged.MultipleValuesException: If even one of the keys has multiple values set. """ results = [] for key in self.keys(): value = self.get_value(key) results.append((key, value)) return results
[docs] def iteritems(self) -> Iterator[Tuple[str, T]]: """ Iterates over the (``key``, ``value``) tuples. Works only if the whole ranges/view has single values. If the ``key`` is a str, the ``value``\\s are single objects. If the ``key`` is iterable (list, tuple, set, etc.) of str (or `None`), the ``value``\\s are dictionary objects This function is safe for value updates but may miss new keys added during iteration. :return: yield (``key``, ``value``) tuples :rtype: iterable(tuple) :raises ~spinn_utilities.ranged.MultipleValuesException: If even one of the keys has multiple values set. """ for key in self.keys(): yield (key, self.get_value(key))
[docs] def values(self) -> Sequence[T]: """ Returns a list of values. Works only if the whole ranges/view has single values. If a key is a str, its value is a single object. If a key is an iterable (list, tuple, set, etc.) of str (or `None`), its value is a dictionary object. :return: List of values :raises ~spinn_utilities.ranged.MultipleValuesException: If even one of the keys has multiple values set. """ results = [] for key in self.keys(): value = self.get_value(key) results.append(value) return results
[docs] def itervalues(self) -> Iterator[T]: """ Iterates over the values. Works only if the whole ranges/view has single values. If a key is a str, its value is a single object. If a key is an iterable (list, tuple, set, etc.) of str (or `None`), its value is a dictionary object. This function is safe for value updates but may miss new keys added during iteration. :return: yield values :raises ~spinn_utilities.ranged.MultipleValuesException: If even one of the keys has multiple values set. """ for key in self.keys(): yield self.get_value(key)
def __contains__(self, key: Union[str, int]) -> bool: """ Checks if the key is a dictionary key or a range ID. :param key: Dictionary key or ID to check :type key: str or int :return: True if the str key is one of the dict keys or if the int key is one of the range IDs. Otherwise False :rtype: bool """ if isinstance(key, str): return key in self.keys() if isinstance(key, int): return key in self.ids() raise KeyError(f"Unexpected key type: {type(key)}")
[docs] def has_key(self, key: str) -> bool: """ As the Deprecated dict ``has_keys`` function. .. note:: Int keys to IDs are not supported. :param key: the key :type key: str :return: If the key is in dict :rtype: bool """ return key in self.keys()
[docs] def reset(self, key: str): """ Sets the value(s) for a single key back to the default value. :param key: Existing dict key :type key: str :param default: Value to be used by reset """ default = self.get_default(key) if default is None: raise ValueError(f"key '{key}' is not resettable") self.set_value(key, default)