dinkleberg 0.1.0__py3-none-any.whl → 0.2.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/dependency_configurator.py +37 -7
- dinkleberg/typing.py +6 -1
- {dinkleberg-0.1.0.dist-info → dinkleberg-0.2.0.dist-info}/METADATA +34 -34
- dinkleberg-0.2.0.dist-info/RECORD +10 -0
- {dinkleberg-0.1.0.dist-info → dinkleberg-0.2.0.dist-info}/WHEEL +2 -1
- dinkleberg-0.2.0.dist-info/top_level.txt +1 -0
- dinkleberg-0.1.0.dist-info/RECORD +0 -9
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import abc
|
|
1
2
|
import asyncio
|
|
2
3
|
import inspect
|
|
3
4
|
import logging
|
|
@@ -8,7 +9,7 @@ from typing import AsyncGenerator, Callable, overload, get_type_hints, Mapping,
|
|
|
8
9
|
from .dependency import Dependency
|
|
9
10
|
from .dependency_scope import DependencyScope
|
|
10
11
|
from .descriptor import Descriptor, Lifetime
|
|
11
|
-
from .typing import get_static_params, get_public_methods
|
|
12
|
+
from .typing import get_static_params, get_public_methods, is_builtin
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
14
15
|
|
|
@@ -60,7 +61,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
60
61
|
def _add(self, lifetime: Lifetime, *, t: type = None, generator: Callable[..., AsyncGenerator] = None,
|
|
61
62
|
callable: Callable = None):
|
|
62
63
|
if generator is None and callable is None:
|
|
63
|
-
raise ValueError('Invalid dependency
|
|
64
|
+
raise ValueError('Invalid dependency registration. Either generator or callable must be provided.')
|
|
64
65
|
if t is None:
|
|
65
66
|
t = self._infer_type(generator=generator, callable=callable)
|
|
66
67
|
self._descriptors[t] = Descriptor(generator=generator, callable=callable, lifetime=lifetime)
|
|
@@ -103,6 +104,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
103
104
|
return None
|
|
104
105
|
|
|
105
106
|
# TODO circular dependency detection
|
|
107
|
+
# TODO singleton race condition prevention (async.Lock)
|
|
106
108
|
async def resolve[T](self, t: type[T], **kwargs) -> T:
|
|
107
109
|
self._raise_if_closed()
|
|
108
110
|
|
|
@@ -126,10 +128,12 @@ class DependencyConfigurator(DependencyScope):
|
|
|
126
128
|
factory = descriptor['generator'] or descriptor['callable']
|
|
127
129
|
deps = await self._resolve_deps(factory)
|
|
128
130
|
else:
|
|
129
|
-
|
|
130
|
-
if origin is not None:
|
|
131
|
+
if get_origin(t) is not None:
|
|
131
132
|
raise ValueError(f'Cannot resolve generic type {t} without explicit registration.')
|
|
132
133
|
|
|
134
|
+
if inspect.isabstract(t) or t is abc.ABC:
|
|
135
|
+
raise ValueError(f'Cannot resolve abstract class {t} without explicit registration.')
|
|
136
|
+
|
|
133
137
|
is_generator = False
|
|
134
138
|
lifetime = 'transient'
|
|
135
139
|
factory = t
|
|
@@ -143,9 +147,21 @@ class DependencyConfigurator(DependencyScope):
|
|
|
143
147
|
raise RuntimeError(f'Generator {t} did not yield any value.')
|
|
144
148
|
|
|
145
149
|
self._active_generators.append(generator)
|
|
150
|
+
elif asyncio.iscoroutinefunction(factory):
|
|
151
|
+
instance = await factory(**deps, **kwargs)
|
|
146
152
|
else:
|
|
147
153
|
instance = factory(**deps, **kwargs)
|
|
148
154
|
|
|
155
|
+
if isinstance(instance, AsyncGenerator):
|
|
156
|
+
try:
|
|
157
|
+
if is_generator:
|
|
158
|
+
raise RuntimeError(f'Generator {t} yielded another generator. Nested generators are not supported.')
|
|
159
|
+
else:
|
|
160
|
+
raise RuntimeError(
|
|
161
|
+
f'Callable {t} returned a generator. This is most likely due to an invalid dependency registration.')
|
|
162
|
+
finally:
|
|
163
|
+
await instance.aclose()
|
|
164
|
+
|
|
149
165
|
self._wrap_instance(instance)
|
|
150
166
|
|
|
151
167
|
if lifetime == 'singleton':
|
|
@@ -164,8 +180,10 @@ class DependencyConfigurator(DependencyScope):
|
|
|
164
180
|
if not param.annotation or param.annotation is inspect.Parameter.empty:
|
|
165
181
|
continue
|
|
166
182
|
|
|
183
|
+
if is_builtin(param.annotation):
|
|
184
|
+
continue
|
|
185
|
+
|
|
167
186
|
# TODO handle more complex cases (e.g., Union, Optional, etc.)
|
|
168
|
-
# TODO handle native types (int, str, etc.)
|
|
169
187
|
names.append(param.name)
|
|
170
188
|
tasks.append(self.resolve(param.annotation))
|
|
171
189
|
|
|
@@ -196,7 +214,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
196
214
|
|
|
197
215
|
# TODO handle __slots__
|
|
198
216
|
def _wrap_instance(self, instance):
|
|
199
|
-
if getattr(instance, '
|
|
217
|
+
if getattr(instance, '__dinkleberg__', False):
|
|
200
218
|
return
|
|
201
219
|
|
|
202
220
|
methods = get_public_methods(instance)
|
|
@@ -222,7 +240,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
222
240
|
raise NotImplementedError('Synchronous methods with Dependency() defaults are not supported.')
|
|
223
241
|
|
|
224
242
|
try:
|
|
225
|
-
setattr(instance, '
|
|
243
|
+
setattr(instance, '__dinkleberg__', True)
|
|
226
244
|
except (AttributeError, TypeError):
|
|
227
245
|
# Some objects (like those with __slots__) might not allow new attributes
|
|
228
246
|
pass
|
|
@@ -284,3 +302,15 @@ class DependencyConfigurator(DependencyScope):
|
|
|
284
302
|
callable: Callable[..., I] = None):
|
|
285
303
|
self._raise_if_closed()
|
|
286
304
|
self._add('scoped', t=t, generator=generator, callable=callable)
|
|
305
|
+
|
|
306
|
+
@overload
|
|
307
|
+
def add_transient[I](self, *, callable: Callable[..., I]):
|
|
308
|
+
...
|
|
309
|
+
|
|
310
|
+
@overload
|
|
311
|
+
def add_transient[T, I](self, *, t: type[T], callable: Callable[..., I]):
|
|
312
|
+
...
|
|
313
|
+
|
|
314
|
+
def add_transient[T, I](self, *, t: type[T] = None, callable: Callable[..., I] = None):
|
|
315
|
+
self._raise_if_closed()
|
|
316
|
+
self._add('transient', t=t, callable=callable)
|
dinkleberg/typing.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from inspect import Parameter, signature
|
|
3
|
-
from typing import Callable
|
|
3
|
+
from typing import Callable, get_origin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def is_builtin(t: type) -> bool:
|
|
7
|
+
origin = get_origin(t) or t
|
|
8
|
+
return getattr(origin, '__module__', None) == 'builtins'
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
def get_static_params(func: Callable) -> list[Parameter]:
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: dinkleberg
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Your friendly neighbour when it comes to dependency management.
|
|
5
|
-
Project-URL: Homepage, https://github.com/DavidVollmers/dinkleberg
|
|
6
|
-
Project-URL: Documentation, https://github.com/DavidVollmers/dinkleberg/blob/main/libs/dinkleberg/README.md
|
|
7
|
-
Project-URL: Repository, https://github.com/DavidVollmers/dinkleberg.git
|
|
8
|
-
Project-URL: Issues, https://github.com/DavidVollmers/dinkleberg/issues
|
|
9
|
-
Project-URL: Changelog, https://github.com/DavidVollmers/dinkleberg/blob/main/CHANGELOG.md
|
|
10
|
-
Keywords:
|
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier:
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier:
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Requires-Python: >=3.13
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
|
|
21
|
-
# dinkleberg
|
|
22
|
-
|
|
23
|
-
> "And this is where I'd put my working dependencies... IF I HAD ANY!"
|
|
24
|
-
|
|
25
|
-
**dinkleberg** is a lightweight Python utility designed to make dependency management less of a neighborhood feud. Built
|
|
26
|
-
to work seamlessly in any project, it ensures your environment stays green—unlike the guy's next door.
|
|
27
|
-
|
|
28
|
-
## Installation
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
pip install dinkleberg
|
|
32
|
-
|
|
33
|
-
uv add dinkleberg
|
|
34
|
-
```
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dinkleberg
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Your friendly neighbour when it comes to dependency management.
|
|
5
|
+
Project-URL: Homepage, https://github.com/DavidVollmers/dinkleberg
|
|
6
|
+
Project-URL: Documentation, https://github.com/DavidVollmers/dinkleberg/blob/main/libs/dinkleberg/README.md
|
|
7
|
+
Project-URL: Repository, https://github.com/DavidVollmers/dinkleberg.git
|
|
8
|
+
Project-URL: Issues, https://github.com/DavidVollmers/dinkleberg/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/DavidVollmers/dinkleberg/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: dependency,package-management,automation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# dinkleberg
|
|
22
|
+
|
|
23
|
+
> "And this is where I'd put my working dependencies... IF I HAD ANY!"
|
|
24
|
+
|
|
25
|
+
**dinkleberg** is a lightweight Python utility designed to make dependency management less of a neighborhood feud. Built
|
|
26
|
+
to work seamlessly in any project, it ensures your environment stays green—unlike the guy's next door.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install dinkleberg
|
|
32
|
+
|
|
33
|
+
uv add dinkleberg
|
|
34
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
dinkleberg/__init__.py,sha256=HGu8mjYt-Fa0y9_aMtJZFYo3s9Yb0q5dmbMKtHrAV_k,217
|
|
2
|
+
dinkleberg/dependency.py,sha256=YcGvuvhoBRxShgSRaznKyZg49GCgOWy_L8HD0sXAMYo,29
|
|
3
|
+
dinkleberg/dependency_configurator.py,sha256=vc3m23BopF0rSFDRO-Fj-8cGmU4bbGve1Qa1ymYsF0g,11727
|
|
4
|
+
dinkleberg/dependency_scope.py,sha256=sufXjQ6F62ZfohItUnrV7Fvh8gSSc7a6orO6PXu19Vw,318
|
|
5
|
+
dinkleberg/descriptor.py,sha256=mVI00K1M2m4Rl5ANatGMeB_HpzeFONoULwg0UmNxfQ0,313
|
|
6
|
+
dinkleberg/typing.py,sha256=kVawkc46Jblqa4XUdangv3ZL8X3RyP8TpZ08EU7RbP0,815
|
|
7
|
+
dinkleberg-0.2.0.dist-info/METADATA,sha256=aVA9a_YmlnDJPMFVirD81BwTPBMBizhmnv02mf7Mkwc,1430
|
|
8
|
+
dinkleberg-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
dinkleberg-0.2.0.dist-info/top_level.txt,sha256=6TjbaJ_eyRzoxzz2r1Eu0hgYfxBBM2bOV3u_TJ8yjjw,11
|
|
10
|
+
dinkleberg-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dinkleberg
|
|
@@ -1,9 +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=IeGSUUbaaxbcb1I6NcIqWyqGs0iWygVzjXzsj0VmOGg,10473
|
|
4
|
-
dinkleberg/dependency_scope.py,sha256=sufXjQ6F62ZfohItUnrV7Fvh8gSSc7a6orO6PXu19Vw,318
|
|
5
|
-
dinkleberg/descriptor.py,sha256=mVI00K1M2m4Rl5ANatGMeB_HpzeFONoULwg0UmNxfQ0,313
|
|
6
|
-
dinkleberg/typing.py,sha256=9AGDNz_aQ8p7ToyJwkj2fg92o0L3-zXwZBEmoTNS1to,670
|
|
7
|
-
dinkleberg-0.1.0.dist-info/METADATA,sha256=vU4SQ-0fafwgtOETUAtr_-zuPTEJX4Haesa4i8OT8L8,1396
|
|
8
|
-
dinkleberg-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
9
|
-
dinkleberg-0.1.0.dist-info/RECORD,,
|