from .attributes import *
from .modules import *
[docs]class ModuleProperty(ModuleAttribute):
"""
A property for a submodule.
The ModuleAttribute is declared with:
ModuleAttribute(module_cls, default, doc)
The module_cls is instantiated in the __init__ of the parent module
For the moment, the following actions are supported:
- module.sub = dict(...) : module.sub.set_setup_attributes(dict(...))
- module.sub: returns the submodule.
"""
default = {}
def __init__(self,
module_cls,
default=None,
doc="",
ignore_errors=False,
call_setup=False,
**kwargs):
self.module_cls = module_cls
self.kwargs = kwargs
ModuleAttribute.__init__(self,
default=default,
doc=doc,
ignore_errors=ignore_errors,
call_setup=call_setup)
[docs] def set_value(self, obj, val):
"""
Use the dictionnary val to set_setup_attributes
:param obj:
:param val:
:return:
"""
getattr(obj, self.name).setup_attributes = val
return val
[docs] def get_value(self, obj):
if not hasattr(obj, '_' + self.name):
# getter must manage the instantiation of default value
setattr(obj, '_' + self.name,
self._create_module(obj))
return getattr(obj, '_' + self.name)
def _create_module(self, obj):
return self.module_cls(obj, name=self.name, **self.kwargs)
[docs]class ModuleList(Module, list):
""" a list of modules"""
def __init__(self, parent, name=None, element_cls=Module, default=[]):
def element_name(element_self):
""" function that is used to dynamically assign each
ModuleListElement's name to the index in the list.
This is needed for proper storage in the config file"""
try:
return element_self.parent.index(element_self)
except ValueError:
return element_self._initial_name #'not in list' #return len(element_self.parent)
def element_next(element_self):
try:
return element_self.parent[element_self.parent.index(element_self)+1]
except:
return None
def element_init(element_self, parent, initial_name=None, *args, **kwargs):
# creates a wrapper around the init function to pass the initial element
# number in the list at object creation
element_self._initial_name = initial_name
return element_cls.__init__(element_self, parent, *args, **kwargs)
# element.name equals element.number in order to get the right config
# file section
self.element_cls = type(element_cls.__name__ + "ListElement",
(element_cls, ),
{'name': property(fget=element_name),
'next': property(fget=element_next),
'__init__': element_init
})
self._signal_launcher = self.element_cls._signal_launcher
super(ModuleList, self).__init__(parent, name=name)
# set to default setting
self.extend(default)
# all read-only methods from the base class 'list' work perfectly well for us, i.e.
# __getitem__, count(), index(), reverse()
def __setitem__(self, index, value):
# setting a list element sets up the corresponding module
self[index].setup_attributes = value
[docs] def insert(self, index, new):
# insert None as a placeholder at the right place in the list
# in order to assign right indices to surrounding elements
super(ModuleList, self).insert(index, None)
# make new module (initial_name must be given).
super(ModuleList, self).__setitem__(index,
self.element_cls(self, initial_name=index))
# set initial name to none, since name is now inferred from index in the list
self[index]._initial_name=None
# initialize setup_attributes
self[index].setup_attributes = new
# save state
self.save_state()
[docs] def append(self, new):
self.insert(self.__len__(), new)
[docs] def extend(self, iterable):
for i in iterable:
self.append(i)
def __delitem__(self, index=-1):
# make sure at object destruction that the name variable corresponds to former name
self[index]._initial_name = index
# setting a list element sets up the corresponding module
to_delete = super(ModuleList, self).pop(index)
# call destructor
to_delete._clear()
# remove saved state from config file
self.c._pop(index)
#self.save_state()
[docs] def pop(self, index=-1):
# get attributes
setup_attributes = self[index].setup_attributes
self.__delitem__(index)
return setup_attributes
[docs] def remove(self, value):
self.__delitem__(self.index(value))
def __repr__(self):
return str(ModuleList.__name__)+"("+list.__repr__(self)+")"
@property
def setup_attributes(self):
return [item.setup_attributes for item in self]
@setup_attributes.setter
def setup_attributes(self, val):
for i, v in enumerate(val):
try:
self[i] = v
except IndexError:
self.append(v)
while len(self) > len(val):
self.__delitem__(-1)
def _load_setup_attributes(self):
"""
Load and sets all setup attributes from config file
"""
if self.c is not None:
# self.c._data is a list that can be passed to setup_attributes
self.setup_attributes = self.c._data
[docs]class ModuleListProperty(ModuleProperty):
"""
A property for a list of submodules.
"""
default = []
module_cls = ModuleList
def __init__(self, element_cls, default=None, doc="", ignore_errors=False):
# only difference to base class: need to assign element_cls (i.e. class of element modules)
self.element_cls = element_cls
ModuleProperty.__init__(self,
self.module_cls,
default=default,
doc=doc,
ignore_errors=ignore_errors)
def _create_module(self, obj):
newmodule = self.module_cls(obj,
name=self.name,
element_cls=self.element_cls,
default=self.default)
try:
newmodule._widget_class = self._widget_class
except AttributeError:
pass
return newmodule
[docs] def validate_and_normalize(self, obj, value):
""" ensures that only list-like values are passed to the ModuleProperty """
if not isinstance(value, list):
try:
value = value.values()
except AttributeError:
raise ValueError("ModuleProperty must be assigned a list. "
"You have wrongly assigned an object of type "
"%s. ", type(value))
return value
[docs]class ModuleDict(Module):
"""
container class that loosely resembles a dictionary which contains submodules
"""
def __getitem__(self, key):
return getattr(self, key)
[docs] def keys(self):
return self._module_attributes
[docs] def values(self):
return [self[k] for k in self.keys()]
[docs] def items(self):
return [(k, self[k]) for k in self.keys()]
def __iter__(self):
# this method allows to write code like this: 'for submodule in modulecontainer: submodule.do_sth()'
return iter(self.values())
@property
def setup_attributes(self):
return super(ModuleDict, self).setup_attributes
@setup_attributes.setter
def setup_attributes(self, kwds):
Module.setup_attributes.fset(self,
{k: v for k, v in kwds.items() if k in self._setup_attributes})
def __setitem__(self, key, value):
# make the new ModuleProperty of module type "value" in the class
mp = ModuleProperty(value)
mp.name = key # assign module name
setattr(self.__class__, key, mp)
# do what the constructor would do: append to setup_attributes...
self._module_attributes.append(key)
self._setup_attributes.append(key)
# ... attribute the name
self[key].name = key
# initialize with saved values if available
self[key]._load_setup_attributes()
def __delitem__(self, key):
self._module_attributes.pop(key)
self._setup_attributes.pop(key)
getattr(self, key)._clear()
delattr(self, key)
[docs] def pop(self, key):
""" same as __delattr__ (does not return a value) """
module = self._setup_attributes.pop(key)
delattr(self, key)
return module
[docs]class ModuleDictProperty(ModuleProperty):
default_module_cls = Module
def __init__(self, module_cls=None, default=None, doc="",
ignore_errors=False, **kwargs):
"""
returns a descriptor for a module container, i.e. a class that contains submodules whose name and class are
specified in kwargs. module_cls is the base class for the module container (typically SoftwareModule)
"""
# get default base class to inherit from
if module_cls is None:
module_cls = self.default_module_cls
# make a copy of ModuleDict class that can be modified without modifying all ModuleDict class instances
# inherit from module_cls
ModuleDictClassInstance = type(module_cls.__name__+"DictPropertyInstance",
(ModuleDict, module_cls),
{key: ModuleProperty(value) for key, value in kwargs.items()})
# metaclass of Module already takes care of _setup/module_attributes
# and names of submodules, so no need for these two:
# ModuleDictClassInstance._setup_attributes = kwargs.keys()
# ModuleDictClassInstance._module_attributes = kwargs.keys()
# init this attribute with the contained module
super(ModuleDictProperty, self).__init__(ModuleDictClassInstance,
default=default,
doc=doc,
ignore_errors=ignore_errors)