Source code for spines.config.base

# -*- coding: utf-8 -*-
"""
Base configuration object for spines.
"""
from __future__ import annotations

from collections import defaultdict
from collections.abc import Mapping
from collections.abc import MutableMapping
from typing import Any
from typing import Iterator
from typing import List
from typing import Tuple


[docs]class BaseConfig(MutableMapping): """ Base configuration object class """ __storage_cls__ = None def __init__(self, *args, **kwargs) -> None: if self.__storage_cls__ is None: self.__storage_cls__ = type(self) self._storage = defaultdict(self.__storage_cls__) self.update(*args, **kwargs) return def __repr__(self) -> str: return "{%s}" % ', '.join(["%s: %s" % (k, v) for k, v in self.items()]) def __getitem__(self, item: str) -> Any: if item is None: return key, sub_key = self._key_helper(item) if key not in self._storage.keys(): raise KeyError("Setting not found: %s" % key) if sub_key: return self._storage[key][sub_key] return self._storage[key] def __setitem__(self, key: str, value) -> None: if key is None: return key, sub_key = self._key_helper(key) if sub_key: self._storage[key][sub_key] = value else: if (key in self._storage and isinstance(value, Mapping) and isinstance(self._storage[key], self.default_cls)): self._storage[key].update(value) else: self._storage[key] = value return def __delitem__(self, key: str) -> None: if key is None: return key, sub_key = self._key_helper(key) if sub_key: del self._storage[key][sub_key] else: del self._storage[key] return def __iter__(self) -> Iterator: return iter(self._storage) def __len__(self) -> int: return len(self._storage) def __contains__(self, item: str) -> bool: if item is None: return key, sub_key = self._key_helper(item) if key not in self._storage.keys(): return False tmp_obj = self._storage[key] if sub_key is not None and isinstance(tmp_obj, Mapping): return sub_key in self._storage[key] return True def __getattr__(self, name: str) -> Any: try: return super().__getattribute__(name) except AttributeError as ex: try: return self.__getitem__(name) except KeyError: raise ex return def __setattr__(self, name: str, value: Any) -> None: if '_storage' not in self.__dict__ or name in self.__dict__: return super().__setattr__(name, value) return self.__setitem__(name, value) @property def default_cls(self) -> type: """type: Default storage class used for missing items.""" return self._storage.default_factory
[docs] def copy(self) -> BaseConfig: """Creates a copy of this configuration object Returns ------- BaseConfig Copy of this configuration object. """ new = self.__class__() for k, v in self._storage.items(): if hasattr(v, 'copy'): new[k] = v.copy() else: new[k] = v return new
[docs] def keys(self) -> List[str]: ret = list() for k, v in self._storage.items(): if isinstance(v, BaseConfig): ret.extend(['%s.%s' % (k, x) for x in v.keys()]) else: ret.append(k) return sorted(ret)
[docs] def values(self) -> List: return [self[k] for k in self.keys()]
@staticmethod def _key_helper(key: str) -> Tuple[str, str]: """Helper function to get properly formatted keys""" ret = key.lower().split('.', 1) if len(ret) == 1: return ret[0], None return ret[0], ret[1]
[docs]class BaseConfigException(Exception): """ Base exception class for configuration subpackage """ pass