python-injection 0.12.1.post0__tar.gz → 0.12.3__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 (25) hide show
  1. {python_injection-0.12.1.post0 → python_injection-0.12.3}/PKG-INFO +1 -1
  2. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/asynchronous.py +12 -5
  3. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/module.py +2 -2
  4. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/integrations/fastapi.py +2 -4
  5. python_injection-0.12.3/injection/py.typed +0 -0
  6. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/utils.py +34 -2
  7. {python_injection-0.12.1.post0 → python_injection-0.12.3}/pyproject.toml +1 -1
  8. python_injection-0.12.1.post0/injection/integrations/__init__.py +0 -11
  9. {python_injection-0.12.1.post0 → python_injection-0.12.3}/README.md +0 -0
  10. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/__init__.py +0 -0
  11. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/__init__.pyi +0 -0
  12. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/__init__.py +0 -0
  13. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/__init__.py +0 -0
  14. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/event.py +0 -0
  15. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/invertible.py +0 -0
  16. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/lazy.py +0 -0
  17. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/threading.py +0 -0
  18. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/common/type.py +0 -0
  19. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/descriptors.py +0 -0
  20. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/hook.py +0 -0
  21. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/_core/injectables.py +0 -0
  22. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/exceptions.py +0 -0
  23. /python_injection-0.12.1.post0/injection/py.typed → /python_injection-0.12.3/injection/integrations/__init__.py +0 -0
  24. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/testing/__init__.py +0 -0
  25. {python_injection-0.12.1.post0 → python_injection-0.12.3}/injection/testing/__init__.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-injection
3
- Version: 0.12.1.post0
3
+ Version: 0.12.3
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -1,7 +1,8 @@
1
+ import asyncio
1
2
  from abc import abstractmethod
2
3
  from collections.abc import Awaitable, Callable, Generator
3
4
  from dataclasses import dataclass
4
- from typing import Any, NoReturn, Protocol, runtime_checkable
5
+ from typing import Any, Protocol, runtime_checkable
5
6
 
6
7
 
7
8
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -32,10 +33,16 @@ class AsyncCaller[**P, T](Caller[P, T]):
32
33
  async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
33
34
  return await self.callable(*args, **kwargs)
34
35
 
35
- def call(self, /, *args: P.args, **kwargs: P.kwargs) -> NoReturn:
36
- raise RuntimeError(
37
- "Synchronous call isn't supported for an asynchronous Callable."
38
- )
36
+ def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
37
+ loop = asyncio.get_event_loop()
38
+
39
+ if loop.is_running():
40
+ raise RuntimeError(
41
+ "Can't call an asynchronous function in a synchronous context."
42
+ )
43
+
44
+ coroutine = self.callable(*args, **kwargs)
45
+ return loop.run_until_complete(coroutine)
39
46
 
40
47
 
41
48
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -78,8 +78,8 @@ class LocatorDependenciesUpdated[T](LocatorEvent):
78
78
  length = len(self.classes)
79
79
  formatted_types = ", ".join(f"`{cls}`" for cls in self.classes)
80
80
  return (
81
- f"{length} dependenc{"ies" if length > 1 else "y"} have been "
82
- f"updated{f": {formatted_types}" if formatted_types else ""}."
81
+ f"{length} dependenc{'ies' if length > 1 else 'y'} have been "
82
+ f"updated{f': {formatted_types}' if formatted_types else ''}."
83
83
  )
84
84
 
85
85
 
@@ -2,15 +2,13 @@ from collections.abc import Awaitable
2
2
  from types import GenericAlias
3
3
  from typing import Any, TypeAliasType
4
4
 
5
+ from fastapi import Depends
6
+
5
7
  from injection import Module, mod
6
8
  from injection.exceptions import InjectionError
7
- from injection.integrations import _is_installed
8
9
 
9
10
  __all__ = ("Inject",)
10
11
 
11
- if _is_installed("fastapi", __name__):
12
- from fastapi import Depends
13
-
14
12
 
15
13
  def Inject[T]( # noqa: N802
16
14
  cls: type[T] | TypeAliasType | GenericAlias,
File without changes
@@ -1,13 +1,15 @@
1
- from collections.abc import Callable, Iterator
1
+ from collections.abc import Callable, Iterable, Iterator
2
2
  from contextlib import contextmanager
3
3
  from importlib import import_module
4
+ from importlib.util import find_spec
4
5
  from pkgutil import walk_packages
5
6
  from types import ModuleType as PythonModule
6
7
  from typing import ContextManager
7
8
 
9
+ from injection import __name__ as injection_package_name
8
10
  from injection import mod
9
11
 
10
- __all__ = ("load_packages", "load_profile")
12
+ __all__ = ("load_modules_with_keywords", "load_packages", "load_profile")
11
13
 
12
14
 
13
15
  def load_profile(*names: str) -> ContextManager[None]:
@@ -33,6 +35,36 @@ def load_profile(*names: str) -> ContextManager[None]:
33
35
  return cleaner()
34
36
 
35
37
 
38
+ def load_modules_with_keywords(
39
+ *packages: PythonModule | str,
40
+ keywords: Iterable[str] | None = None,
41
+ ) -> dict[str, PythonModule]:
42
+ """
43
+ Function to import modules from a Python package if one of the keywords is contained in the Python script.
44
+ The default keywords are:
45
+ - `from injection`
46
+ - `import injection`
47
+ """
48
+
49
+ if keywords is None:
50
+ keywords = f"from {injection_package_name}", f"import {injection_package_name}"
51
+
52
+ b_keywords = tuple(keyword.encode() for keyword in keywords)
53
+
54
+ def predicate(module_name: str) -> bool:
55
+ if (spec := find_spec(module_name)) and (module_path := spec.origin):
56
+ with open(module_path, "rb") as script:
57
+ for line in script:
58
+ line = b" ".join(line.split(b" ")).strip()
59
+
60
+ if line and any(keyword in line for keyword in b_keywords):
61
+ return True
62
+
63
+ return False
64
+
65
+ return load_packages(*packages, predicate=predicate)
66
+
67
+
36
68
  def load_packages(
37
69
  *packages: PythonModule | str,
38
70
  predicate: Callable[[str], bool] = lambda module_name: True,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-injection"
3
- version = "0.12.1.post0"
3
+ version = "0.12.3"
4
4
  description = "Fast and easy dependency injection framework."
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -1,11 +0,0 @@
1
- from importlib.util import find_spec
2
- from typing import Literal
3
-
4
- __all__ = ("_is_installed",)
5
-
6
-
7
- def _is_installed(package: str, needed_for: object, /) -> Literal[True]:
8
- if find_spec(package) is None:
9
- raise RuntimeError(f"To use `{needed_for}`, {package} must be installed.")
10
-
11
- return True