dinkleberg 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dinkleberg/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
- from .dependency import Dependency
1
+ from dinkleberg_abc import Dependency, DependencyScope
2
+
2
3
  from .dependency_configurator import DependencyConfigurator
3
- from .dependency_scope import DependencyScope
4
4
 
5
5
  __all__ = ['DependencyScope', 'DependencyConfigurator', 'Dependency']
@@ -1,4 +1,3 @@
1
- import abc
2
1
  import asyncio
3
2
  import inspect
4
3
  import logging
@@ -6,10 +5,9 @@ from inspect import Signature
6
5
  from types import MappingProxyType
7
6
  from typing import AsyncGenerator, Callable, overload, get_type_hints, Mapping, get_origin
8
7
 
9
- from .dependency import Dependency
10
- from .dependency_scope import DependencyScope
8
+ from dinkleberg_abc import DependencyScope, Dependency
11
9
  from .descriptor import Descriptor, Lifetime
12
- from .typing import get_static_params, get_public_methods, is_builtin_type
10
+ from .typing import get_static_params, get_public_methods, is_builtin_type, is_abstract
13
11
 
14
12
  logger = logging.getLogger(__name__)
15
13
 
@@ -58,13 +56,45 @@ class DependencyConfigurator(DependencyScope):
58
56
  if exceptions:
59
57
  raise ExceptionGroup('Errors occurred during closing DependencyConfigurator', exceptions)
60
58
 
61
- def _add(self, lifetime: Lifetime, *, t: type = None, generator: Callable[..., AsyncGenerator] = None,
62
- callable: Callable = None):
63
- if generator is None and callable is None:
64
- raise ValueError('Invalid dependency registration. Either generator or callable must be provided.')
59
+ def _add(self, lifetime: Lifetime, *, t: type = None, i: type = None,
60
+ generator: Callable[..., AsyncGenerator] = None, callable: Callable = None):
61
+ if t is None and i is None and generator is None and callable is None:
62
+ raise ValueError(
63
+ 'Invalid dependency registration. At least one of t, i, generator, or callable must be provided.')
64
+
65
+ if lifetime == 'singleton' and self._parent is not None:
66
+ raise RuntimeError(
67
+ 'Singleton dependencies, which are not instances, can only be registered in the root DependencyConfigurator.')
68
+
69
+ if i is not None:
70
+ if is_builtin_type(i):
71
+ raise ValueError(f'Cannot use built-in type {i} as implementation.')
72
+
73
+ if get_origin(i) is not None:
74
+ raise ValueError(f'Cannot use generic type {i} as implementation.')
75
+
76
+ if is_abstract(i):
77
+ raise ValueError(f'Cannot use abstract class {i} as implementation.')
78
+
65
79
  if t is None:
66
- t = self._infer_type(generator=generator, callable=callable)
67
- self._descriptors[t] = Descriptor(generator=generator, callable=callable, lifetime=lifetime)
80
+ if i is not None:
81
+ t = i
82
+ else:
83
+ t = self._infer_type(generator=generator, callable=callable)
84
+ elif generator is None and callable is None:
85
+ if is_builtin_type(t):
86
+ raise ValueError(
87
+ f'Cannot register built-in type {t} without explicit implementation, generator or callable.')
88
+
89
+ if get_origin(t) is not None:
90
+ raise ValueError(
91
+ f'Cannot register generic type {t} without explicit implementation, generator or callable.')
92
+
93
+ if is_abstract(t):
94
+ raise ValueError(
95
+ f'Cannot register abstract class {t} without explicit implementation, generator or callable.')
96
+
97
+ self._descriptors[t] = Descriptor(implementation=i, generator=generator, callable=callable, lifetime=lifetime)
68
98
 
69
99
  @staticmethod
70
100
  def _infer_type(*, generator: Callable[..., AsyncGenerator], callable: Callable) -> type:
@@ -117,8 +147,26 @@ class DependencyConfigurator(DependencyScope):
117
147
  if t in self._scoped_instances:
118
148
  return self._scoped_instances[t]
119
149
 
120
- if t in self._descriptors:
121
- descriptor = self._descriptors[t]
150
+ descriptor = self._descriptors.get(t)
151
+ if descriptor is None or descriptor['generator'] is None and descriptor['callable'] is None:
152
+ if descriptor is None:
153
+ if is_builtin_type(t):
154
+ raise ValueError(f'Cannot resolve built-in type {t} without explicit registration.')
155
+
156
+ if get_origin(t) is not None:
157
+ raise ValueError(f'Cannot resolve generic type {t} without explicit registration.')
158
+
159
+ if is_abstract(t):
160
+ raise ValueError(f'Cannot resolve abstract class {t} without explicit registration.')
161
+
162
+ factory = t
163
+ else:
164
+ factory = descriptor['implementation'] or t
165
+
166
+ is_generator = False
167
+ lifetime = descriptor['lifetime'] if descriptor else 'transient'
168
+ deps = await self._resolve_deps(factory.__init__)
169
+ else:
122
170
  lifetime = descriptor['lifetime']
123
171
  if lifetime == 'singleton' and self._parent:
124
172
  # we need to resolve singleton from the root scope
@@ -127,20 +175,6 @@ class DependencyConfigurator(DependencyScope):
127
175
  is_generator = descriptor['generator'] is not None
128
176
  factory = descriptor['generator'] or descriptor['callable']
129
177
  deps = await self._resolve_deps(factory)
130
- else:
131
- if is_builtin_type(t):
132
- raise ValueError(f'Cannot resolve built-in type {t} without explicit registration.')
133
-
134
- if get_origin(t) is not None:
135
- raise ValueError(f'Cannot resolve generic type {t} without explicit registration.')
136
-
137
- if inspect.isabstract(t) or t is abc.ABC:
138
- raise ValueError(f'Cannot resolve abstract class {t} without explicit registration.')
139
-
140
- is_generator = False
141
- lifetime = 'transient'
142
- factory = t
143
- deps = await self._resolve_deps(t.__init__)
144
178
 
145
179
  if is_generator:
146
180
  generator = factory(**deps, **kwargs)
@@ -256,6 +290,18 @@ class DependencyConfigurator(DependencyScope):
256
290
  def add_singleton[T, I](self, *, t: type[T], instance: I):
257
291
  ...
258
292
 
293
+ @overload
294
+ def add_singleton[T](self, *, t: type[T]):
295
+ ...
296
+
297
+ @overload
298
+ def add_singleton[I](self, *, i: type[I]):
299
+ ...
300
+
301
+ @overload
302
+ def add_singleton[T, I](self, *, t: type[T], i: type[I]):
303
+ ...
304
+
259
305
  @overload
260
306
  def add_singleton[I](self, *, callable: Callable[..., I]):
261
307
  ...
@@ -272,11 +318,13 @@ class DependencyConfigurator(DependencyScope):
272
318
  def add_singleton[T, I](self, *, t: type[T], generator: Callable[..., AsyncGenerator[I]]):
273
319
  ...
274
320
 
275
- def add_singleton[T, I](self, *, t: type[T] = None, generator: Callable[..., AsyncGenerator[I]] = None,
321
+ def add_singleton[T, I](self, *, t: type[T] = None, i: type[I] = None,
322
+ generator: Callable[..., AsyncGenerator[I]] = None,
276
323
  callable: Callable[..., I] = None, instance: I = None):
277
324
  self._raise_if_closed()
325
+
278
326
  if instance is None:
279
- self._add('singleton', t=t, generator=generator, callable=callable)
327
+ self._add('singleton', t=t, i=i, generator=generator, callable=callable)
280
328
  return
281
329
  elif t is None:
282
330
  t = type(instance)
@@ -285,6 +333,18 @@ class DependencyConfigurator(DependencyScope):
285
333
 
286
334
  self._singleton_instances[t] = instance
287
335
 
336
+ @overload
337
+ def add_scoped[T](self, *, t: type[T]):
338
+ ...
339
+
340
+ @overload
341
+ def add_scoped[I](self, *, i: type[I]):
342
+ ...
343
+
344
+ @overload
345
+ def add_scoped[T, I](self, *, t: type[T], i: type[I]):
346
+ ...
347
+
288
348
  @overload
289
349
  def add_scoped[I](self, *, callable: Callable[..., I]):
290
350
  ...
@@ -301,10 +361,22 @@ class DependencyConfigurator(DependencyScope):
301
361
  def add_scoped[T, I](self, *, t: type[T], generator: Callable[..., AsyncGenerator[I]]):
302
362
  ...
303
363
 
304
- def add_scoped[T, I](self, *, t: type[T] = None, generator: Callable[..., AsyncGenerator[I]] = None,
305
- callable: Callable[..., I] = None):
364
+ def add_scoped[T, I](self, *, t: type[T] = None, i: type[I] = None,
365
+ generator: Callable[..., AsyncGenerator[I]] = None, callable: Callable[..., I] = None):
306
366
  self._raise_if_closed()
307
- self._add('scoped', t=t, generator=generator, callable=callable)
367
+ self._add('scoped', t=t, i=i, generator=generator, callable=callable)
368
+
369
+ @overload
370
+ def add_transient[T](self, *, t: type[T]):
371
+ ...
372
+
373
+ @overload
374
+ def add_transient[I](self, *, i: type[I]):
375
+ ...
376
+
377
+ @overload
378
+ def add_transient[T, I](self, *, t: type[T], i: type[I]):
379
+ ...
308
380
 
309
381
  @overload
310
382
  def add_transient[I](self, *, callable: Callable[..., I]):
@@ -314,6 +386,6 @@ class DependencyConfigurator(DependencyScope):
314
386
  def add_transient[T, I](self, *, t: type[T], callable: Callable[..., I]):
315
387
  ...
316
388
 
317
- def add_transient[T, I](self, *, t: type[T] = None, callable: Callable[..., I] = None):
389
+ def add_transient[T, I](self, *, t: type[T] = None, i: type[I] = None, callable: Callable[..., I] = None):
318
390
  self._raise_if_closed()
319
- self._add('transient', t=t, callable=callable)
391
+ self._add('transient', i=i, t=t, callable=callable)
dinkleberg/descriptor.py CHANGED
@@ -4,6 +4,7 @@ Lifetime = Union[Literal['singleton'], Literal['scoped'], Literal['transient']]
4
4
 
5
5
 
6
6
  class Descriptor(TypedDict):
7
+ implementation: Optional[type]
7
8
  generator: Optional[Callable[..., AsyncGenerator]]
8
9
  callable: Optional[Callable]
9
10
  lifetime: Lifetime
@@ -0,0 +1,13 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from _dinkleberg_fastapi import di
5
+
6
+ try:
7
+ from _dinkleberg_fastapi import di
8
+
9
+ __all__ = ['di']
10
+ except ImportError:
11
+ raise ImportError(
12
+ "dinkleberg-fastapi is not installed. Please install it with 'pip install dinkleberg[fastapi]' to use FastAPI integration."
13
+ )
dinkleberg/typing.py CHANGED
@@ -1,8 +1,13 @@
1
+ import abc
1
2
  import inspect
2
3
  from inspect import Parameter, signature
3
4
  from typing import Callable, get_origin
4
5
 
5
6
 
7
+ def is_abstract(t: type) -> bool:
8
+ return inspect.isabstract(t) or t is abc.ABC
9
+
10
+
6
11
  def is_builtin_type(t: type) -> bool:
7
12
  origin = get_origin(t) or t
8
13
  return getattr(origin, '__module__', None) in ('builtins', 'typing', 'types')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dinkleberg
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Your friendly neighbour when it comes to dependency management.
5
5
  Project-URL: Homepage, https://github.com/DavidVollmers/dinkleberg
6
6
  Project-URL: Documentation, https://github.com/DavidVollmers/dinkleberg/blob/main/libs/dinkleberg/README.md
@@ -17,6 +17,9 @@ Classifier: Operating System :: OS Independent
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: >=3.13
19
19
  Description-Content-Type: text/markdown
20
+ Requires-Dist: dinkleberg-abc>=0.3.0
21
+ Provides-Extra: fastapi
22
+ Requires-Dist: dinkleberg-fastapi>=0.3.0; extra == "fastapi"
20
23
 
21
24
  # dinkleberg
22
25
 
@@ -0,0 +1,9 @@
1
+ dinkleberg/__init__.py,sha256=6gowHoL3xTWd2iyU09upgjXKUv_WWp6Tw1iqw2uz7Vc,192
2
+ dinkleberg/dependency_configurator.py,sha256=vsXTHFUkhW5_bR95qFYOzaio-UwupIkS9qjPL2e8s4o,14356
3
+ dinkleberg/descriptor.py,sha256=ZtVwJihvCLugakHx201iX7Jm4A50Z0SzPu9zeHfD320,349
4
+ dinkleberg/typing.py,sha256=qAdSQomntQfI4bSWc9X32DXGftuyj56WP7nG3Pia7ZE,942
5
+ dinkleberg/fastapi/__init__.py,sha256=boj1ywpWhuuOjHLwdUha2EPBG2f1zbc374N35k1CJxk,352
6
+ dinkleberg-0.3.0.dist-info/METADATA,sha256=sGBRFKAmbtQS1vc79a5tyotUivlciNlhZAbwYqSC1FQ,1555
7
+ dinkleberg-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
+ dinkleberg-0.3.0.dist-info/top_level.txt,sha256=6TjbaJ_eyRzoxzz2r1Eu0hgYfxBBM2bOV3u_TJ8yjjw,11
9
+ dinkleberg-0.3.0.dist-info/RECORD,,
dinkleberg/dependency.py DELETED
@@ -1,2 +0,0 @@
1
- class Dependency:
2
- pass
@@ -1,15 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
-
4
- class DependencyScope(ABC):
5
- @abstractmethod
6
- async def resolve[T](self, t: type[T], **kwargs) -> T:
7
- pass
8
-
9
- @abstractmethod
10
- async def close(self) -> None:
11
- pass
12
-
13
- @abstractmethod
14
- def scope(self) -> 'DependencyScope':
15
- pass
@@ -1,10 +0,0 @@
1
- dinkleberg/__init__.py,sha256=HGu8mjYt-Fa0y9_aMtJZFYo3s9Yb0q5dmbMKtHrAV_k,217
2
- dinkleberg/dependency.py,sha256=YcGvuvhoBRxShgSRaznKyZg49GCgOWy_L8HD0sXAMYo,29
3
- dinkleberg/dependency_configurator.py,sha256=N0mF78B9HnCKPwhDo4DTt8aDGbLwKEPFqEfo5ZtrrxA,11877
4
- dinkleberg/dependency_scope.py,sha256=sufXjQ6F62ZfohItUnrV7Fvh8gSSc7a6orO6PXu19Vw,318
5
- dinkleberg/descriptor.py,sha256=mVI00K1M2m4Rl5ANatGMeB_HpzeFONoULwg0UmNxfQ0,313
6
- dinkleberg/typing.py,sha256=_T2plzKBU_GTMNazpU8bqKuqTvQotJPuWEHQH2dF76g,841
7
- dinkleberg-0.2.1.dist-info/METADATA,sha256=Mv6RezK3xmMBpnRY0DKkwXSxPnm6mTezMRcMWFMiNs8,1430
8
- dinkleberg-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
- dinkleberg-0.2.1.dist-info/top_level.txt,sha256=6TjbaJ_eyRzoxzz2r1Eu0hgYfxBBM2bOV3u_TJ8yjjw,11
10
- dinkleberg-0.2.1.dist-info/RECORD,,