anydi 0.39.0__tar.gz → 0.39.1__tar.gz

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.
Files changed (87) hide show
  1. {anydi-0.39.0 → anydi-0.39.1}/PKG-INFO +2 -2
  2. {anydi-0.39.0 → anydi-0.39.1}/README.md +1 -1
  3. {anydi-0.39.0 → anydi-0.39.1}/anydi/_container.py +19 -44
  4. {anydi-0.39.0 → anydi-0.39.1}/anydi/_context.py +13 -2
  5. {anydi-0.39.0 → anydi-0.39.1}/anydi/_types.py +4 -5
  6. {anydi-0.39.0 → anydi-0.39.1}/anydi/_utils.py +16 -0
  7. {anydi-0.39.0 → anydi-0.39.1}/docs/usage.md +11 -10
  8. {anydi-0.39.0 → anydi-0.39.1}/pyproject.toml +2 -2
  9. {anydi-0.39.0 → anydi-0.39.1}/tests/fixtures.py +7 -0
  10. {anydi-0.39.0 → anydi-0.39.1}/tests/test_container.py +271 -232
  11. {anydi-0.39.0 → anydi-0.39.1}/uv.lock +1 -1
  12. {anydi-0.39.0 → anydi-0.39.1}/.editorconfig +0 -0
  13. {anydi-0.39.0 → anydi-0.39.1}/.github/workflows/ci.yml +0 -0
  14. {anydi-0.39.0 → anydi-0.39.1}/.gitignore +0 -0
  15. {anydi-0.39.0 → anydi-0.39.1}/.readthedocs.yaml +0 -0
  16. {anydi-0.39.0 → anydi-0.39.1}/LICENSE +0 -0
  17. {anydi-0.39.0 → anydi-0.39.1}/Makefile +0 -0
  18. {anydi-0.39.0 → anydi-0.39.1}/anydi/__init__.py +0 -0
  19. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/__init__.py +0 -0
  20. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/_utils.py +0 -0
  21. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/__init__.py +0 -0
  22. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_container.py +0 -0
  23. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_settings.py +0 -0
  24. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_utils.py +0 -0
  25. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/apps.py +0 -0
  26. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/middleware.py +0 -0
  27. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/__init__.py +0 -0
  28. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/_operation.py +0 -0
  29. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/_signature.py +0 -0
  30. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/fastapi.py +0 -0
  31. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/faststream.py +0 -0
  32. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/pydantic_settings.py +0 -0
  33. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/pytest_plugin.py +0 -0
  34. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/starlette/__init__.py +0 -0
  35. {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/starlette/middleware.py +0 -0
  36. {anydi-0.39.0 → anydi-0.39.1}/anydi/py.typed +0 -0
  37. {anydi-0.39.0 → anydi-0.39.1}/docs/examples/basic.md +0 -0
  38. {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/django.md +0 -0
  39. {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/fastapi.md +0 -0
  40. {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/faststream.md +0 -0
  41. {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/pydantic_settings.md +0 -0
  42. {anydi-0.39.0 → anydi-0.39.1}/docs/index.md +0 -0
  43. {anydi-0.39.0 → anydi-0.39.1}/mkdocs.yml +0 -0
  44. {anydi-0.39.0 → anydi-0.39.1}/tests/__init__.py +0 -0
  45. {anydi-0.39.0 → anydi-0.39.1}/tests/conftest.py +0 -0
  46. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/__init__.py +0 -0
  47. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/__init__.py +0 -0
  48. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/__init__.py +0 -0
  49. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/router.py +0 -0
  50. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/test_router.py +0 -0
  51. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/urls.py +0 -0
  52. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/conftest.py +0 -0
  53. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/container.py +0 -0
  54. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/scan/__init__.py +0 -0
  55. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/services.py +0 -0
  56. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/settings.py +0 -0
  57. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/test_views.py +0 -0
  58. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/urls.py +0 -0
  59. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/views.py +0 -0
  60. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/__init__.py +0 -0
  61. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/app.py +0 -0
  62. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/conftest.py +0 -0
  63. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_auto_register.py +0 -0
  64. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_ext.py +0 -0
  65. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_routes.py +0 -0
  66. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/__init__.py +0 -0
  67. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/test_ext.py +0 -0
  68. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/test_subscribers.py +0 -0
  69. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fixtures.py +0 -0
  70. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/__init__.py +0 -0
  71. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/app.py +0 -0
  72. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/conftest.py +0 -0
  73. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/test_routes.py +0 -0
  74. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/test_pydantic.py +0 -0
  75. {anydi-0.39.0 → anydi-0.39.1}/tests/ext/test_pytest_plugin.py +0 -0
  76. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/__init__.py +0 -0
  77. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/__init__.py +0 -0
  78. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a1/__init__.py +0 -0
  79. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a1/handlers.py +0 -0
  80. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/__init__.py +0 -0
  81. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/a21/__init__.py +0 -0
  82. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/a21/handlers.py +0 -0
  83. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a3/__init__.py +0 -0
  84. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a3/handlers.py +0 -0
  85. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/b/__init__.py +0 -0
  86. {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/b/handlers.py +0 -0
  87. {anydi-0.39.0 → anydi-0.39.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.39.0
3
+ Version: 0.39.1
4
4
  Summary: Dependency Injection library
5
5
  Project-URL: Repository, https://github.com/antonrh/anydi
6
6
  Author-email: Anton Ruhlov <antonruhlov@gmail.com>
@@ -91,7 +91,7 @@ container = Container()
91
91
 
92
92
  @container.provider(scope="singleton")
93
93
  def message() -> str:
94
- return "Hello, world!"
94
+ return "Hello, World!"
95
95
 
96
96
 
97
97
  @container.inject
@@ -56,7 +56,7 @@ container = Container()
56
56
 
57
57
  @container.provider(scope="singleton")
58
58
  def message() -> str:
59
- return "Hello, world!"
59
+ return "Hello, World!"
60
60
 
61
61
 
62
62
  @container.inject
@@ -8,7 +8,6 @@ import importlib
8
8
  import inspect
9
9
  import logging
10
10
  import pkgutil
11
- import threading
12
11
  import types
13
12
  import uuid
14
13
  from collections import defaultdict
@@ -36,7 +35,6 @@ from ._types import (
36
35
  is_marker,
37
36
  )
38
37
  from ._utils import (
39
- AsyncRLock,
40
38
  get_full_qualname,
41
39
  get_typed_annotation,
42
40
  get_typed_parameters,
@@ -44,15 +42,11 @@ from ._utils import (
44
42
  is_async_context_manager,
45
43
  is_builtin_type,
46
44
  is_context_manager,
45
+ is_iterator_type,
46
+ is_none_type,
47
47
  run_async,
48
48
  )
49
49
 
50
- try:
51
- from types import NoneType
52
- except ImportError:
53
- NoneType = type(None) # type: ignore[misc]
54
-
55
-
56
50
  T = TypeVar("T", bound=Any)
57
51
  M = TypeVar("M", bound="Module")
58
52
  P = ParamSpec("P")
@@ -107,8 +101,6 @@ class Container:
107
101
  self._logger = logger or logging.getLogger(__name__)
108
102
  self._resources: dict[str, list[type[Any]]] = defaultdict(list)
109
103
  self._singleton_context = InstanceContext()
110
- self._singleton_lock = threading.RLock()
111
- self._singleton_async_lock = AsyncRLock()
112
104
  self._request_context_var: ContextVar[InstanceContext | None] = ContextVar(
113
105
  "request_context", default=None
114
106
  )
@@ -254,7 +246,7 @@ class Container:
254
246
  )
255
247
  return request_context
256
248
 
257
- def _get_scoped_context(self, scope: Scope) -> InstanceContext:
249
+ def _get_instance_context(self, scope: Scope) -> InstanceContext:
258
250
  """Get the instance context for the specified scope."""
259
251
  if scope == "singleton":
260
252
  return self._singleton_context
@@ -292,7 +284,7 @@ class Container:
292
284
  # Cleanup instance context
293
285
  if provider.scope != "transient":
294
286
  try:
295
- context = self._get_scoped_context(provider.scope)
287
+ context = self._get_instance_context(provider.scope)
296
288
  except LookupError:
297
289
  pass
298
290
  else:
@@ -343,16 +335,16 @@ class Container:
343
335
  interface = signature.return_annotation
344
336
  if interface is inspect.Signature.empty:
345
337
  interface = None
346
- else:
347
- interface = get_typed_annotation(interface, globalns, module)
338
+
339
+ if isinstance(interface, str):
340
+ interface = get_typed_annotation(interface, globalns, module)
348
341
 
349
342
  # If the callable is an iterator, return the actual type
350
- iterator_types = {Iterator, AsyncIterator}
351
- if interface in iterator_types or get_origin(interface) in iterator_types:
343
+ if is_iterator_type(interface) or is_iterator_type(get_origin(interface)):
352
344
  if args := get_args(interface):
353
345
  interface = args[0]
354
346
  # If the callable is a generator, return the resource type
355
- if interface in {None, NoneType}:
347
+ if is_none_type(interface):
356
348
  interface = type(f"Event_{uuid.uuid4().hex}", (Event,), {})
357
349
  else:
358
350
  raise TypeError(
@@ -361,7 +353,7 @@ class Container:
361
353
  )
362
354
 
363
355
  # None interface is not allowed
364
- if interface in {None, NoneType}:
356
+ if is_none_type(interface):
365
357
  raise TypeError(f"Missing `{name}` provider return annotation.")
366
358
 
367
359
  # Check for existing provider
@@ -424,7 +416,7 @@ class Container:
424
416
 
425
417
  # Check for unresolved parameters
426
418
  if unresolved_parameter:
427
- if detected_scope not in {"singleton", "transient"}:
419
+ if detected_scope not in ("singleton", "transient"):
428
420
  self._unresolved_interfaces.add(interface)
429
421
  else:
430
422
  raise LookupError(
@@ -466,10 +458,7 @@ class Container:
466
458
  f"scopes are supported: {', '.join(allowed_scopes)}. "
467
459
  "Please use one of the supported scopes when registering a provider."
468
460
  )
469
- if (
470
- kind in {ProviderKind.GENERATOR, ProviderKind.ASYNC_GENERATOR}
471
- and scope == "transient"
472
- ):
461
+ if scope == "transient" and ProviderKind.is_resource(kind):
473
462
  raise TypeError(
474
463
  f"The resource provider `{name}` is attempting to register "
475
464
  "with a transient scope, which is not allowed."
@@ -565,7 +554,7 @@ class Container:
565
554
  return False
566
555
  if provider.scope == "transient":
567
556
  return False
568
- context = self._get_scoped_context(provider.scope)
557
+ context = self._get_instance_context(provider.scope)
569
558
  return interface in context
570
559
 
571
560
  def release(self, interface: AnyInterface) -> None:
@@ -573,7 +562,7 @@ class Container:
573
562
  provider = self._get_provider(interface)
574
563
  if provider.scope == "transient":
575
564
  return None
576
- context = self._get_scoped_context(provider.scope)
565
+ context = self._get_instance_context(provider.scope)
577
566
  del context[interface]
578
567
 
579
568
  def reset(self) -> None:
@@ -582,7 +571,7 @@ class Container:
582
571
  if provider.scope == "transient":
583
572
  continue
584
573
  try:
585
- context = self._get_scoped_context(provider.scope)
574
+ context = self._get_instance_context(provider.scope)
586
575
  except LookupError:
587
576
  continue
588
577
  del context[interface]
@@ -595,15 +584,8 @@ class Container:
595
584
  if provider.scope == "transient":
596
585
  instance = self._create_instance(provider, None, **defaults)
597
586
  else:
598
- context = self._get_scoped_context(provider.scope)
599
- if provider.scope == "singleton":
600
- with self._singleton_lock:
601
- instance = (
602
- self._get_or_create_instance(provider, context)
603
- if not create
604
- else self._create_instance(provider, context, **defaults)
605
- )
606
- else:
587
+ context = self._get_instance_context(provider.scope)
588
+ with context.lock():
607
589
  instance = (
608
590
  self._get_or_create_instance(provider, context)
609
591
  if not create
@@ -623,15 +605,8 @@ class Container:
623
605
  if provider.scope == "transient":
624
606
  instance = await self._acreate_instance(provider, None, **defaults)
625
607
  else:
626
- context = self._get_scoped_context(provider.scope)
627
- if provider.scope == "singleton":
628
- async with self._singleton_async_lock:
629
- instance = (
630
- await self._aget_or_create_instance(provider, context)
631
- if not create
632
- else await self._acreate_instance(provider, context, **defaults)
633
- )
634
- else:
608
+ context = self._get_instance_context(provider.scope)
609
+ async with context.alock():
635
610
  instance = (
636
611
  await self._aget_or_create_instance(provider, context)
637
612
  if not create
@@ -1,23 +1,26 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ import threading
4
5
  from types import TracebackType
5
6
  from typing import Any
6
7
 
7
8
  from typing_extensions import Self
8
9
 
9
- from ._utils import run_async
10
+ from ._utils import AsyncRLock, run_async
10
11
 
11
12
 
12
13
  class InstanceContext:
13
14
  """A context to store instances."""
14
15
 
15
- __slots__ = ("_instances", "_stack", "_async_stack")
16
+ __slots__ = ("_instances", "_stack", "_async_stack", "_lock", "_async_lock")
16
17
 
17
18
  def __init__(self) -> None:
18
19
  self._instances: dict[type[Any], Any] = {}
19
20
  self._stack = contextlib.ExitStack()
20
21
  self._async_stack = contextlib.AsyncExitStack()
22
+ self._lock = threading.RLock()
23
+ self._async_lock = AsyncRLock()
21
24
 
22
25
  def get(self, interface: type[Any]) -> Any | None:
23
26
  """Get an instance from the context."""
@@ -82,3 +85,11 @@ class InstanceContext:
82
85
  async def aclose(self) -> None:
83
86
  """Close the scoped context asynchronously."""
84
87
  await self.__aexit__(None, None, None)
88
+
89
+ def lock(self) -> threading.RLock:
90
+ """Acquire the context lock."""
91
+ return self._lock
92
+
93
+ def alock(self) -> AsyncRLock:
94
+ """Acquire the context lock asynchronously."""
95
+ return self._async_lock
@@ -82,10 +82,9 @@ class ProviderKind(enum.IntEnum):
82
82
  "object. Only callable providers are allowed."
83
83
  )
84
84
 
85
-
86
- @dataclass(kw_only=True, frozen=True)
87
- class ProviderParameter:
88
- pass
85
+ @classmethod
86
+ def is_resource(cls, kind: ProviderKind) -> bool:
87
+ return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
89
88
 
90
89
 
91
90
  @dataclass(kw_only=True, frozen=True)
@@ -122,7 +121,7 @@ class Provider:
122
121
 
123
122
  @cached_property
124
123
  def is_resource(self) -> bool:
125
- return self.is_generator or self.is_async_generator
124
+ return ProviderKind.is_resource(self.kind)
126
125
 
127
126
 
128
127
  class ProviderArgs(NamedTuple):
@@ -8,12 +8,18 @@ import importlib
8
8
  import inspect
9
9
  import re
10
10
  import sys
11
+ from collections.abc import AsyncIterator, Iterator
11
12
  from types import TracebackType
12
13
  from typing import Any, Callable, ForwardRef, TypeVar
13
14
 
14
15
  import anyio
15
16
  from typing_extensions import ParamSpec, Self, get_args, get_origin
16
17
 
18
+ try:
19
+ from types import NoneType
20
+ except ImportError:
21
+ NoneType = type(None) # type: ignore[misc]
22
+
17
23
  T = TypeVar("T")
18
24
  P = ParamSpec("P")
19
25
 
@@ -57,6 +63,16 @@ def is_async_context_manager(obj: Any) -> bool:
57
63
  return hasattr(obj, "__aenter__") and hasattr(obj, "__aexit__")
58
64
 
59
65
 
66
+ def is_none_type(tp: Any) -> bool:
67
+ """Check if the given object is a None type."""
68
+ return tp in (None, NoneType)
69
+
70
+
71
+ def is_iterator_type(tp: Any) -> bool:
72
+ """Check if the given object is an iterator type."""
73
+ return tp in (Iterator, AsyncIterator)
74
+
75
+
60
76
  def get_typed_annotation(
61
77
  annotation: Any, globalns: dict[str, Any], module: Any = None
62
78
  ) -> Any:
@@ -8,7 +8,7 @@ Once a provider is registered with `Container`, it can be used to resolve depend
8
8
  ### Registering Providers
9
9
 
10
10
  To register a provider, you can use the `register` method of the `Container` instance. The method takes
11
- three arguments: the type of the object to be provided, the provider function or class, and an scope.
11
+ three arguments: the type of the object to be provided, the provider function or class, and a scope.
12
12
 
13
13
  ```python
14
14
  from anydi import Container
@@ -17,12 +17,12 @@ container = Container()
17
17
 
18
18
 
19
19
  def message() -> str:
20
- return "Hello, message!"
20
+ return "Hello, World!"
21
21
 
22
22
 
23
23
  container.register(str, message, scope="singleton")
24
24
 
25
- assert container.resolve(str) == "Hello, world!"
25
+ assert container.resolve(str) == "Hello, World!"
26
26
  ```
27
27
 
28
28
  Alternatively, you can use the `@provider` decorator to register a provider function. The decorator takes care of registering the provider with `Container`.
@@ -35,10 +35,10 @@ container = Container()
35
35
 
36
36
  @container.provider(scope="singleton")
37
37
  def message() -> str:
38
- return "Hello, message!"
38
+ return "Hello, World!"
39
39
 
40
40
 
41
- assert container.resolve(str) == "Hello, world!"
41
+ assert container.resolve(str) == "Hello, World!"
42
42
  ```
43
43
 
44
44
  ### Annotated Providers
@@ -82,7 +82,7 @@ container = Container()
82
82
 
83
83
  @container.provider(scope="singleton")
84
84
  def message() -> str:
85
- return "Hello, message!"
85
+ return "Hello, World!"
86
86
 
87
87
 
88
88
  assert container.is_registered(str)
@@ -105,13 +105,13 @@ container = Container()
105
105
 
106
106
  @container.provider(scope="singleton")
107
107
  def message() -> str:
108
- return "Hello, message!"
108
+ return "Hello, World!"
109
109
 
110
110
 
111
111
  # Check if an instance is resolved
112
112
  assert not container.is_resolved(str)
113
113
 
114
- assert container.resolve(str) == "Hello, world!"
114
+ assert container.resolve(str) == "Hello, World!"
115
115
 
116
116
  assert container.is_resolved(str)
117
117
 
@@ -126,7 +126,7 @@ To release a provider instance, you can use the `release` method of the `Contain
126
126
  from anydi import Container
127
127
 
128
128
  container = Container()
129
- container.register(str, lambda: "Hello, world!", scope="singleton")
129
+ container.register(str, lambda: "Hello, World!", scope="singleton")
130
130
  container.register(int, lambda: 100, scope="singleton")
131
131
 
132
132
  container.resolve(str)
@@ -165,6 +165,7 @@ class Database:
165
165
  class Repository:
166
166
  db: Database
167
167
 
168
+
168
169
  @dataclass
169
170
  class Service:
170
171
  repo: Repository
@@ -445,7 +446,7 @@ async def resource_provider() -> AsyncIterator[Resource]:
445
446
  async def main() -> None:
446
447
  await container.astart() # start resources
447
448
 
448
- assert (await container.resolve(Resource)).name == "demo"
449
+ assert (await container.aresolve(Resource)).name == "demo"
449
450
 
450
451
  await container.aclose() # close resources
451
452
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anydi"
3
- version = "0.39.0"
3
+ version = "0.39.1"
4
4
  description = "Dependency Injection library"
5
5
  authors = [{ name = "Anton Ruhlov", email = "antonruhlov@gmail.com" }]
6
6
  requires-python = "~=3.9"
@@ -136,7 +136,7 @@ omit = [
136
136
  ]
137
137
 
138
138
  [tool.bumpversion]
139
- current_version = "0.39.0"
139
+ current_version = "0.39.1"
140
140
  parse = """(?x)
141
141
  (?P<major>0|[1-9]\\d*)\\.
142
142
  (?P<minor>0|[1-9]\\d*)\\.
@@ -1,4 +1,6 @@
1
+ import uuid
1
2
  from collections.abc import AsyncIterator, Iterator
3
+ from dataclasses import dataclass, field
2
4
  from typing import Annotated
3
5
 
4
6
  from anydi import Container, Module, provider
@@ -36,6 +38,11 @@ def iterator() -> Iterator: # type: ignore[type-arg]
36
38
  yield
37
39
 
38
40
 
41
+ @dataclass(frozen=True)
42
+ class UniqueId:
43
+ id: uuid.UUID = field(default_factory=uuid.uuid4)
44
+
45
+
39
46
  class Service:
40
47
  def __init__(self, ident: str) -> None:
41
48
  self.ident = ident