# ]*[ --------------------------------------------------------------------- ]*[
# . Micro ESB Python Module .
# ]*[ --------------------------------------------------------------------- ]*[
# . .
# . Copyright Claus Prüfer (2016 - 2024) .
# . .
# . .
# ]*[ --------------------------------------------------------------------- ]*[
import abc
import sys
import logging
import importlib
import esbconfig
from microesb.transformer import JSONTransformer
[docs]class BaseHandler(JSONTransformer):
""" Abstract Base Class (ABC) Meta Class.
"""
__metaclass__ = abc.ABCMeta
[docs] def __init__(self):
"""
:ivar classref logger: logging logger reference
:ivar dict[SYSProperties] _SYSProperties: internal properties processing dict
:ivar classref _SYSParentObject: internal (hierarchical) class instance ref
:ivar list[classref] _SYSClassNames: internal class refs dict (should be renamed to _SYSClassRefs)
"""
self.logger = logging.getLogger(__name__)
self._SYSProperties = None
self._SYSParentObject = None
self._SYSClassNames = []
super().__init__()
[docs] @abc.abstractmethod
def _add_class(self):
""" Abstract _add_class() method.
"""
[docs] @abc.abstractmethod
def set_properties(self):
""" Abstract set_properties() method.
"""
[docs] def iterate(self):
""" Recursive iterate through hierarchical class instances.
"""
yield self
for x in self:
for y in x.iterate():
yield y
[docs] def add_properties(self, properties, parent_instance):
""" add_properties() method.
:param dict properties: system properties dictionary
:param classref parent_instance: parent class instance reference
The ClassMapper recursively adds instance properties by calling
add_properties() method on initialization for each existing class instance.
"""
properties = self._add_sys_default_properties(properties)
self.logger.debug('add properties:{}'.format(properties))
self._SYSParentObject = parent_instance
setattr(self, '_SYSProperties', properties)
for p_key, p_value in properties.items():
setattr(self, p_key, p_value['default'])
[docs] def _add_sys_default_properties(self, properties):
""" _add_sys_default_properties() method.
:param dict properties: system properties dictionary
Enhance (add) system default properties dictionary by properties dict
defined inside this method.
Currently 'SYSServiceMethod' is the only system property added.
:return: properties
:rtype: dict
"""
properties['SYSServiceMethod'] = {
'type': 'str',
'default': None,
'required': False,
'description': 'System Service Method'
}
return properties
[docs] def _set_property(self, key, value):
""" _set_property() method.
:param str key: property key name
:param str value: property value
"""
if key in self._SYSProperties:
setattr(self, key, value)
@property
def parent_object(self):
""" parent_object() method.
:return: self._SYSParentObject
:rtype: classref
Decorated with @property so direct property access possible
"""
return self._SYSParentObject
@property
def properties(self):
""" properties() method.
:return: self._SYSProperties
:rtype: dict
Decorated with @property so direct property access possible
"""
return self._SYSProperties
@property
def class_count(self):
""" class_count() method.
:return: len(self._SYSClassNames)
:rtype: int
Decorated with @property so direct property access possible
"""
return len(self._SYSClassNames)
@property
def class_name(self):
""" class_name() method.
:return: self.__class__.__name__
:rtype: str
Decorated with @property so direct property access possible
"""
return self.__class__.__name__
def get_value_by_property_id(self, id):
raise NotImplementedError
[docs]class ClassHandler(BaseHandler):
""" ClassHandler class. Inherits BaseHandler class.
"""
[docs] def __init__(self):
"""
:ivar str _SYSType: const internal system type to differentiate handler types
"""
super().__init__()
self._SYSType = 'class_instance'
[docs] def __add__(self, args):
""" overloaded internal __add__() method (+ operator).
:param dict args: class setup dictionary
_add_class() "wrapper" primary used for ClassMapper.
>>> args = {
>>> 'class_name': class_name,
>>> 'class_ref': class_ref
>>> }
>>> parent_instance + args
"""
self._add_class(**args)
[docs] def __iter__(self):
""" overloaded internal __iter__() method.
Overloaded for using iter() on class references.
"""
for class_name in self._SYSClassNames:
yield getattr(self, class_name)
[docs] def _add_class(self, *, class_name, class_ref):
""" _add_class() method.
:param dict *: used for passing params as **args dictionary
:param str class_name: class name
:param classref class_ref: class instance reference
Append class_name to self._SYSClassNames. Setup new class instance
in global namespace.
Primary called by overloaded __add__() method.
"""
self._SYSClassNames.append(class_ref)
new_class = globals()[class_ref]
instance = new_class()
setattr(self, class_name, instance)
[docs] def set_properties(self, item_dict):
""" set_properties() method.
:param dict item_dict: properties dictionary
Iterates over item_dict and calls self._set_property(property_id, value)
foreach item.
"""
for property_id, value in item_dict.items():
self._set_property(property_id, value)
[docs] def set_json_dict(self):
""" set_json_dict() method.
Preprare self.json_dict from self._SYSProperties (used by JSONTransformer).
"""
self.logger.debug('self._SYSProperties:{}'.format(self._SYSProperties))
for property_id in self._SYSProperties:
self.logger.debug('processing property:{}'.format(property_id))
self.json_dict[property_id] = getattr(self, property_id)
[docs]class MultiClassHandler(BaseHandler):
""" MultiObject handler class.
"""
[docs] def __init__(self):
"""
:ivar str _SYSType: const internal system type to differentiate handler types
:ivar list[object] _object_container: object instance container
"""
super().__init__()
self._SYSType = 'multiclass_container'
self._object_container = []
[docs] def __iter__(self):
""" overloaded internal __iter__() method.
Overloaded for using iter() on class references.
"""
for class_instance in self._object_container:
yield class_instance
[docs] def _add_class(self):
""" _add_class() method.
:return: instance
:rtype: object instance
Setup class instance and append it to self._object_container.
"""
self.logger.debug('Add class multiclass handler')
new_class = globals()[self.class_name]
instance = new_class()
setattr(instance, '_SYSProperties', getattr(self, '_SYSProperties'))
setattr(instance, '_SYSParentObject', getattr(self, '_SYSParentObject'))
setattr(instance, '_SYSType', 'multiclass_instance')
self._object_container.append(instance)
return instance
[docs] def set_properties(self, property_list):
""" set_properties() method.
:param list property_list: properties dictionary
Setup class instance and append it to self._object_container.
"""
for class_config in property_list:
instance = self._add_class()
for var, value in class_config.items():
instance._set_property(var, value)
[docs] def set_json_dict(self):
""" set_json_dict() method.
Preprare self.json_dict from self (self._object_container)).
"""
self.logger.debug('Object container:{}'.format(self._object_container))
class_name = self.class_name
self.json_dict[class_name] = []
for class_instance in self:
self.logger.debug('Loop class instance:{}'.format(dir(class_instance)))
class_instance.set_instance_json_dict()
self.json_dict[class_name].append(class_instance.json_dict)
if len(self.json_dict[class_name]) == 0:
del self.json_dict[class_name]
[docs] def set_instance_json_dict(self):
""" set_instance_json_dict() method.
Preprare self.json_dict from self._SYSProperties (used by JSONTransformer).
"""
for property_id in self._SYSProperties:
try:
self.json_dict[property_id] = getattr(self, property_id)
except Exception as e:
pass
[docs]class ClassMapper(ClassHandler):
""" Class Mapper class.
"""
[docs] def __init__(self, *, class_references, class_mappings, class_properties):
"""
:param dict *: used for passing params as **args dictionary
:param dict class_references: class references dictionary
:param dict class_mappings: class mappings dictionary
:param dict class_properties: class properties dictionary
:ivar dict _class_mappings: set from class_mappings param
:ivar dict _class_properties: set from class_properties param
:ivar dict _class_references: set from class_references param
:ivar dict _class_hierarchy: internally used to map parent instances
"""
super().__init__()
self._class_mappings = class_mappings
self._class_properties = class_properties
self._class_references = class_references
root_class = next(iter(class_references))
root_index = class_references[root_class]
self._class_hierarchy = {}
call_dict = {
'class_name': root_class,
'children': root_index['children'],
'property_ref': root_index['property_ref'],
'parent_instance': self,
}
self._map(**call_dict)
def __repr__(self):
""" overloaded __repr__() method.
Print out class mappings, properties and references.
"""
return 'Class mappings:{} properties:{} references:{}'.format(
self._class_mappings,
self._class_properties,
self._class_references
)
[docs] def _get_mapping(self, class_name):
""" _get_mapping() method.
:param str class_name: mapping class_name
:return: self._class_mappings[class_name]
:rtype: str
Get class name from class_mappings dictionary by class_name.
"""
return self._class_mappings[class_name]
[docs] def get_references(self):
""" get_references() method.
:return: self._class_references
:rtype: dict
Get class references dictionary.
"""
return self._class_references
[docs] def _map(
self,
*,
class_name,
property_ref,
parent_instance,
children={}
):
""" _map() method.
:param dict *: used for passing params as **args dictionary
:param str class_name: (root) class name
:param dict property_ref: property reference dictionary
:param classref parent_instance: property reference dictionary
:param dict children: children definition dictionary
Recursive map class hierarchy / class instances.
"""
self.logger.debug(
'class_name:{} property_ref:{} parent_instance:{} children:{}'.format(
class_name,
property_ref,
parent_instance,
children,
)
)
class_ref = self._get_mapping(class_name)
self._class_hierarchy[class_name] = parent_instance
args = {
'class_name': class_name,
'class_ref': class_ref
}
parent_instance + args
child_instance = getattr(parent_instance, class_name)
child_instance.add_properties(
self._class_properties[property_ref]['properties'],
parent_instance
)
for child_class_name, child_class_config in children.items():
child_class_config['class_name'] = child_class_name
child_class_config['parent_instance'] = child_instance
self._map(**child_class_config)
[docs]class ServiceMapper(ClassHandler):
""" Service Mapper class.
"""
[docs] def __init__(self, *, class_mapper, service_call_data):
"""
:param dict *: used for passing params as **args dictionary
:param classref class_mapper: class mapper instance reference
:param dict service_call_data: service call metadata dictionary
:ivar classref _class_mapper: set from class_mapper param
"""
super().__init__()
self._class_mapper = class_mapper
class_references = self._class_mapper.get_references()
root_class = next(iter(class_references))
root_index = class_references[root_class]
call_dict = {
'class_name': root_class,
'children': root_index['children'],
'parent_instance': self._class_mapper,
'hierarchy': service_call_data
}
self._map(**call_dict)
try:
for class_ref, class_props in class_references.items():
for method_def in class_mapper._class_properties['SYSBackendMethods']:
if method_def[1] == 'on_recursion_finish':
self.logger.debug('SYSBackendMethod:{}'.format(method_def[0]))
try:
getattr(getattr(self._class_mapper, class_ref), method_def[0])()
except Exception as e:
pass
except Exception as e:
self.logger.debug('SYSBackendMethods preocessing exception:{}'.format(e))
[docs] def _map(
self,
*,
class_name,
parent_instance,
hierarchy,
children={},
property_ref=None
):
""" _map() method.
:param dict *: used for passing params as **args dictionary
:param str class_name: (root) class name
:param classref parent_instance: property reference dictionary
:param dict hierarchy: (root) class setup item
:param dict children: children definition dictionary
:param dict property_ref: property reference dictionary
Recursive process class hierarchy / service properties mapping.
"""
self.logger.debug(
'class_name:{} parent_instance:{} children:{} hierarchy:{}'.format(
class_name,
parent_instance,
children,
hierarchy
)
)
class_instance = getattr(parent_instance, class_name)
try:
hierarchy = hierarchy[class_name]
class_instance.set_properties(hierarchy)
try:
getattr(class_instance, class_instance.SYSServiceMethod)()
except Exception as e:
self.logger.debug('SYSServiceMethod call exception:{}'.format(e))
for child_class_name, child_class_config in children.items():
child_class_config['class_name'] = child_class_name
child_class_config['parent_instance'] = class_instance
child_class_config['hierarchy'] = hierarchy
self._map(**child_class_config)
try:
for ci in class_instance._object_container:
getattr(ci, ci.SYSServiceMethod)()
except Exception as e:
self.logger.debug('SYSServiceMethod call exception:{}'.format(e))
except Exception as e:
self.logger.debug('Class reference in service call metadata not set:{}'.format(e))
pass
[docs]class ServiceExecuter(object):
""" Service Executer class.
"""
[docs] def __init__(self):
pass
[docs] def execute(self, class_mapper, service_data):
"""
:param classref class_mapper: class mapper instance reference
:param list service_data: list of service call metadata dictionary items
"""
rlist = []
for item in service_data['data']:
res = ServiceMapper(
class_mapper=class_mapper,
service_call_data=item
)
rlist.append(res)
return rlist
# import classes into current namespace
current_mod = sys.modules[__name__]
import_classes = esbconfig.import_classes
for module_name in import_classes:
mod_ref = importlib.import_module(module_name)
for class_name in import_classes[module_name]:
setattr(current_mod, class_name, getattr(mod_ref, class_name))