Source code for coolfig.schema

from six import iteritems, with_metaclass

from .providers import NOT_PROVIDED


[docs]class ImproperlyConfigured(Exception): """ Raised when e request for a configuration value cannot be fulfilled. """
[docs]class ValueBase(object): def __call__(self, settingsobj, key): # NOCOV raise NotImplementedError
[docs]class Value(ValueBase): def __init__(self, type, default=NOT_PROVIDED, key=None): self.type = type self.default = default self.key = key def _get_provided_value(self, settingsobj, key): return settingsobj.config_provider.get(key) def __call__(self, settingsobj, key): key = self.key if self.key else key value = self._get_provided_value(settingsobj, key) if value is NOT_PROVIDED and self.default is NOT_PROVIDED: # Value is required but was not provided raise ImproperlyConfigured("no value set for {}".format(key)) elif value is NOT_PROVIDED: # Value is optional, return the default if isinstance(self.default, Reference): return self.default(settingsobj) else: return self.default else: # Coerce to the correct type and return it return self.type(value)
[docs]class ComputedValue(ValueBase): def __init__(self, callable, *args, **kwargs): self.callable = callable self.args = args self.kwargs = kwargs def __call__(self, settingsobj, key): return self.callable(settingsobj, *self.args, **self.kwargs)
[docs]def computed_value(func): return ComputedValue(func)
[docs]class DictValue(Value): def __init__(self, type, keytype=str, *args, **kwargs): super(DictValue, self).__init__(type, *args, **kwargs) self.keytype = keytype def __call__(self, settingsobj, key): key = (self.key if self.key else key) + "_" return { self.keytype(k[len(key) :]): self.type(v) for k, v in settingsobj.config_provider.iterprefixed(key) }
[docs]class Dictionary(ValueBase): def __init__(self, spec): self.spec = spec def __call__(self, settingsobj, key): return { key: value(settingsobj, key) for key, value in iteritems(self.spec) }
[docs]class BoundValue(object): def __init__(self, cls, name, value): self.cls = cls self.name = name self.value = value def __get__(self, obj, objtype=None): if obj is None: return self return self.value(obj, self.name) def __set__(self, obj, objtype=None): raise AttributeError("can't set attribute") def __repr__(self): # NOCOV return "BoundValue({}, {}) of class {}".format( self.name, self.value.type, self.cls.__name__ )
[docs]class StaticValue(BoundValue): def __init__(self, value): self.value = value def __get__(self, obj, objtype=None): if obj is None: return self return self.value def __repr__(self): # NOCOV return "StaticValue({!r})".format(self.value)
[docs]class Reference(object): def __init__(self, key): self.key = key def __call__(self, obj): return getattr(obj, self.key)
ref = Reference # NOQA
[docs]def bind_values(cls, clsdict): for k, v in clsdict.items(): if isinstance(v, ValueBase): setattr(cls, k, BoundValue(cls, k, v))
[docs]class SettingsMeta(type): def __init__(cls, name, bases, clsdict): for base in bases: if not isinstance(base, cls.__class__): bind_values(cls, base.__dict__) bind_values(cls, clsdict) return super(SettingsMeta, cls).__init__(name, bases, clsdict) def __iter__(self): for k in dir(self): v = getattr(self, k, None) if isinstance(v, BoundValue): yield k, v
[docs]class SettingsBase(object): def __init__(self, config_provider): self.config_provider = config_provider def __iter__(self): return iter(self.__class__)
[docs] def items(self): for k, v in self: yield k, getattr(self, k)
[docs] def keys(self): for k, v in self: yield k
[docs] def as_dict(self): return dict(self.items())
[docs]class Settings(with_metaclass(SettingsMeta, SettingsBase)):
[docs] @classmethod def merge(cls, *others): """ Merge the `others` schema into this instance. The values will all be read from the provider of the original object. """ for other in others: for k, v in other: setattr(cls, k, BoundValue(cls, k, v.value))