ooze/ooze/util.py

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