Completed
Push — master ( 336d1a...d357d6 )
by Konstantinos
20s queued 13s
created

software_patterns.proxy.ProxySubject.__init__()   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
"""Proxy structural software pattern.
2
3
This module contains boiler-plate code to supply the Proxy structural software
4
design pattern, to the client code."""
5
6
from abc import ABC, abstractmethod
7
from typing import Callable, Generic, TypeVar
8
9
T = TypeVar('T')
10
11
12
__all__ = ['ProxySubject', 'Proxy']
13
14
15
class ProxySubjectInterfaceClass(type, Generic[T]):
16
    """Interfacing enabling classes to construct classes (instead of instances).
17
18
    Dynamically creates classes that represent a ProxySubjectInterface.
19
    The created classes automatically gain an abstract method with name given at
20
    creation time. The input name can match the desired named selected to a
21
    proxied object.
22
23
    For example in a scenario where you proxy a remote web server you might
24
    create ProxySubjectInterface with a 'make_request' abstract method where as
25
    in a scenario where the proxied object is a Tensorflow function you might
26
    name the abstract method as 'tensorflow'.
27
28
    Dynamically, creating a class (as this class allows) is useful to adjust to
29
    scenarios like the above.
30
31
    Args:
32
        Generic ([type]): [description]
33
34
    Raises:
35
        NotImplementedError: [description]
36
37
    Returns:
38
        [type]: [description]
39
    """
40
41
    def __new__(mcs, *args, **kwargs):
42
        def __init__(self, proxied_object):
43
            self._proxy_subject = proxied_object
44
45
        def object(self, *args, **kwargs) -> T:
46
            return self._proxy_subject
47
48
        return super().__new__(
49
            mcs,
50
            'ProxySubjectInterface',
51
            (ABC,),
52
            {
53
                '__init__': __init__,
54
                args[0]: object,
55
            },
56
        )
57
58
59
class ProxySubjectInterface(ABC, Generic[T]):
60
    """Proxy Subject interface holding the important 'request' operation.
61
62
    Declares common operations for both ProxySubject and
63
    the Proxy. As long as the client uses ProxySubject's interface, a proxy can
64
    be passed pass to it, instead of a real subject.
65
    """
66
67
    @abstractmethod
68
    def request(self, *args, **kwargs) -> T:
69
        raise NotImplementedError
70
71
72
class ProxySubject(ProxySubjectInterface, Generic[T]):
73
    """
74
    The ProxySubject contains some core business logic. Usually, ProxySubject are
75
    capable of doing some useful work which may also be very slow or sensitive -
76
    e.g. correcting input data. A Proxy can solve these issues without any
77
    changes to the ProxySubject's code.
78
79
    Example:
80
81
        >>> from software_patterns import ProxySubject
82
        >>> proxied_operation = lambda x: x + 1
83
        >>> proxied_operation(1)
84
        2
85
86
        >>> proxied_object = ProxySubject(proxied_operation)
87
        >>> proxied_object.request(1)
88
        2
89
    """
90
91
    def __init__(self, callback: Callable[..., T]):
92
        self._callback = callback
93
94
    def request(self, *args, **kwargs) -> T:
95
        return self._callback(*args, **kwargs)
96
97
98
class Proxy(ProxySubjectInterface, Generic[T]):
99
    """
100
    The Proxy has an interface identical to the ProxySubject.
101
102
    Example:
103
104
        >>> from software_patterns import Proxy
105
        >>> from software_patterns import ProxySubject
106
107
        >>> class ClientProxy(Proxy):
108
        ...  def request(self, *args, **kwargs):
109
        ...   args = [args[0] + 1]
110
        ...   result = super().request(*args, **kwargs)
111
        ...   result += 1
112
        ...   return result
113
114
        >>> proxied_operation = lambda x: x * 2
115
        >>> proxy_subject = ProxySubject(proxied_operation)
116
        >>> proxy_subject.request(3)
117
        6
118
119
        >>> proxy = ClientProxy(proxy_subject)
120
        >>> proxy.request(3)
121
        9
122
    """
123
124
    def __init__(self, proxy_subject: ProxySubject):
125
        self._proxy_subject = proxy_subject
126
127
    def request(self, *args, **kwargs) -> T:
128
        """
129
        The most common applications of the Proxy pattern are lazy loading,
130
        caching, controlling the access, logging, etc. A Proxy can perform one
131
        of these things and then, depending on the result, pass the execution to
132
        the same method in a linked ProxySubject object.
133
        """
134
        return self._proxy_subject.request(*args, **kwargs)
135