so_magic.data.backend.panda_handling.df_backend   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 122
rs 10
c 0
b 0
f 0
wmc 20

2 Functions

Rating   Name   Duplication   Size   Complexity  
A with_self() 0 4 1
A magic_backends() 0 2 1

11 Methods

Rating   Name   Duplication   Size   Complexity  
A Delegate.__init__() 0 7 5
A EngineBackends.__iter__() 0 2 1
A EngineBackends.define_operator() 0 11 2
A EngineBackends.name() 0 2 1
A EngineBackends._get_interface_names() 0 3 1
A EngineBackends._add() 0 6 1
A EngineBackends.add() 0 3 2
A EngineBackends.defined_interfaces() 0 3 1
A EngineBackends.from_initial_available() 0 5 1
A EngineBackends.defined_backend_names() 0 3 1
A EngineBackends.register() 0 10 2
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