Coverage for src/diy/errors.py: 66%
38 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-10 22:09 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-10 22:09 +0200
1from __future__ import annotations
3from typing import Any
6class DiyError(Exception):
7 pass
10class UninstanciableTypeError(DiyError):
11 abstract: type[Any]
13 def __init__(self, abstract: type[Any]) -> None:
14 message = f"Can't instantiate type '{abstract.__qualname__}', since it's __init__ method does not accept 'self' as its first argument!"
15 super().__init__(message)
16 self.abstract = abstract
19class UnsupportedParameterTypeError(DiyError):
20 pass
23class UnresolvableDependencyError(DiyError):
24 """
25 Gets thrown when a :class:`Container` tries to instantiate a type, but not
26 all requirements can be automatically resolved.
28 ## Example
30 Imagine we want to issue HTTP requests to an external API. As a security
31 measure, that API requires us to send a secret token in a header:
32 ```python
33 class ApiClient:
34 def __init__(self, auth_token: str):
35 self.auth_token = auth_token
37 def request_things():
38 url = "http://my-api.com/things"
39 return requests.get(url, headers={token: self.auth_token}).json()
40 ```
42 if we don't tell our container what to use.
43 """
45 def __init__(self, abstract: type[Any], known: list[type[Any]]) -> None:
46 super().__init__(f"Failed to resolve an instance of '{abstract.__qualname__}'")
47 self.abstract = abstract
48 enumeration = "\n".join([f"- {x.__qualname__}" for x in known])
49 note = f"Known types are:\n{enumeration}"
50 self.add_note(note)
53class MissingReturnTypeAnnotationError(DiyError):
54 """
55 Gets thrown when trying to register a builder function for a
56 :class:`Specification`, but the function is missing a return type
57 annotation.
59 In this case, we can not identify which type the function tries to build,
60 without leveraging extensive mechanisms such as parsing its body
61 on-the-fly. This would be be bad for performance and also feels a bit too
62 much.
63 """
65 def __init__(self) -> None:
66 message = "A builder function requires a return type annotation."
67 super().__init__(message)
68 self.add_note(
69 """If you e.g. define a function
70 @spec.builder
71 def my_builder():
72 return MyType()
74you can add a return type like this:
76 @spec.builder
77 def my_builder() -> MyType:
78 return MyType()
80diy can't infer this without extensive measures, so you as a user are required
81to provide proper annotations.
82"""
83 )
86class MissingConstructorKeywordArgumentError(DiyError):
87 def __init__(self, abstract: type[Any], name: str) -> None:
88 message = f"Tried to register partial builder for parameter '{abstract.__qualname__}'::'{name}' of type , but its __init__ function does not have a keyword argument named '{name}'!"
89 super().__init__(message)
92class MissingConstructorKeywordTypeAnnotationError(DiyError):
93 def __init__(self, abstract: type[Any], name: str) -> None:
94 message = f"Tried to build an instance of '{abstract.__qualname__}', but the '{name}' parameter is missing a type annotation."
95 super().__init__(message)
96 self.add_note(
97 "Either register an explicit builder function via diy.Specification.builders.add, or provide a type annotation for a type that is already known or can be automatically resolved."
98 )
101class InvalidConstructorKeywordArgumentError(DiyError):
102 def __init__(
103 self, abstract: type[Any], name: str, provided: type[Any], required: type[Any]
104 ) -> None:
105 target = f"'{abstract.__qualname__}::{name}"
106 message = f"Tried to register partial builder for '{target}'. The builder returns '{provided.__qualname__}', but '{target}' accepts '{required.__qualname__}'!"
107 super().__init__(message)