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

green_magic.utils.commands.CommandHistory.push()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
from abc import ABC, abstractmethod
2
import copy
3
from typing import List
4
5
__all__ = ['Command', 'Invoker', 'CommandHistory']
6
7
8
class CommandInterface(ABC):
9
    """A class implementing this interface can act as a standalone command, encapsulating all logic and data needed."""
10
    @abstractmethod
11
    def execute(self) -> None:
12
        """Call this method to execute the command.
13
        """
14
        raise NotImplementedError
15
16
class AbstractCommand(CommandInterface, ABC):
17
    """An abstract implementation of the Command Interface. The assumption is that the command involves a 'reveiver' object
18
    (of arbitrary type) acting as an 'oracle' on the application.
19
20
    Args:
21
        receiver (object): an object that is actually executing/receiving the command; usually holds the callback function/code
22
    """
23
    def __init__(self, receiver):
24
        self._receiver = receiver
25
26
27
class BaseCommand(AbstractCommand):
28
    """A basic implementation of the Abstract Command. The assumption is that the command involves calling a method of the reveiver
29
    using the user-provided function arguments. Use the optional args to provide the receiver's method runtime arguments.
30
31
    Args:
32
        receiver (object): an object that is actually executing/receiving the command; usually holds the callback function/code
33
        method (str): the name of the receiver's method to call (it has to be callable and to exist on the receiver)
34
    """
35
    def __init__(self, receiver, method: str, *args):
36
        super().__init__(receiver)
37
        self._method = method
38
        self._args = [_ for _ in args]  # this is a list that can be minimally be []
39
40
    def append_arg(self, *args):
41
        self._args.extend([_ for _ in args])
42
43
    @property
44
    def args(self):
45
        return self._args
46
47
    @args.setter
48
    def args(self, args_list):
49
        self._args = args_list
50
51
    def execute(self) -> None:
52
        getattr(self._receiver, self._method)(*self._args)
53
54
55
class Command(BaseCommand):
56
    """A BaseCommand acting as a prototype. The receiver is copied explicitly in a shallow way. The rest are
57
    assumed to be performance invariant (eg it is not expensive to copy the 'method' attribute, which is a string) and are handled automatically.
58
    """
59
    def __copy__(self):
60
        _ = Command(copy.copy(self._receiver), self._method)
61
        _.append_arg(*self._args)
62
        return _
63
64
65
class CommandHistory:
66
    """The global command history is just a stack; supports 'push' and 'pop' methods."""
67
    def __init__(self):
68
        self._history = []
69
70
    def push(self, c: Command):
71
        self._history.append(c)
72
73
    def pop(self) -> Command:
74
        return self._history.pop(0)
75
76
77
class Invoker:
78
    """A class that simply executes a command and pushes it into its internal command history stack.
79
80
    Args:
81
        history (CommandHistory): the command history object which acts as a stack
82
    """
83
    def __init__(self, history: CommandHistory):
84
        self.history = history
85
86
    def execute_command(self, command: Command):
87
        if command.execute():
88
            self.history.push(command)
89
90
91
if __name__ == '__main__':
92
    class A:
93
        def a(self, x):
94
            res = x + 1
95
            print(res)
96
    a = A()
97
98
    invoker = Invoker(CommandHistory())
99
    cmd1 = Command(a, 'a', 2)
100
    invoker.execute_command(cmd1)
101
102
    cmd2 = copy.copy(cmd1)
103
    cmd2.args = [12]
104
    invoker.execute_command(cmd2)
105
106
    invoker.execute_command(cmd1)
107
    del cmd2
108
    invoker.execute_command(cmd1)
109