1
|
|
|
from typing import Tuple |
2
|
|
|
import attr |
3
|
|
|
|
4
|
|
|
from so_magic.utils import Subject, Command, CommandFactoryInterface, CommandFactoryType |
5
|
|
|
|
6
|
|
|
|
7
|
|
|
class BaseCommandFactory(metaclass=CommandFactoryType): |
8
|
|
|
pass |
9
|
|
|
|
10
|
|
|
|
11
|
|
|
@BaseCommandFactory.register_as_subclass('generic') |
12
|
|
|
class GenericCommandFactory(CommandFactoryInterface): |
13
|
|
|
"""Command Factory that constructs a command given all the necessary arguments. |
14
|
|
|
|
15
|
|
|
Assumes the 1st argument is the 'receiver' (see Command module), |
16
|
|
|
2nd is the method to call on the receiver and the rest are the method's runtime arguments. |
17
|
|
|
""" |
18
|
|
|
def construct(self, *args, **kwargs) -> Command: |
19
|
|
|
"""Construct a command object (Command class instance). |
20
|
|
|
|
21
|
|
|
Assumes the 1st argument is the 'receiver' (see Command module), |
22
|
|
|
2nd is the method to call on the receiver and the rest are the method's runtime arguments. |
23
|
|
|
|
24
|
|
|
Returns: |
25
|
|
|
Command: the command object |
26
|
|
|
""" |
27
|
|
|
return Command(*args, **kwargs) |
28
|
|
|
|
29
|
|
|
|
30
|
|
|
@BaseCommandFactory.register_as_subclass('function') |
31
|
|
|
class FunctionCommandFactory(CommandFactoryInterface): |
32
|
|
|
"""Command Factory that constructs a command assuming the 1st argument is a python function. |
33
|
|
|
|
34
|
|
|
Assumes that the function (1st argument) acts as the the 'receiver' (see Command module), |
35
|
|
|
2nd is the method to call on the receiver and the rest are the method's runtime arguments. |
36
|
|
|
""" |
37
|
|
|
def construct(self, *args, **kwargs) -> Command: |
38
|
|
|
"""Construct a command object (Command class instance). |
39
|
|
|
|
40
|
|
|
Assumes that the 1st argument is a python function and that it acts as the the 'receiver' (see Command module). |
41
|
|
|
The rest are the function's runtime arguments. |
42
|
|
|
|
43
|
|
|
Raises: |
44
|
|
|
RuntimeError: [description] |
45
|
|
|
|
46
|
|
|
Returns: |
47
|
|
|
Command: [description] |
48
|
|
|
""" |
49
|
|
|
if len(args) < 1: |
50
|
|
|
raise RuntimeError("Will break") |
51
|
|
|
return Command(args[0], '__call__', *args[1:]) |
52
|
|
|
|
53
|
|
|
|
54
|
|
|
class CommandFactory: |
55
|
|
|
"""A factory class able to construct new command objects.""" |
56
|
|
|
constructors = {k: v().construct for k, v in BaseCommandFactory.subclasses.items()} |
57
|
|
|
|
58
|
|
|
@classmethod |
59
|
|
|
def pick(cls, *args, **kwargs): |
60
|
|
|
decision = {True: 'function', False: 'generic'} |
61
|
|
|
is_function = hasattr(args[0], '__code__') |
62
|
|
|
dec2 = {'function': lambda x: x[0].__code__.co_name, 'generic': lambda x: type(x[0]).__name__ + '-' + x[1]} |
63
|
|
|
return decision[is_function], kwargs.get('name', dec2[decision[is_function]](args)) |
64
|
|
|
|
65
|
|
|
@classmethod |
66
|
|
|
def create(cls, *args, **kwargs) -> Tuple[Command, str]: |
67
|
|
|
|
68
|
|
|
key, name = cls.pick(*args, **kwargs) |
69
|
|
|
if len(args) < 1: |
70
|
|
|
raise RuntimeError(args) |
71
|
|
|
return cls.constructors[key](*args), name |
72
|
|
|
|
73
|
|
|
|
74
|
|
|
@attr.s |
75
|
|
|
class MagicCommandFactory(Subject): |
76
|
|
|
"""Instances of this class act as callable command factories that notify, |
77
|
|
|
subscribed observers/listeners upon new command object creation. |
78
|
|
|
|
79
|
|
|
Args: |
80
|
|
|
command_factory (CommandFactory, optional): an instance of a CommandFActory |
81
|
|
|
""" |
82
|
|
|
command_factory = attr.ib(init=True, default=CommandFactory()) |
83
|
|
|
|
84
|
|
|
def __call__(self, *args, **kwargs): |
85
|
|
|
self.state, self.name = self.command_factory.create(*args, **kwargs) |
86
|
|
|
self.notify() |
87
|
|
|
return self.state |
88
|
|
|
|