from .common import Mapping, Sequence, RequiredValue, RequiredValueType, \
NoneType, dbg_print
CONFIG_VALUE_TYPES = (bool, int, float, str, RequiredValueType, NoneType)
[docs]class Setting(property):
_tag = 'abstract'
_cast = None
[docs] @classmethod
def tag(cls):
return cls._tag
[docs] @classmethod
def Factory(cls, clsname, cast, bases=None, mixins=None, tag=None):
if not isinstance(clsname, str):
raise ValueError("clsname must be a non-empty string")
if not callable(cast):
raise ValueError("cast must be callable but is {:s}")
if bases is None:
bases = ()
elif not isinstance(bases, Sequence) \
or any(not isinstance(b, type) for b in bases):
raise ValueError("bases must be a Sequence of classes or types")
if mixins is None:
mixins = {}
elif not isinstance(mixins, Mapping):
raise ValueError("mixins must be a Mapping of attributes")
if tag is None:
tag = clsname
pcast = property(lambda s: tag, doc="cast {:s} to {:s}"
.format(tag, cast.__name__))
return type(clsname, (Setting, *bases,),
{"cast": pcast, "tag": tag, **mixins})
def __new__(cls, name, default=None,
getter=True, setter=None, deleter=None, doc=None):
self = super().__new__(cls)
self._name = name
return self
# instance properties
_default = None
@property
def default(self):
return self._default
def __init__(self, name, default=None,
getter=True, setter=None, deleter=None, doc="Undocumented"):
dbg_print("Setting.__init__",
name, default, getter, setter, deleter, doc)
cast = self._cast
value = None if default in (RequiredValue, None) else \
cast(default)
setter_wrapper = None
if getter is True:
if callable(setter):
raise AttributeError("if {:s} getter is True then the"
.format(name) + " setter may only be True or False"
+ " (or None defaulting to True")
if default is RequiredValue:
if setter is False:
raise AttributeError("if {:s} getter is True and"
.format(name) + " default is RequiredValue then"
+ " the setter may only be True (or None"
+ " defaulting to True")
if deleter:
raise AttributeError("if {:s} getter is True and"
.format(name) + " default is RequiredValue"
+ " then deleters are not allowed")
def getter(self):
nonlocal value
return value
if setter is True or default is RequiredValue:
def setter_wrapper(self, val):
nonlocal value, default
if val is None:
if default is RequiredValue:
raise AttributeError("{:s} is a required value"
.format(name))
val = None if default is None else cast(default)
value = val
elif setter is True:
raise AttributeError("if {:s} getter is not True then the setter"
.format(name)
+ " may only be callable or None")
elif not setter and default is RequiredValue:
raise AttributeError("if {:s} getter is not True and".format(name)
+ " default is RequiredValue then the setter"
+ " may only be callable")
elif callable(setter):
def setter_wrapper(self, val):
nonlocal default, setter
if val is None:
if default is RequiredValue:
raise AttributeError("{:s} is a required value"
.format(name))
setter(self, None if default is None else cast(default))
dbg_print("Setting.__init__ (before super)",
name, default, getter, setter, deleter, doc)
self._default = default
super().__init__(getter, setter_wrapper, deleter, doc)
dbg_print("Setting.__init__ (after super)",
name, default, getter, setter, deleter, doc)
def __bool__(self):
return bool(self.default if self is None else self)
def __float__(self):
return float(self.default if self is None else self)
def __int__(self):
return int(self.default if self is None else self)
def __str__(self):
return str(self.default if self is None else self)
Setting.__index__ = Setting.__int__
[docs]class SettingMap(Mapping):
[docs] @classmethod
def Factory(cls, name, settings, base=None, repr_=None):
# settings is (Setting instance, *setting_args)
def __init__(self, initializer={}):
for key in self._keys:
self._settings[key] = settings[key](initializer[key]
if key in initializer else None)
def __getitem__(self, key):
if key in self._keys:
self._settings[key].value
raise KeyError("invalid key {:s}".format(key))
def __setitem__(self, key, value):
if key in self._keys:
self._settings[key].value = value
raise KeyError("invalid key {:s}".format(key))
bases = (cls,) if base is None else (cls, base)
mixins = {"_settings": settings}
return type(name, bases, mixins)