dinkleberg 0.1.0__py3-none-any.whl → 0.2.1__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 +40 -7
- dinkleberg/typing.py +6 -1
- {dinkleberg-0.1.0.dist-info → dinkleberg-0.2.1.dist-info}/METADATA +34 -34
- dinkleberg-0.2.1.dist-info/RECORD +10 -0
- {dinkleberg-0.1.0.dist-info → dinkleberg-0.2.1.dist-info}/WHEEL +2 -1
- dinkleberg-0.2.1.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_type
|
|
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,15 @@ class DependencyConfigurator(DependencyScope):
|
|
|
126
128
|
factory = descriptor['generator'] or descriptor['callable']
|
|
127
129
|
deps = await self._resolve_deps(factory)
|
|
128
130
|
else:
|
|
129
|
-
|
|
130
|
-
|
|
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:
|
|
131
135
|
raise ValueError(f'Cannot resolve generic type {t} without explicit registration.')
|
|
132
136
|
|
|
137
|
+
if inspect.isabstract(t) or t is abc.ABC:
|
|
138
|
+
raise ValueError(f'Cannot resolve abstract class {t} without explicit registration.')
|
|
139
|
+
|
|
133
140
|
is_generator = False
|
|
134
141
|
lifetime = 'transient'
|
|
135
142
|
factory = t
|
|
@@ -143,9 +150,21 @@ class DependencyConfigurator(DependencyScope):
|
|
|
143
150
|
raise RuntimeError(f'Generator {t} did not yield any value.')
|
|
144
151
|
|
|
145
152
|
self._active_generators.append(generator)
|
|
153
|
+
elif asyncio.iscoroutinefunction(factory):
|
|
154
|
+
instance = await factory(**deps, **kwargs)
|
|
146
155
|
else:
|
|
147
156
|
instance = factory(**deps, **kwargs)
|
|
148
157
|
|
|
158
|
+
if isinstance(instance, AsyncGenerator):
|
|
159
|
+
try:
|
|
160
|
+
if is_generator:
|
|
161
|
+
raise RuntimeError(f'Generator {t} yielded another generator. Nested generators are not supported.')
|
|
162
|
+
else:
|
|
163
|
+
raise RuntimeError(
|
|
164
|
+
f'Callable {t} returned a generator. This is most likely due to an invalid dependency registration.')
|
|
165
|
+
finally:
|
|
166
|
+
await instance.aclose()
|
|
167
|
+
|
|
149
168
|
self._wrap_instance(instance)
|
|
150
169
|
|
|
151
170
|
if lifetime == 'singleton':
|
|
@@ -164,8 +183,10 @@ class DependencyConfigurator(DependencyScope):
|
|
|
164
183
|
if not param.annotation or param.annotation is inspect.Parameter.empty:
|
|
165
184
|
continue
|
|
166
185
|
|
|
186
|
+
if is_builtin_type(param.annotation):
|
|
187
|
+
continue
|
|
188
|
+
|
|
167
189
|
# TODO handle more complex cases (e.g., Union, Optional, etc.)
|
|
168
|
-
# TODO handle native types (int, str, etc.)
|
|
169
190
|
names.append(param.name)
|
|
170
191
|
tasks.append(self.resolve(param.annotation))
|
|
171
192
|
|
|
@@ -196,7 +217,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
196
217
|
|
|
197
218
|
# TODO handle __slots__
|
|
198
219
|
def _wrap_instance(self, instance):
|
|
199
|
-
if getattr(instance, '
|
|
220
|
+
if getattr(instance, '__dinkleberg__', False):
|
|
200
221
|
return
|
|
201
222
|
|
|
202
223
|
methods = get_public_methods(instance)
|
|
@@ -222,7 +243,7 @@ class DependencyConfigurator(DependencyScope):
|
|
|
222
243
|
raise NotImplementedError('Synchronous methods with Dependency() defaults are not supported.')
|
|
223
244
|
|
|
224
245
|
try:
|
|
225
|
-
setattr(instance, '
|
|
246
|
+
setattr(instance, '__dinkleberg__', True)
|
|
226
247
|
except (AttributeError, TypeError):
|
|
227
248
|
# Some objects (like those with __slots__) might not allow new attributes
|
|
228
249
|
pass
|
|
@@ -284,3 +305,15 @@ class DependencyConfigurator(DependencyScope):
|
|
|
284
305
|
callable: Callable[..., I] = None):
|
|
285
306
|
self._raise_if_closed()
|
|
286
307
|
self._add('scoped', t=t, generator=generator, callable=callable)
|
|
308
|
+
|
|
309
|
+
@overload
|
|
310
|
+
def add_transient[I](self, *, callable: Callable[..., I]):
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
@overload
|
|
314
|
+
def add_transient[T, I](self, *, t: type[T], callable: Callable[..., I]):
|
|
315
|
+
...
|
|
316
|
+
|
|
317
|
+
def add_transient[T, I](self, *, t: type[T] = None, callable: Callable[..., I] = None):
|
|
318
|
+
self._raise_if_closed()
|
|
319
|
+
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_type(t: type) -> bool:
|
|
7
|
+
origin = get_origin(t) or t
|
|
8
|
+
return getattr(origin, '__module__', None) in ('builtins', 'typing', 'types')
|
|
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.1
|
|
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.1
|
|
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=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,,
|
|
@@ -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,,
|