so_magic.utils.commands   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 105
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 105
rs 10
c 0
b 0
f 0
wmc 14

12 Methods

Rating   Name   Duplication   Size   Complexity  
A CommandHistory.push() 0 2 1
A BaseCommand.append_arg() 0 2 1
A CommandHistory.__init__() 0 2 1
A Invoker.execute_command() 0 4 2
A BaseCommand.execute() 0 2 1
A CommandHistory.pop() 0 2 1
A BaseCommand.__init__() 0 4 1
A Invoker.__init__() 0 2 1
A BaseCommand.args() 0 3 1
A CommandHistory.stack() 0 3 1
A AbstractCommand.__init__() 0 2 1
A Command.__copy__() 0 4 1
1
import copy
2
from abc import ABC
3
from .command_interface import CommandInterface
4
5
__all__ = ['Command', 'Invoker', 'CommandHistory', 'CommandInterface']
6
7
8
class AbstractCommand(CommandInterface, ABC):
9
    """An abstract implementation of the CommandInterface.
10
11
    The assumption is that the command involves a main 'receiver' object.
12
    Commands of this type follow the receiver.method(*args) pattern/model.
13
    The receiver object usually is commonly acting as an 'oracle' on the
14
    application or on the situation/context.
15
16
    Args:
17
        receiver (object): usually holds the callback function/code with the business logic
18
    """
19
    def __init__(self, receiver):
20
        self._receiver = receiver
21
22
23
class BaseCommand(AbstractCommand):
24
    """A concrete implementation of the Abstract Command.
25
26
    This command simply invokes a 'method' on the 'receiver'. When constructing
27
    instances of BaseCommand make sure you respect the 'method' signature. For
28
    that, you can use the *args to provide the receiver's method arguments.
29
30
    Intuitively, what happens is
31
32
    .. code-block:: python
33
34
        receiver.method(*args)
35
36
    and that is another way to show how the *args are passed to method
37
38
    Args:
39
        receiver (object): an object that is actually executing/receiving the command; usually holds the callback
40
        function/code
41
        method (str): the name of the receiver's method to call (it has to be callable and to exist on the receiver)
42
    """
43
    def __init__(self, receiver, method: str, *args):
44
        super().__init__(receiver)
45
        self._method = method
46
        self._args = list(args)  # this is a list that can be minimally be []
47
48
    def append_arg(self, *args):
49
        self._args.extend(args)
50
51
    @property
52
    def args(self):
53
        return self._args
54
55
    @args.setter
56
    def args(self, args_list):
57
        self._args = list(args_list)
58
59
    def execute(self) -> None:
60
        return getattr(self._receiver, self._method)(*self._args)
61
62
63
class Command(BaseCommand):
64
    """An runnable/executable Command that acts as a prototype through the 'copy' python magic function.
65
66
    When a command instance is invoked with 'copy', the receiver is copied explicitly in a shallow way. The rest of the
67
    command arguments are assumed to be performance invariant (eg it is not expensive to copy the 'method' attribute,
68
    which is a string) and are handled automatically.
69
    """
70
    def __copy__(self):
71
        _ = Command(copy.copy(self._receiver), self._method)
72
        _.append_arg(*self._args)
73
        return _
74
75
76
class CommandHistory:
77
    """The global command history is just a stack; supports 'push' and 'pop' methods."""
78
    def __init__(self):
79
        self._history = []
80
81
    def push(self, command: Command):
82
        self._history.append(command)
83
84
    def pop(self) -> Command:
85
        return self._history.pop(0)
86
87
    @property
88
    def stack(self):
89
        return self._history
90
91
92
class Invoker:
93
    """A class that simply executes a command and pushes it into its internal command history stack.
94
95
    Args:
96
        history (CommandHistory): the command history object which acts as a stack
97
    """
98
    def __init__(self, history: CommandHistory):
99
        self.history = history
100
101
    def execute_command(self, command: Command):
102
        print("INPUT COMMAND", command)
103
        if command.execute() is not None:
104
            self.history.push(command)
105