144 lines
4.5 KiB
Python
144 lines
4.5 KiB
Python
"""
|
|
Utility module.
|
|
Nothing in here may depend on other parts of ooze!
|
|
"""
|
|
|
|
def indent(string, indent=4*' '):
|
|
lines = string.split('\n')
|
|
lines_indented = [f'{indent}{line}' for line in lines]
|
|
return '\n'.join(lines_indented)
|
|
|
|
def allowed_chars_only(value, allowed_chars):
|
|
|
|
# return a bool indicating whether value
|
|
# contains only allowed_chars.
|
|
# both parameters are strings
|
|
|
|
return set(value) <= set(allowed_chars)
|
|
|
|
def prettydict(d, level=0, indent=4):
|
|
|
|
"""
|
|
Returns a dict as prettily formatted string.
|
|
"""
|
|
|
|
s = ''
|
|
|
|
if level == 0:
|
|
s += '{\n'
|
|
|
|
for key, value in d.items():
|
|
if isinstance(value, dict):
|
|
s += ' ' * indent * (level + 1)
|
|
s += f"'{key}': {{\n"
|
|
s += prettydict(
|
|
value,
|
|
level=level + 1,
|
|
indent=indent)
|
|
s += ' ' * indent * (level + 1) + '}\n'
|
|
else:
|
|
s += ' ' * indent * (level + 1)
|
|
s += f"'{key}': {value}\n"
|
|
|
|
if level == 0:
|
|
s += '\n}'
|
|
|
|
return s
|
|
|
|
class class_or_instance_method(classmethod):
|
|
|
|
def __get__(self, instance, owner=None):
|
|
|
|
if instance is None: # call via class
|
|
return super().__get__(instance, owner) # invoke classmethod.__get__
|
|
return self.__func__.__get__(instance, owner) # invoke standard implementation
|
|
|
|
class StorageObject:
|
|
|
|
def __init__(self, **data):
|
|
super().__setattr__('_storage_data', data)
|
|
|
|
def __getitem__(self, name):
|
|
return super().__getattribute__('_storage_data')[name]
|
|
|
|
def __setitem__(self, name, value):
|
|
super().__getattribute__('_storage_data')[name] = value
|
|
|
|
def __delitem__(self, name):
|
|
del super().__getattribute__('storage_date')[name]
|
|
|
|
def __getattribute__(self, name):
|
|
return super().__getattribute__('__getitem__')(name)
|
|
|
|
def __setattr__(self, name, value):
|
|
super().__getattribute__('__setitem__')(name, value)
|
|
|
|
def __delattr__(self, name):
|
|
super().__getattribute__('__delitem__')(name)
|
|
|
|
def __contains__(self, item):
|
|
return item in super().__getattribute__('_storage_data')
|
|
|
|
def __len__(self):
|
|
return len(super().__getattribute__('_storage_data'))
|
|
|
|
def __repr__(self):
|
|
|
|
class_name = super().__getattribute__('__class__').__name__
|
|
data_dict = super().__getattribute__('_storage_data')
|
|
|
|
return f"<{class_name} {str(data_dict)}>"
|
|
|
|
def keys(self):
|
|
return super().__getattribute__('_storage_data').keys()
|
|
|
|
def values(self):
|
|
return super().__getattribute__('_storage_data').values()
|
|
|
|
class ChildAwareMeta(type):
|
|
|
|
def __init__(self, name, parents, properties, **kwargs):
|
|
|
|
super().__init__(name, parents, properties, **kwargs)
|
|
|
|
self.__abstract__ = '__abstract__' in properties and properties['__abstract__']
|
|
self.class_children = () # direct descendants
|
|
self.class_tree = () # all descendants as hierarchical tree
|
|
self.class_descendants = () # all descendants as one-dimensional tuple
|
|
self.class_descendants_abstract = () # same, but only abstract descendants
|
|
self.class_descendants_concrete = () # ditto for non-abstract descendants
|
|
|
|
for parent in parents:
|
|
parent.class_children += (self,)
|
|
parent._class_graph_update()
|
|
|
|
def _class_graph_update(self):
|
|
|
|
# build an iterable tree/graph of all descendant classes
|
|
self.class_tree = tuple((child, tuple(child.class_tree)) for child in self.class_children)
|
|
|
|
# build a one-dimensional tuple of all descendant classes
|
|
self.class_descendants = ()
|
|
self.class_descendants_abstract = ()
|
|
self.class_descendants_concrete = ()
|
|
|
|
for child in self.class_children:
|
|
|
|
self.class_descendants += (child, *child.class_descendants)
|
|
|
|
if child.__abstract__:
|
|
self.class_descendants_abstract += (child, *child.class_descendants_abstract)
|
|
self.class_descendants_concrete += child.class_descendants_concrete
|
|
else:
|
|
self.class_descendants_concrete += (child, *child.class_descendants_concrete)
|
|
self.class_descendants_abstract += child.class_descendants_abstract
|
|
|
|
# propagate graph update to parents
|
|
for parent in self.mro()[1:]: # mro[0] is self
|
|
if hasattr(parent, '_class_graph_update'):
|
|
parent._class_graph_update()
|
|
|
|
class ChildAware(metaclass=ChildAwareMeta):
|
|
# TODO: __init_subclass__ instead of metaclass?
|
|
pass
|