anydi 0.47.0rc0__py3-none-any.whl → 0.48.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.
anydi/_container.py CHANGED
@@ -28,7 +28,6 @@ from ._typing import (
28
28
  get_typed_annotation,
29
29
  get_typed_parameters,
30
30
  is_async_context_manager,
31
- is_builtin_type,
32
31
  is_context_manager,
33
32
  is_event_type,
34
33
  is_inject_marker,
@@ -49,11 +48,9 @@ class Container:
49
48
  *,
50
49
  providers: Iterable[ProviderDef] | None = None,
51
50
  modules: Iterable[ModuleDef] | None = None,
52
- default_scope: Scope = "transient",
53
51
  logger: logging.Logger | None = None,
54
52
  ) -> None:
55
53
  self._providers: dict[Any, Provider] = {}
56
- self._default_scope: Scope = default_scope
57
54
  self._logger = logger or logging.getLogger(__name__)
58
55
  self._resources: dict[str, list[Any]] = defaultdict(list)
59
56
  self._singleton_context = InstanceContext()
@@ -85,11 +82,6 @@ class Container:
85
82
  # Properties
86
83
  ############################
87
84
 
88
- @property
89
- def default_scope(self) -> Scope:
90
- """Get the default scope."""
91
- return self._default_scope
92
-
93
85
  @property
94
86
  def providers(self) -> dict[type[Any], Provider]:
95
87
  """Get the registered providers."""
@@ -259,7 +251,7 @@ class Container:
259
251
  def _register_provider( # noqa: C901
260
252
  self,
261
253
  call: Callable[..., Any],
262
- scope: Scope | None,
254
+ scope: Scope,
263
255
  interface: Any = NOT_SET,
264
256
  override: bool = False,
265
257
  /,
@@ -268,11 +260,9 @@ class Container:
268
260
  """Register a provider with the specified scope."""
269
261
  name = type_repr(call)
270
262
  kind = ProviderKind.from_call(call)
271
- detected_scope = scope
272
263
 
273
264
  # Validate scope if it provided
274
- if scope:
275
- self._validate_provider_scope(scope, name, kind)
265
+ self._validate_provider_scope(scope, name, kind)
276
266
 
277
267
  # Get the signature
278
268
  globalns = getattr(call, "__globals__", {})
@@ -335,9 +325,7 @@ class Container:
335
325
  )
336
326
 
337
327
  try:
338
- sub_provider = self._get_or_register_provider(
339
- parameter.annotation, scope
340
- )
328
+ sub_provider = self._get_or_register_provider(parameter.annotation)
341
329
  except LookupError:
342
330
  if self._parameter_has_default(parameter, **defaults):
343
331
  continue
@@ -350,24 +338,9 @@ class Container:
350
338
 
351
339
  parameters.append(parameter)
352
340
 
353
- # Set detected scope
354
- if detected_scope is None:
355
- if "transient" in scopes:
356
- detected_scope = "transient"
357
- elif "request" in scopes:
358
- detected_scope = "request"
359
- elif "singleton" in scopes:
360
- detected_scope = "singleton"
361
- else:
362
- detected_scope = self.default_scope
363
-
364
- # Validate the provider scope after detection
365
- if scope is None:
366
- self._validate_provider_scope(detected_scope, name, kind)
367
-
368
341
  # Check for unresolved parameters
369
342
  if unresolved_parameter:
370
- if detected_scope not in ("singleton", "transient"):
343
+ if scope not in ("singleton", "transient"):
371
344
  self._unresolved_interfaces.add(interface)
372
345
  else:
373
346
  raise LookupError(
@@ -380,16 +353,16 @@ class Container:
380
353
 
381
354
  # Check scope compatibility
382
355
  for sub_provider in scopes.values():
383
- if sub_provider.scope not in ALLOWED_SCOPES.get(detected_scope, []):
356
+ if sub_provider.scope not in ALLOWED_SCOPES.get(scope, []):
384
357
  raise ValueError(
385
- f"The provider `{name}` with a `{detected_scope}` scope cannot "
358
+ f"The provider `{name}` with a `{scope}` scope cannot "
386
359
  f"depend on `{sub_provider}` with a `{sub_provider.scope}` scope. "
387
360
  "Please ensure all providers are registered with matching scopes."
388
361
  )
389
362
 
390
363
  provider = Provider(
391
364
  call=call,
392
- scope=detected_scope,
365
+ scope=scope,
393
366
  interface=interface,
394
367
  name=name,
395
368
  kind=kind,
@@ -425,20 +398,18 @@ class Container:
425
398
  "properly registered before attempting to use it."
426
399
  ) from exc
427
400
 
428
- def _get_or_register_provider(
429
- self, interface: Any, parent_scope: Scope | None, /, **defaults: Any
430
- ) -> Provider:
401
+ def _get_or_register_provider(self, interface: Any, /, **defaults: Any) -> Provider:
431
402
  """Get or register a provider by interface."""
432
403
  try:
433
404
  return self._providers[interface]
434
405
  except KeyError:
435
- if inspect.isclass(interface) and not is_builtin_type(interface):
436
- # Try to get defined scope
437
- if is_provided(interface):
438
- scope = interface.__provided__["scope"]
439
- else:
440
- scope = parent_scope
441
- return self._register_provider(interface, scope, interface, **defaults)
406
+ if inspect.isclass(interface) and is_provided(interface):
407
+ return self._register_provider(
408
+ interface,
409
+ interface.__provided__["scope"],
410
+ NOT_SET,
411
+ **defaults,
412
+ )
442
413
  raise LookupError(
443
414
  f"The provider interface `{type_repr(interface)}` is either not "
444
415
  "registered, not provided, or not set in the scoped context. "
@@ -533,7 +504,7 @@ class Container:
533
504
  self, interface: Any, create: bool, /, **defaults: Any
534
505
  ) -> Any:
535
506
  """Internal method to handle instance resolution and creation."""
536
- provider = self._get_or_register_provider(interface, None, **defaults)
507
+ provider = self._get_or_register_provider(interface, **defaults)
537
508
  if provider.scope == "transient":
538
509
  return self._create_instance(provider, None, **defaults)
539
510
  context = self._get_instance_context(provider.scope)
@@ -548,7 +519,7 @@ class Container:
548
519
  self, interface: Any, create: bool, /, **defaults: Any
549
520
  ) -> Any:
550
521
  """Internal method to handle instance resolution and creation asynchronously."""
551
- provider = self._get_or_register_provider(interface, None, **defaults)
522
+ provider = self._get_or_register_provider(interface, **defaults)
552
523
  if provider.scope == "transient":
553
524
  return await self._acreate_instance(provider, None, **defaults)
554
525
  context = self._get_instance_context(provider.scope)
@@ -884,3 +855,16 @@ class Container:
884
855
  self, /, packages: PackageOrIterable, *, tags: Iterable[str] | None = None
885
856
  ) -> None:
886
857
  self._scanner.scan(packages=packages, tags=tags)
858
+
859
+ ############################
860
+ # Testing
861
+ ############################
862
+
863
+ @contextlib.contextmanager
864
+ def override(self, interface: Any, instance: Any) -> Iterator[None]:
865
+ raise RuntimeError(
866
+ "Dependency overriding is not supported in this container.\n"
867
+ "Wrap your container with `anydi.testing.Container` instead.\n"
868
+ "Example:\n\n"
869
+ " container = TestContainer.from_container(container)"
870
+ )
anydi/_typing.py CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import builtins
6
5
  import inspect
7
6
  import re
8
7
  import sys
@@ -44,11 +43,6 @@ def type_repr(obj: Any) -> str:
44
43
  )
45
44
 
46
45
 
47
- def is_builtin_type(tp: type[Any]) -> bool:
48
- """Check if the given type is a built-in type."""
49
- return tp.__module__ == builtins.__name__
50
-
51
-
52
46
  def is_context_manager(obj: Any) -> bool:
53
47
  """Check if the given object is a context manager."""
54
48
  return hasattr(obj, "__enter__") and hasattr(obj, "__exit__")
anydi/ext/django/apps.py CHANGED
@@ -9,6 +9,7 @@ from django.core.exceptions import ImproperlyConfigured
9
9
  from django.utils.module_loading import import_string
10
10
 
11
11
  import anydi
12
+ from anydi.testing import TestContainer
12
13
 
13
14
  from ._settings import get_settings
14
15
  from ._utils import inject_urlpatterns, register_components, register_settings
@@ -32,9 +33,16 @@ class ContainerConfig(AppConfig):
32
33
  raise ImproperlyConfigured(
33
34
  f"Cannot import container factory '{container_factory_path}'."
34
35
  ) from exc
35
- self.container = container_factory()
36
+ container = container_factory()
36
37
  else:
37
- self.container = anydi.Container()
38
+ container = anydi.Container()
39
+
40
+ # Use test container
41
+ testing = getattr(settings, "ANYDI_TESTING", False)
42
+ if testing:
43
+ container = TestContainer.from_container(container)
44
+
45
+ self.container = container
38
46
 
39
47
  def ready(self) -> None: # noqa: C901
40
48
  # Register Django settings
anydi/testing.py CHANGED
@@ -28,12 +28,7 @@ class TestContainer(Container):
28
28
  default_scope: Scope = "transient",
29
29
  logger: logging.Logger | None = None,
30
30
  ) -> None:
31
- super().__init__(
32
- providers=providers,
33
- modules=modules,
34
- default_scope=default_scope,
35
- logger=logger,
36
- )
31
+ super().__init__(providers=providers, modules=modules, logger=logger)
37
32
  self._override_instances: dict[Any, Any] = {}
38
33
 
39
34
  @classmethod
@@ -47,7 +42,6 @@ class TestContainer(Container):
47
42
  )
48
43
  for provider in container.providers.values()
49
44
  ],
50
- default_scope=container.default_scope,
51
45
  logger=container.logger,
52
46
  )
53
47
 
@@ -1,36 +1,36 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.47.0rc0
3
+ Version: 0.48.0
4
4
  Summary: Dependency Injection library
5
- Project-URL: Repository, https://github.com/antonrh/anydi
5
+ Keywords: dependency injection,dependencies,di,async,asyncio,application
6
+ Author: Anton Ruhlov
6
7
  Author-email: Anton Ruhlov <antonruhlov@gmail.com>
7
8
  License-Expression: MIT
8
- License-File: LICENSE
9
- Keywords: application,async,asyncio,dependencies,dependency injection,di
9
+ Classifier: Intended Audience :: Information Technology
10
+ Classifier: Intended Audience :: System Administrators
11
+ Classifier: Operating System :: OS Independent
10
12
  Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Topic :: Internet
14
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Classifier: Topic :: Software Development
18
+ Classifier: Typing :: Typed
11
19
  Classifier: Environment :: Web Environment
12
20
  Classifier: Intended Audience :: Developers
13
- Classifier: Intended Audience :: Information Technology
14
- Classifier: Intended Audience :: System Administrators
15
21
  Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Operating System :: OS Independent
17
22
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3 :: Only
19
23
  Classifier: Programming Language :: Python :: 3.9
20
24
  Classifier: Programming Language :: Python :: 3.10
21
25
  Classifier: Programming Language :: Python :: 3.11
22
26
  Classifier: Programming Language :: Python :: 3.12
23
27
  Classifier: Programming Language :: Python :: 3.13
24
- Classifier: Topic :: Internet
25
- Classifier: Topic :: Software Development
26
- Classifier: Topic :: Software Development :: Libraries
27
- Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
28
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
- Classifier: Typing :: Typed
30
- Requires-Python: ~=3.9
28
+ Classifier: Programming Language :: Python :: 3 :: Only
29
+ Requires-Dist: typing-extensions>=4.15.0,<5
31
30
  Requires-Dist: anyio>=3.7.1
32
- Requires-Dist: typing-extensions<5,>=4.14.0
33
- Requires-Dist: wrapt<2,>=1.17.0
31
+ Requires-Dist: wrapt>=1.17.0,<2
32
+ Requires-Python: ~=3.9
33
+ Project-URL: Repository, https://github.com/antonrh/anydi
34
34
  Description-Content-Type: text/markdown
35
35
 
36
36
  # AnyDI
@@ -1,33 +1,32 @@
1
1
  anydi/__init__.py,sha256=KjjYm-1yAFxiPYaMs1WRJMtxE0q_vdX7ZRLQR1fFGs8,567
2
2
  anydi/_async.py,sha256=KhRd3RmZFcwNDzrMm8ctA1gwrg-6m_7laECTYsZdF5k,1445
3
- anydi/_container.py,sha256=Ug0TVOuNQ1cpwUQiHSNtAiZB-qJHv8ECd89WcK6ZwWg,32350
3
+ anydi/_container.py,sha256=LQEmh4iTV9pJNuYhqWHc9h_6sR2E9wA2SOivWG5xzcc,31756
4
4
  anydi/_context.py,sha256=_Xy8cTpRskb4cxTd-Fe-5NnIZyBe1DnovkofhdeUfmw,3020
5
5
  anydi/_decorators.py,sha256=F3yBeGQSz1EsulZaEvYn3cd6FEjJRMoyA6u1QCbEwcs,2813
6
6
  anydi/_module.py,sha256=QPvP27JndZkwl-FYUZWscJm6yfkNzjwoFGURyDhb6Pc,2582
7
7
  anydi/_provider.py,sha256=ig2ecn-STmFGcpkLE5A5OM35XHtU2NsxFVrGp2CvuvM,2123
8
8
  anydi/_scan.py,sha256=nOpspmceVucdwf8nUv1QVFsz2sRVWCVUb0QTH9EbWr4,3653
9
9
  anydi/_scope.py,sha256=PFHjPb2-n0vhRo9mvD_craTFfoJBzR3y-N3_0apL5Q0,258
10
- anydi/_typing.py,sha256=7Nhg4ezPgvFmdGOd2SI_9y5iQv8j-QkNOohXRGNAaIE,4057
11
- anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- anydi/testing.py,sha256=ex9grqKpQmmJWwhIVnzq6aHaUAGLu2-7fwLyYTUuKHE,5678
10
+ anydi/_typing.py,sha256=cYsEnv_QpI8B1DIZ8zPZwywEmZjtx0otJjj6vTLLk_E,3895
13
11
  anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- anydi/ext/fastapi.py,sha256=L9VGPHGy23se1sflmJqTE7LNfQuElVdYEogoT1f5-4A,2324
15
- anydi/ext/faststream.py,sha256=Dy81Vf34CP6pEIbZ-41vh_-Dn6Qc-rcf14U5poebjxI,1905
16
- anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
17
- anydi/ext/pytest_plugin.py,sha256=IoP6XKuGLGLd2Xlpfttc3mI4pxCm2WQLE7x_a7asbv4,4732
18
12
  anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
19
13
  anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
20
14
  anydi/ext/django/_settings.py,sha256=NsYbFBeslqw_tcXCn2b2gKnSp7eb64DM4dRc4tn_eiw,808
21
15
  anydi/ext/django/_utils.py,sha256=sYfUgBV9azddWYB4vNaZadDXcv_MiU-wSfVKV-TZDrE,2695
22
- anydi/ext/django/apps.py,sha256=YLL1uU6dSQ3uf3GB0VHzPW_eLuB1j9h6pv8EdAutMqo,2725
16
+ anydi/ext/django/apps.py,sha256=kGqTUZK55OPtaG5lktavSr88_quDm9GNFUE94eQh6zQ,2965
23
17
  anydi/ext/django/middleware.py,sha256=5OUdp0OWRozyW338Sq04BDhacaFlyUTTOduS_7EwCTA,854
24
18
  anydi/ext/django/ninja/__init__.py,sha256=4J0zoHPK9itbTVrjjvLX6Ftrsb2ND8bITqNDIJzEhks,520
25
19
  anydi/ext/django/ninja/_operation.py,sha256=wk5EOjLY3KVIHk9jMCGsFsja9-dQmMOpLpHXciqxQdk,2680
26
20
  anydi/ext/django/ninja/_signature.py,sha256=p7JtyMdFhX4fWQOvAhvZNss6iURNERcdsTsQADTHkMY,2358
21
+ anydi/ext/fastapi.py,sha256=L9VGPHGy23se1sflmJqTE7LNfQuElVdYEogoT1f5-4A,2324
22
+ anydi/ext/faststream.py,sha256=Dy81Vf34CP6pEIbZ-41vh_-Dn6Qc-rcf14U5poebjxI,1905
23
+ anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
24
+ anydi/ext/pytest_plugin.py,sha256=IoP6XKuGLGLd2Xlpfttc3mI4pxCm2WQLE7x_a7asbv4,4732
27
25
  anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
26
  anydi/ext/starlette/middleware.py,sha256=MxnzshAs-CMvjJp0r457k52MzBL8O4KAuClnF6exBdU,803
29
- anydi-0.47.0rc0.dist-info/METADATA,sha256=MB9wf9GrUWqVQ-SlB4Y3jdz8Fx4WYS6XNA9L298u0lc,4960
30
- anydi-0.47.0rc0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- anydi-0.47.0rc0.dist-info/entry_points.txt,sha256=Nklo9f3Oe4AkNsEgC4g43nCJ-23QDngZSVDNRMdaILI,43
32
- anydi-0.47.0rc0.dist-info/licenses/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
33
- anydi-0.47.0rc0.dist-info/RECORD,,
27
+ anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ anydi/testing.py,sha256=waz0nO-MnfPGQLnPuDT0bdEdfqfmgkbsuFuxWfct6kw,5539
29
+ anydi-0.48.0.dist-info/WHEEL,sha256=Pi5uDq5Fdo_Rr-HD5h9BiPn9Et29Y9Sh8NhcJNnFU1c,79
30
+ anydi-0.48.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
31
+ anydi-0.48.0.dist-info/METADATA,sha256=tT8829LveFWBpSMJMkqdxmBkEQBKAFXzNZ4ZNTac7zI,4956
32
+ anydi-0.48.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.8.17
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -1,2 +1,3 @@
1
1
  [pytest11]
2
2
  anydi = anydi.ext.pytest_plugin
3
+
@@ -1,4 +0,0 @@
1
- Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
3
- Root-Is-Purelib: true
4
- Tag: py3-none-any
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Anton Ruhlov
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.