python-injection 0.12.1.post0__tar.gz → 0.12.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
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