| 1 |  |  | import inspect | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | import types | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | import attr | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | from so_magic.data.magic_datapoints_factory import BroadcastingDatapointsFactory | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | from so_magic.data.interfaces import TabularRetriever, TabularIterator, TabularMutator | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | from so_magic.data.backend.backend import EngineBackend | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | from ..backend_specs import EngineTabularRetriever, EngineTabularIterator, EngineTabularMutator, BackendSpecifications | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | from .client_code import BACKEND | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | # INFRASTRUCTURE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | def with_self(function): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |     def _function(_self, *args, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  |         return function(*args, **kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |     return _function | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  | class Delegate: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |     def __init__(self, tabular_operator): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |         for _member_name, member in inspect.getmembers( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |                 tabular_operator, predicate=lambda x: any([inspect.ismethod(x), inspect.isfunction(x)])): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |             if isinstance(member, types.FunctionType):  # if no decorator is used | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |                 setattr(self, member.__name__, types.MethodType(member, self)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |             if isinstance(member, types.MethodType):  # if @classmethod is used | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |                 setattr(self, member.__name__, types.MethodType(with_self(member), self)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | tabular_operators = { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |     'retriever': { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |         'interface': TabularRetriever, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |         'class_registry': EngineTabularRetriever, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |     }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  |     'iterator': { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |         'interface': TabularIterator, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         'class_registry': EngineTabularIterator, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |     }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |     'mutator': { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |         'interface': TabularMutator, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |         'class_registry': EngineTabularMutator, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  | } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  | BUILT_IN_BACKENDS_DATA = [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |     BACKEND, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  | ] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  | @attr.s | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  | class EngineBackends: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |     backend_interfaces = attr.ib() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |     _interface_2_name = attr.ib(init=False, default=attr.Factory( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |         lambda self: {v['interface']: interface_id for interface_id, v in self.backend_interfaces.items()}, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |         takes_self=True)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |     implementations = attr.ib(init=False, default=attr.Factory(dict)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |     backends = attr.ib(init=False, default=attr.Factory(dict)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |     # id of the backend that is currently being registered/built | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |     __id: str = attr.ib(init=False, default='') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |     @staticmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |     def from_initial_available(backends): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |         engine_backends = EngineBackends(tabular_operators) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |         engine_backends.add(*list(backends)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |         return engine_backends | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |     @property | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |     def defined_interfaces(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |         return self.backend_interfaces.keys() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |     @property | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |     def defined_backend_names(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |         return self.implementations.keys() | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 74 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 75 |  |  |     def __iter__(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 76 |  |  |         return iter((backend_name, interfaces_dict) for backend_name, interfaces_dict in self.implementations.items()) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |     def _get_interface_names(self, backend_id): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  |         """Get the names of the interfaces that the backend has found to implement.""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |         return self.implementations[backend_id].keys() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |     def add(self, *backend_implementations): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |         for backend_implementation in backend_implementations: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |             self._add(backend_implementation) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |     def _add(self, backend_implementation): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |         self.__id = backend_implementation['backend_id'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |         implemented_interfaces = backend_implementation['interfaces'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |         self.implementations[self.__id] =\ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |             {self.name(implementation): implementation for implementation in implemented_interfaces} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |         self.register(backend_implementation) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |     def name(self, interface_implementation): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |         return self._interface_2_name[inspect.getmro(interface_implementation)[1]] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |     def register(self, backend_implementation: dict): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |         for implemented_interface_name in self._get_interface_names(self.__id): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |             self.define_operator(self.__id, implemented_interface_name) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |         # Build | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |         backend_specs = BackendSpecifications(self.__id, backend_implementation['backend_name']) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |         backend = EngineBackend.new(self.__id) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |         # init backend attributes | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |         backend_specs(backend) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |         backend.datapoints_factory = BroadcastingDatapointsFactory() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |         self.backends[self.__id] = backend | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |     def define_operator(self, backend_id, operator_type: str): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |         class_registry = self.backend_interfaces[operator_type]['class_registry'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |         @attr.s | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |         @class_registry.register_as_subclass(backend_id) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |         class _OperatorClass(class_registry): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |             _delegate = attr.ib( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |                 default=attr.Factory(lambda: Delegate(self.implementations[backend_id][operator_type]))) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |             def __getattr__(self, name: str): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |                 return getattr(self._delegate, name) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  | def magic_backends(): | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 121 |  |  |     return EngineBackends.from_initial_available(BUILT_IN_BACKENDS_DATA) | 
            
                                                        
            
                                    
            
            
                | 122 |  |  |  |