python-injection 0.9.1__tar.gz → 0.9.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.

Potentially problematic release.


This version of python-injection might be problematic. Click here for more details.

Files changed (21) hide show
  1. {python_injection-0.9.1 → python_injection-0.9.3}/PKG-INFO +1 -1
  2. {python_injection-0.9.1 → python_injection-0.9.3}/injection/__init__.pyi +2 -0
  3. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/invertible.py +1 -1
  4. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/lazy.py +2 -2
  5. {python_injection-0.9.1 → python_injection-0.9.3}/injection/core/module.py +42 -23
  6. {python_injection-0.9.1 → python_injection-0.9.3}/injection/testing/__init__.py +2 -0
  7. {python_injection-0.9.1 → python_injection-0.9.3}/injection/utils.py +12 -3
  8. {python_injection-0.9.1 → python_injection-0.9.3}/pyproject.toml +1 -1
  9. {python_injection-0.9.1 → python_injection-0.9.3}/documentation/basic-usage.md +0 -0
  10. {python_injection-0.9.1 → python_injection-0.9.3}/injection/__init__.py +0 -0
  11. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/__init__.py +0 -0
  12. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/event.py +0 -0
  13. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/tools/__init__.py +0 -0
  14. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/tools/threading.py +0 -0
  15. {python_injection-0.9.1 → python_injection-0.9.3}/injection/common/tools/type.py +0 -0
  16. {python_injection-0.9.1 → python_injection-0.9.3}/injection/core/__init__.py +0 -0
  17. {python_injection-0.9.1 → python_injection-0.9.3}/injection/exceptions.py +0 -0
  18. {python_injection-0.9.1 → python_injection-0.9.3}/injection/integrations/__init__.py +0 -0
  19. {python_injection-0.9.1 → python_injection-0.9.3}/injection/integrations/blacksheep.py +0 -0
  20. {python_injection-0.9.1 → python_injection-0.9.3}/injection/py.typed +0 -0
  21. {python_injection-0.9.1 → python_injection-0.9.3}/injection/testing/__init__.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.9.1
3
+ Version: 0.9.3
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -2,6 +2,7 @@ from abc import abstractmethod
2
2
  from collections.abc import Callable
3
3
  from contextlib import ContextDecorator
4
4
  from enum import StrEnum
5
+ from logging import Logger
5
6
  from types import UnionType
6
7
  from typing import (
7
8
  Any,
@@ -179,6 +180,7 @@ class Module:
179
180
  Function to unlock the module by deleting cached instances of singletons.
180
181
  """
181
182
 
183
+ def add_logger(self, logger: Logger) -> Self: ...
182
184
  @classmethod
183
185
  def from_name(cls, name: str) -> Self:
184
186
  """
@@ -15,7 +15,7 @@ class Invertible[T](Protocol):
15
15
 
16
16
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
17
17
  class SimpleInvertible[T](Invertible[T]):
18
- callable: Callable[[], T]
18
+ callable: Callable[..., T]
19
19
 
20
20
  def __invert__(self) -> T:
21
21
  return self.callable()
@@ -9,7 +9,7 @@ __all__ = ("Lazy", "LazyMapping")
9
9
  class Lazy[T](Invertible[T]):
10
10
  __slots__ = ("__cache", "__is_set")
11
11
 
12
- def __init__(self, factory: Callable[[], T]):
12
+ def __init__(self, factory: Callable[..., T]):
13
13
  self.__setup_cache(factory)
14
14
 
15
15
  def __invert__(self) -> T:
@@ -19,7 +19,7 @@ class Lazy[T](Invertible[T]):
19
19
  def is_set(self) -> bool:
20
20
  return self.__is_set
21
21
 
22
- def __setup_cache(self, factory: Callable[[], T]):
22
+ def __setup_cache(self, factory: Callable[..., T]):
23
23
  def cache_generator() -> Iterator[T]:
24
24
  nonlocal factory
25
25
  cached = factory()
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
- import logging
5
4
  from abc import ABC, abstractmethod
6
5
  from collections import OrderedDict
7
6
  from collections.abc import (
@@ -17,7 +16,8 @@ from dataclasses import dataclass, field
17
16
  from enum import StrEnum
18
17
  from functools import partialmethod, singledispatchmethod, update_wrapper
19
18
  from inspect import Signature, isclass
20
- from queue import Queue
19
+ from logging import Logger, getLogger
20
+ from queue import Empty, Queue
21
21
  from types import MethodType, UnionType
22
22
  from typing import (
23
23
  Any,
@@ -59,8 +59,6 @@ __all__ = (
59
59
  "PriorityStr",
60
60
  )
61
61
 
62
- _logger = logging.getLogger(__name__)
63
-
64
62
  """
65
63
  Events
66
64
  """
@@ -161,7 +159,7 @@ class Injectable[T](Protocol):
161
159
 
162
160
  @dataclass(repr=False, frozen=True, slots=True)
163
161
  class BaseInjectable[T](Injectable[T], ABC):
164
- factory: Callable[[], T]
162
+ factory: Callable[..., T]
165
163
 
166
164
 
167
165
  class NewInjectable[T](BaseInjectable[T]):
@@ -386,6 +384,11 @@ class Module(EventListener, Broker):
386
384
  init=False,
387
385
  repr=False,
388
386
  )
387
+ __loggers: list[Logger] = field(
388
+ default_factory=lambda: [getLogger(__name__)],
389
+ init=False,
390
+ repr=False,
391
+ )
389
392
 
390
393
  __instances: ClassVar[dict[str, Module]] = {}
391
394
 
@@ -476,7 +479,7 @@ class Module(EventListener, Broker):
476
479
 
477
480
  function = InjectedFunction(wp)
478
481
 
479
- @function.on_setup(block=False)
482
+ @function.on_setup
480
483
  def listen():
481
484
  function.update(self)
482
485
  self.add_listener(function)
@@ -585,6 +588,10 @@ class Module(EventListener, Broker):
585
588
 
586
589
  return self
587
590
 
591
+ def add_logger(self, logger: Logger) -> Self:
592
+ self.__loggers.append(logger)
593
+ return self
594
+
588
595
  def add_listener(self, listener: EventListener) -> Self:
589
596
  self.__channel.add_listener(listener)
590
597
  return self
@@ -603,7 +610,7 @@ class Module(EventListener, Broker):
603
610
 
604
611
  with self.__channel.dispatch(event):
605
612
  yield
606
- _logger.debug(event)
613
+ self.__send_debug(event)
607
614
 
608
615
  def __check_locking(self):
609
616
  if self.is_locked:
@@ -619,13 +626,19 @@ class Module(EventListener, Broker):
619
626
  f"`{module}` can't be found in the modules used by `{self}`."
620
627
  ) from exc
621
628
 
629
+ def __send_debug(self, message: object):
630
+ for logger in self.__loggers:
631
+ logger.debug(message)
632
+
622
633
  @classmethod
623
634
  def from_name(cls, name: str) -> Self:
624
635
  with suppress(KeyError):
625
636
  return cls.__instances[name]
626
637
 
627
- instance = cls(name)
628
- cls.__instances[name] = instance
638
+ with synchronized():
639
+ instance = cls(name)
640
+ cls.__instances[name] = instance
641
+
629
642
  return instance
630
643
 
631
644
  @classmethod
@@ -723,10 +736,8 @@ class InjectedFunction(EventListener):
723
736
  update_wrapper(self, wrapped, updated=())
724
737
  self.__dependencies = Dependencies.empty()
725
738
  self.__owner = None
726
-
727
- queue = Queue[Callable[[], Any]]()
728
- queue.put_nowait(self.__set_signature)
729
- self.__setup_queue = queue
739
+ self.__setup_queue = Queue[Callable[..., Any]](maxsize=2)
740
+ self.on_setup(self.__set_signature)
730
741
 
731
742
  def __repr__(self) -> str: # pragma: no cover
732
743
  return repr(self.wrapped)
@@ -735,13 +746,7 @@ class InjectedFunction(EventListener):
735
746
  return str(self.wrapped)
736
747
 
737
748
  def __call__(self, /, *args, **kwargs) -> Any:
738
- queue = self.__setup_queue
739
-
740
- while not queue.empty():
741
- setup = queue.get()
742
- setup()
743
- queue.task_done()
744
-
749
+ self.__setup()
745
750
  arguments = self.bind(args, kwargs)
746
751
  return self.wrapped(*arguments.args, **arguments.kwargs)
747
752
 
@@ -796,15 +801,15 @@ class InjectedFunction(EventListener):
796
801
  self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
797
802
  return self
798
803
 
799
- def on_setup(self, wrapped: Callable[[], Any] = None, /, *, block: bool = True):
804
+ def on_setup(self, wrapped: Callable[..., Any] = None, /):
800
805
  def decorator(wp):
801
- self.__setup_queue.put(wp, block=block)
806
+ self.__setup_queue.put_nowait(wp)
802
807
  return wp
803
808
 
804
809
  return decorator(wrapped) if wrapped else decorator
805
810
 
806
811
  @singledispatchmethod
807
- def on_event(self, event: Event, /) -> None: # type: ignore
812
+ def on_event(self, event: Event, /) -> ContextManager | None: # type: ignore
808
813
  return None
809
814
 
810
815
  @on_event.register
@@ -813,6 +818,20 @@ class InjectedFunction(EventListener):
813
818
  yield
814
819
  self.update(event.on_module)
815
820
 
821
+ def __setup(self):
822
+ queue = self.__setup_queue
823
+
824
+ while True:
825
+ try:
826
+ task = queue.get_nowait()
827
+ except Empty:
828
+ break
829
+
830
+ task()
831
+ queue.task_done()
832
+
833
+ queue.join()
834
+
816
835
  def __set_signature(self) -> Self:
817
836
  self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
818
837
  return self
@@ -33,6 +33,8 @@ def use_test_injectables(*, on: Module = None, test_module: Module = None):
33
33
  for module in (on, test_module):
34
34
  module.unlock()
35
35
 
36
+ del module
37
+
36
38
  with on.use_temporarily(test_module, priority=ModulePriority.HIGH):
37
39
  yield
38
40
  on.unlock()
@@ -1,3 +1,4 @@
1
+ from collections.abc import Callable
1
2
  from importlib import import_module
2
3
  from pkgutil import walk_packages
3
4
  from types import ModuleType
@@ -5,9 +6,10 @@ from types import ModuleType
5
6
  __all__ = ("load_package",)
6
7
 
7
8
 
8
- def load_package(package: ModuleType | str):
9
+ def load_package(package: ModuleType | str, predicate: Callable[[str], bool] = None):
9
10
  """
10
11
  Function for importing all modules in a Python package.
12
+ Pass the `predicate` parameter if you want to filter the modules to be imported.
11
13
  """
12
14
 
13
15
  if isinstance(package, str):
@@ -20,8 +22,15 @@ def load_package(package: ModuleType | str):
20
22
  "Package has no `__path__` attribute, as it's probably a module."
21
23
  ) from exc
22
24
 
25
+ if predicate is None:
26
+
27
+ def predicate(_: str) -> bool:
28
+ return True
29
+
23
30
  for info in walk_packages(path=path, prefix=f"{package.__name__}."):
24
- if info.ispkg:
31
+ name = info.name
32
+
33
+ if info.ispkg or not predicate(name):
25
34
  continue
26
35
 
27
- import_module(info.name)
36
+ import_module(name)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-injection"
3
- version = "0.9.1"
3
+ version = "0.9.3"
4
4
  description = "Fast and easy dependency injection framework."
5
5
  authors = ["remimd"]
6
6
  keywords = ["dependencies", "inject", "injection"]