Passed
Push — dev ( 1b3874...6a1e3c )
by Konstantinos
03:33
created

CommandRegistrator.__getitem__()   A

Complexity

Conditions 2

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nop 2
1
import inspect
2
import attr
3
from green_magic.utils import Command
4
from green_magic.utils import Subject, Observer
5
6
7
class MyDecorator(type):
8
    """Metaclass that provides a decorator able to be invoked both with and without parenthesis.
9
    The wrapper function logic should be implemented by the client code.
10
    """
11
    @classmethod
12
    def magic_decorator(mcs, arg=None):
13
        print("DEBUG magic_decorator 1, args:", str(arg))
14
        def decorator(func):
15
            print("DEBUG magic_decorator 2, args:", str(func))
16
            def wrapper(*a, **ka):
17
                print("DEBUG magic_decorator 3, args: [{}]".format(', '.join(str(x) for x in a)))
18
                mcs._wrapper(func, *a, **ka)
19
                return func(*a, **ka)
20
            return wrapper
21
22
        if callable(arg):
23
            return decorator(arg)  # return 'wrapper'
24
        else:
25
            return decorator  # ... or 'decorator'
26
27
    def _wrapper(cls, function, *args, **kwargs):
28
        print('LA8OS')
29
        raise NotImplementedError
30
31
32
class CommandRegistrator(MyDecorator):
33
    """Classes can use this class as metaclass to obtain a single registration point accessible as class attribute
34
    """
35
    def __new__(mcs, *args, **kwargs):
36
        class_object = super().__new__(mcs, *args, **kwargs)
37
        class_object.state = None
38
        class_object.registry = {}
39
        return class_object
40
41
    def __getitem__(self, item):
42
        if item not in self.registry:
43
            raise RuntimeError(f"Key '{item}' fot found in registry: [{', '.join(str(x) for x in self.registry.keys())}]")
44
        return self.registry[item]
45
46
@attr.s
47
class CommandFactory(Subject):
48
    """A factory class able to construct new command objects."""
49
    command_constructor = attr.ib(init=True, default=Command)
50
51
    def create(self, *args) -> Command:
52
        """Call to create a new Command object. The input arguments can be in two formats:
53
54
        1. create(an_object, method, *arguments)
55
        In this case the command is of the form an_object.method(*arguments)
56
57
        2. create(a_function, *arguments)
58
        In this case the command is of the form a_function(*arguments)
59
60
        Returns:
61
            Command: an instance of a command object
62
        """
63
        is_function = hasattr(args[0], '__code__')
64
        if is_function:  # if receiver is a function; creating from function
65
            return self.command_constructor(args[0], '__call__', *args[1:])
66
        return self.command_constructor(args[0], args[1], *args[2:])
67
68
@attr.s
69
class MagicCommandFactory(Subject):
70
    """Instances of this class act as callable command factories that notify,
71
    subscribed observers/listeners upon new command object creation.
72
73
    Args:
74
        command_factory (CommandFactory, optional): an instance of a CommandFActory
75
    """
76
    command_factory = attr.ib(init=True, default=CommandFactory())
77
78
    def __call__(self, *args, **kwargs):
79
        self._state = self.command_factory.create(*args)
80
        self.notify()
81
        return self._state
82
83
84
@attr.s
85
class CommandsAccumulator(Observer):
86
    """"""
87
    commands = attr.ib(init=False, default={})
88
89
    def update(self, subject: Subject) -> None:
90
        self.commands[getattr(subject, 'name', str(subject.state))] = subject.state
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable getattr does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable str does not seem to be defined.
Loading history...
91
92
@attr.s
93
class CommandGetter:
94
    _commands_accumulator = attr.ib(init=True, default=CommandsAccumulator())
95
96
    def __getattr__(self, item):
97
        if item not in self._commands_accumulator.commands:
98
            raise KeyError(f"Item '{item}' not found in [{', '.join(str(_) for _ in self._commands_accumulator.commands.keys())}]")
99
        return self._commands_accumulator.commands[item]
100
101
102
@attr.s
103
class CommandsManager:
104
    """[summary]
105
106
    Args:
107
        prototypes (dict, optional): initial prototypes to be supplied
108
        command_factory (callable, optional): a callable that returns an instance of Command
109
    """
110
    _commands_getter = attr.ib(init=True, default=CommandGetter())
111
112
    @property
113
    def command(self):
114
        return self._commands_getter
115
116
    @property
117
    def commands_dict(self):
118
        return self._commands_accumulator.commands
119
120
    def __getattr__(self, item):
121
        if item not in self._commands_accumulator.commands:
122
            raise KeyError(f"Item '{item}' not found in [{', '.join(str(_) for _ in self._commands_accumulator.commands.keys())}]")
123
        return self._commands_accumulator.commands[item]
124