Passed
Push — master ( 3e847f...cc3d3c )
by Steve
03:10
created

RecursiveDefinitionError.__init__()   A

Complexity

Conditions 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.2963

Importance

Changes 0
Metric Value
cc 1
eloc 4
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
ccs 1
cts 3
cp 0.3333
crap 1.2963
1
"""
2
Exceptions raised by the library
3
"""
4 1
import inspect
5 1
import typing
6 1
from abc import ABC
7 1
from typing import Type
8
9
10 1
class LagomException(Exception, ABC):
11
    """All exceptions in this library are instances of a LagomException"""
12
13 1
    pass
14
15
16 1
class InjectableNotResolved(RuntimeError, LagomException):
17
    """
18
    An instance of injectable was consumed in some user code.
19
    This should not occur as lagom should have replaced the injectable
20
    with an object. This likely means the function wasn't bound to
21
    an injection container.
22
    """
23
24 1
    pass
25
26
27 1
class InvalidDependencyDefinition(ValueError, LagomException):
28
    """The provided construction logic is not valid"""
29
30 1
    pass
31
32
33 1
class ClassesCannotBeDecorated(SyntaxError, LagomException):
34
    """Decorating classes is not supported by lagom"""
35
36 1
    dep_type: str
37
38 1
    def __init__(self):
39 1
        super().__init__(
40
            "Decorating classes is not supported by lagom. \n"
41
            "Alternative is to create a factory method and use that: \n"
42
            "factory_func = container.partial(ClassName)"
43
        )
44
45
46 1
class MissingReturnType(SyntaxError, LagomException):
47
    """The function provided doesnt type hint a return"""
48
49 1
    pass
50
51
52 1
class DuplicateDefinition(ValueError, LagomException):
53
    """The type has already been defined somewhere else"""
54
55 1
    pass
56
57
58 1
class TypeOnlyAvailableAsAwaitable(SyntaxError, LagomException):
59
    """The type is only available as Awaitable[T]"""
60
61 1
    dep_type: str
62
63 1
    def __init__(self, dep_type: Type):
64
        """
65
66
        :param dep_type: The type that could not be constructed without Awaitable
67
        """
68 1
        self.dep_type = _dep_type_as_string(dep_type)
69 1
        if inspect.isabstract(dep_type):
70
            super().__init__(
71
                f"Unable to construct type {self.dep_type} as it is only available as an async."
72
                "Try requesting Awaitable[{self.dep_type}] instead"
73
            )
74
75
76 1
class UnableToInvokeBoundFunction(TypeError, LagomException):
77
    """A function bound to the container could not be invoked"""
78
79 1
    unresolvable_deps: typing.List[Type]
80
81 1
    def __init__(self, msg, unresolvable_deps):
82 1
        self.unresolvable_deps = unresolvable_deps
83 1
        unresolvable_string_list = ",".join(d.__name__ for d in unresolvable_deps)
84 1
        super().__init__(
85
            f"{msg}. The container could not construct the following types: {unresolvable_string_list}"
86
        )
87
88
89 1
class UnresolvableType(ValueError, LagomException):
90
    """The type cannot be constructed"""
91
92 1
    dep_type: str
93
94 1
    def __init__(self, dep_type: Type):
95
        """
96
97
        :param dep_type: The type that could not be constructed
98
        """
99 1
        self.dep_type = _dep_type_as_string(dep_type)
100 1
        if inspect.isabstract(dep_type):
101 1
            super().__init__(
102
                f"Unable to construct Abstract type {self.dep_type}."
103
                "Try defining an alias or a concrete class to construct"
104
            )
105
        else:
106 1
            super().__init__(
107
                f"Unable to construct dependency of type {self.dep_type} "
108
                "The constructor probably has some unresolvable dependencies"
109
            )
110
111
112 1
class TypeResolutionBlocked(UnresolvableType):
113
    """The type was explicitly blocked by configuration"""
114
115 1
    def __init__(self, msg: str):
116 1
        super(ValueError, self).__init__(msg)
117
118
119 1
class RecursiveDefinitionError(SyntaxError, LagomException):
120
    """Whilst trying to resolve the type python exceeded the recursion depth"""
121
122 1
    dep_type: Type
123
124 1
    def __init__(self, dep_type: Type):
125
        """
126
        :param dep_type: The type that could not be constructed
127
        """
128
        self.dep_type = dep_type
129
130
        super().__init__(
131
            f"When trying to build dependency of type '{_dep_type_as_string(dep_type)}' python hit a recursion limit. "
132
            "This could indicate a circular definition somewhere."
133
        )
134
135
136 1
class DependencyNotDefined(ValueError, LagomException):
137
    """The type must be explicitly defined in the container"""
138
139 1
    dep_type: Type
140
141 1
    def __init__(self, dep_type: Type):
142
        """
143
        :param dep_type: The type that was not defined
144
        """
145 1
        self.dep_type = dep_type
146 1
        super().__init__(
147
            f"{_dep_type_as_string(dep_type)} has not been defined. "
148
            f"In an explict container all dependencies must be defined"
149
        )
150
151
152 1
class MissingEnvVariable(LagomException):
153
    """
154
    Whilst trying to load settings from environment variables one or more required variables had not been set.
155
    More documentation for this can be found in the lagom.environment module.
156
    """
157
158 1
    def __init__(self, variable_names: typing.List[str]):
159 1
        super().__init__(
160
            f"Expected environment variables not found: {', '.join(variable_names)}"
161
        )
162
163
164 1
class InvalidEnvironmentVariables(LagomException):
165
    """
166
    Whilst trying to load settings from environment variables one or more variables failed the validation rules.
167
    Internally the pydantic library is used to coerce and validate the environment variable from a string into
168
    a python data type.
169
    More documentation for this can be found in the lagom.environment module.
170
    """
171
172 1
    def __init__(self, variable_names: typing.List[str], details: str):
173 1
        super().__init__(
174
            f"Unable to load environment variables: {', '.join(variable_names)} \n {details}"
175
        )
176
177
178 1
class MissingFeature(LagomException):
179
    """This is raised by code in the experimental module. It represents a feature that is planned but not implemented"""
180
181 1
    pass
182
183
184 1
def _dep_type_as_string(dep_type: Type):
185
    # This first check makes 3.6 behave the same as 3.7 and later
186 1
    if hasattr(typing, "GenericMeta") and isinstance(dep_type, typing.GenericMeta):  # type: ignore
187
        return str(dep_type)
188 1
    elif hasattr(typing, "get_origin") and typing.get_origin(dep_type) is not None:  # type: ignore
189
        # repr() gives a more sensible output in version 3.10 for List[X] and others like this
190 1
        return repr(dep_type)
191 1
    elif hasattr(dep_type, "__name__"):
192 1
        return dep_type.__name__
193
194
    return str(dep_type)
195