Source code for pymlconf.config_nodes


import abc
from pymlconf.errors import ConfigKeyError, ConfigurationMergeError
from pymlconf.compat import OrderedDict, isiterable
from pymlconf.yaml_helper import load_string
import copy


# noinspection PyUnresolvedReferences,PyUnresolvedReferences
[docs]class Mergable(object): """ Base class for all configuration nodes, so all configuration nodes are mergable """ __metaclass__ = abc.ABCMeta def __init__(self,data=None): """ :param data: Initial value to constract a mergable instance. default: None. :type data: list or dict """ if data: self.merge(data) @abc.abstractmethod
[docs] def can_merge(self, data): """ Determines whenever can merge with the passed argument or not. :param data: An object to test. :type data: any :returns: bool """ raise NotImplementedError()
@abc.abstractmethod def _merge(self,data): raise NotImplementedError() @abc.abstractmethod
[docs] def copy(self): """ When implemented, returns copy of current config instance. :returns: :class:`.Mergable` """ return copy.deepcopy(self)
@classmethod @abc.abstractmethod
[docs] def empty(cls): """ When implemented, returns an empty instance of drived :class:`.Mergable` class. :returns: :class:`.Mergable` """ raise NotImplementedError()
@classmethod
[docs] def make_mergable_if_possible(cls,data): """ Makes an object mergable if possible. Returns the virgin object if cannot convert it to a mergable instance. :returns: :class:`.Mergable` or type(data) """ if isinstance(data, dict): return ConfigDict(data=data) elif isiterable(data): return ConfigList([cls.make_mergable_if_possible(i) for i in data]) else: return data
[docs] def merge(self, *args): """ Merges this instance with new instances, in-place. :param \*args: Configuration values to merge with current instance. :type \*args: iterable """ for data in args: if isinstance(data, str): to_merge = load_string(data) if not to_merge: continue else: to_merge = data if self.can_merge(to_merge): self._merge(to_merge) else: raise ConfigurationMergeError('Cannot merge myself:%s with %s. data: %s' % (type(self), type(data), data))
def _ensure_namespaces(self, *namespaces): if namespaces: ns = namespaces[0] if ns not in self: self[ns] = ConfigNamespace() # noinspection PyProtectedMember return self.__getattr__(ns)._ensure_namespaces(*namespaces[1:]) else: return self
[docs]class ConfigDict(OrderedDict, Mergable): """ Configuration node that represents python dictionary data. """ def __init__(self, data=None): OrderedDict.__init__(self) Mergable.__init__(self, data=data)
[docs] def can_merge(self, data): return data and isinstance(data, dict)
def _merge(self, data): for k in list(data.keys()): v = data[k] if k in self \ and isinstance(self[k], Mergable) \ and self[k].can_merge(v): self[k].merge(v) else: if isinstance(v, Mergable): self[k] = v.copy() else: self[k] = Mergable.make_mergable_if_possible(copy.deepcopy(v)) def __getattr__(self, key): if key in self: return self.get(key) raise ConfigKeyError(key) def __setattr__(self, key, value): if not key in self: self.__dict__[key] = value else: self[key] = value
[docs] def copy(self): return ConfigDict(self)
@classmethod
[docs] def empty(cls): return cls()
[docs]class ConfigNamespace(ConfigDict, Mergable): """ Configuration node that represents the configuration namespace node. """ def __init__(self, data=None): ConfigDict.__init__(self) Mergable.__init__(self, data=data)
[docs]class ConfigList(list, Mergable): """ Configuration node that represents the python list data. """ def __init__(self, data=None): # noinspection PyTypeChecker list.__init__(self) Mergable.__init__(self, data=data)
[docs] def can_merge(self, data): return data and hasattr(data, '__iter__')
def _merge(self, data): for item in data: if item not in self: self.append(item)
[docs] def copy(self): return ConfigList(self)
@classmethod
[docs] def empty(cls): return cls()