GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 0bdc53...7603ec )
by Raphaël
01:23
created

DependencyInjector   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Test Coverage

Coverage 88.16%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 224
ccs 67
cts 76
cp 0.8816
rs 9.3999
wmc 33

11 Methods

Rating   Name   Duplication   Size   Complexity  
A _set_singleton() 0 14 2
A _get_singleton() 0 16 3
A __init__() 0 3 1
A register() 0 23 2
B _generate_arguments_dict() 0 31 4
A _is_object_init() 0 14 3
B get() 0 27 4
A has_service() 0 16 3
C _get_argument() 0 32 7
A _get_instance() 0 12 2
B register_singleton() 0 24 2
1 1
from inspect import signature
2 1
from inspect import Parameter
3 1
from collections import OrderedDict
4
5 1
from pyjection.service import Service
6 1
from pyjection.reference import Reference
7 1
from pyjection.helper import get_service_subject_identifier
8
9
10 1
class DependencyInjector(object):
11
12 1
    def __init__(self):
13 1
        self._services = dict()
14 1
        self._singletons = dict()
15
16 1
    def register(self, service_subject, identifier=None):
17
        """
18
        Register a new service in the dependency injector
19
20
        This service can be :
21
            * A class that will be instantiated when called
22
            * An already instantiated instance that will be returned
23
24
        If no identifier is passed, it will be the class name in snake_case
25
26
        :param service_subject: The class or instance
27
        :type service_subject: mixed
28
        :param identifier: The identifier used to later retrieve a service instance
29
        :type identifier: string
30
31
        :return: Return the newly created service entry
32
        :rtype: Service
33
        """
34 1
        if identifier is None:
35 1
            identifier = get_service_subject_identifier(service_subject)
36 1
        service = Service(service_subject)
37 1
        self._services[identifier] = service
38 1
        return service
39
40 1
    def register_singleton(self, service_subject, identifier=None):
41
        """
42
        Register a new singleton service in in the dependency injector
43
44
        This service can be :
45
            * A class that will be instantiated when called
46
            * An already instantiated instance that will be returned
47
48
        If no identifier is passed, it will be the class name in snake_case
49
50
        :param service_subject: The class or instance
51
        :type service_subject: mixed
52
        :param identifier: The identifier used to later retrieve a service singleton
53
        :type identifier: string
54
55
        :return: Return the newly created dependency entry
56
        :rtype: Service
57
        """
58 1
        if identifier is None:
59 1
            identifier = get_service_subject_identifier(service_subject)
60 1
        service = Service(service_subject)
61 1
        service.is_singleton = True
62 1
        self._services[identifier] = service
63 1
        return service
64
65 1
    def get(self, identifier):
66
        """
67
        Instantiate and retrieve the service matching this identifier
68
69
        If the service has been self has a singleton the same service object
70
        will be return each time this service is asked
71
72
        :param identifier: The identifier or the class to retrieve
73
        :type identifier: mixed
74
        :return: The instantiated object
75
        :rtype: mixed
76
        """
77 1
        if isinstance(identifier, str) is False:
78 1
            identifier = get_service_subject_identifier(identifier)
79
80 1
        if self.has_service(identifier) is False:
81 1
            raise Exception("No service has been declared with this ID")
82
83 1
        service = self._services[identifier]
84 1
        instance = self._get_singleton(identifier, service)
85 1
        if instance is not None:
86 1
            return instance
87
88 1
        instance = self._get_instance(service)
89 1
        self._set_singleton(identifier, instance, service)
90
91 1
        return instance
92
93 1
    def has_service(self, identifier):
94
        """
95
        Check if the service matching the given identifier
96
        has already been declared
97
98
        :param identifier: Name of the service or the class
99
        :type identifier: mixed
100
        :return: Whether or not the service exists
101
        :rtype: boolean
102
        """
103 1
        if isinstance(identifier, str) is False:
104 1
            identifier = get_service_subject_identifier(identifier)
105
106 1
        if identifier in self._services:
107 1
            return True
108 1
        return False
109
110 1
    def _get_singleton(self, identifier, service):
111
        """
112
        Return the singleton if it has been setted and
113
        the service represents a singleton
114
115
        :param identifier: the singleton identifier
116
        :param service: The service we need the singleton for
117
        :type identifier: string
118
        :type service: Service
119
120
        :return: The singleton instance or None
121
        :rtype: mixed
122
        """
123 1
        if service.is_singleton is True and identifier in self._singletons:
124 1
            return self._singletons[identifier]
125 1
        return None
126
127 1
    def _set_singleton(self, identifier, instance, service):
128
        """
129
        Set the instance as a singleton in the dict
130
        if the service represents a singleton
131
132
        :param identifier: the singleton identifier
133
        :param service: The service we want to set a singleton for
134
        :param instance: The singleton instance
135
        :type identifier: string
136
        :type service: Service
137
        :type instance: mixed
138
        """
139 1
        if service.is_singleton is True:
140 1
            self._singletons[identifier] = instance
141
142 1
    def _get_instance(self, service):
143
        """
144
        Return the instantiated object for the given service
145
146
        :param service: The service we need an instance for
147
        :type service: Service
148
        :return: The instantiated object
149
        """
150 1
        if service.type == 'instance':
151 1
            return service.subject
152 1
        arguments = self._generate_arguments_dict(service)
153 1
        return service.subject(**arguments)
154
155 1
    def _generate_arguments_dict(self, service):
156
        """
157
        Generate a dict containing all the parameters values
158
        required to Instantiate the service.
159
160
        An exception is raised if a mandatory argument cannot be
161
        retrieved.
162
163
        :param service: The service that needs to be instantiated
164
        :type service: Service
165
        :return: The parameters values to use to instantiate the service
166
        :rtype: dict
167
        """
168 1
        arguments = dict()
169
170
        # We can't use signature on object __init__
171 1
        if self._is_object_init(service.subject) is True:
172
            return arguments
173
174 1
        sig = signature(service.subject.__init__)
175 1
        method_parameters = OrderedDict(sig.parameters)
176
177
        # Pop the first param since it's the self class instance
178 1
        method_parameters.popitem(False)
179
180 1
        for method_parameter in method_parameters.values():
181 1
            argument = self._get_argument(service, method_parameter)
182 1
            if argument is not None:
183
                arguments[method_parameter.name] = argument
184
185 1
        return arguments
186
187 1
    def _is_object_init(self, subject):
188
        """
189
        Check if the __init__ method for the object comes from
190
        the default object or has been overridden
191
192
        :param subject: The subject we want to check the __init__ for
193
        :type subject: mixed
194
195
        :return: Whether the __init__ method is the default on or not
196
        :rtype: boolean
197
        """
198 1
        if '__objclass__' in dir(subject.__init__) and subject.__init__.__objclass__ == object:
199
            return True
200 1
        return False
201
202 1
    def _get_argument(self, service, method_parameter):
203
        """
204
        Retrieve the argument value for the given service
205
206
        :param service: The service we need an argument for
207
        :param method_parameter: The parameter we need the value for
208
        :type service: Service
209
        :type method_parameter: Parameter
210
        :return: The argument value
211
        :rtype: mixed
212
        """
213
        # First check if we specified this argument for the service
214 1
        if method_parameter.name in service.arguments:
215
            value = service.arguments[method_parameter.name]
216
            # The value references an other dependency service
217
            if isinstance(value, Reference):
218
                return self.get(value.name)
219
            return value
220
221
        # Then check if another service has this name
222 1
        if self.has_service(method_parameter.name):
223
            return self.get(method_parameter.name)
224
225
        # If the parameter is *args or **kwargs then we don't raise any exception
226 1
        if method_parameter.kind == Parameter.VAR_POSITIONAL or \
227
                method_parameter.kind == Parameter.VAR_KEYWORD:
228 1
            return None
229
        # If the parameter has a default value then we don't raise any exception
230 1
        if method_parameter.default is not Parameter.empty:
231 1
            return None
232
233
        raise Exception("A required argument is not set (%s)" % method_parameter.name)
234