1111 Iterator ,
1212 Mapping ,
1313)
14- from contextlib import asynccontextmanager , contextmanager , suppress
14+ from contextlib import asynccontextmanager , contextmanager , nullcontext , suppress
1515from dataclasses import dataclass , field
1616from enum import StrEnum
1717from functools import partialmethod , singledispatchmethod , update_wrapper
2525)
2626from inspect import signature as inspect_signature
2727from logging import Logger , getLogger
28+ from threading import Lock
2829from types import MethodType
2930from typing import (
3031 Any ,
@@ -542,13 +543,19 @@ def set_constant[T](
542543 )
543544 return self
544545
545- def inject [** P , T ](self , wrapped : Callable [P , T ] | None = None , / ) -> Any :
546+ def inject [** P , T ](
547+ self ,
548+ wrapped : Callable [P , T ] | None = None ,
549+ / ,
550+ * ,
551+ threadsafe : bool = False ,
552+ ) -> Any :
546553 def decorator (wp : Callable [P , T ]) -> Callable [P , T ]:
547554 if isclass (wp ):
548- wp .__init__ = self .inject (wp .__init__ )
555+ wp .__init__ = self .inject (wp .__init__ , threadsafe = threadsafe )
549556 return wp
550557
551- return self .make_injected_function (wp )
558+ return self .make_injected_function (wp , threadsafe )
552559
553560 return decorator (wrapped ) if wrapped else decorator
554561
@@ -557,17 +564,19 @@ def make_injected_function[**P, T](
557564 self ,
558565 wrapped : Callable [P , T ],
559566 / ,
567+ threadsafe : bool = ...,
560568 ) -> SyncInjectedFunction [P , T ]: ...
561569
562570 @overload
563571 def make_injected_function [** P , T ](
564572 self ,
565573 wrapped : Callable [P , Awaitable [T ]],
566574 / ,
575+ threadsafe : bool = ...,
567576 ) -> AsyncInjectedFunction [P , T ]: ...
568577
569- def make_injected_function (self , wrapped , / ): # type: ignore[no-untyped-def]
570- metadata = InjectMetadata (wrapped )
578+ def make_injected_function (self , wrapped , / , threadsafe = False ): # type: ignore[no-untyped-def]
579+ metadata = InjectMetadata (wrapped , threadsafe )
571580
572581 @metadata .task
573582 def listen () -> None :
@@ -753,6 +762,23 @@ def unlock(self) -> Self:
753762
754763 return self
755764
765+ def load_profile (self , * names : str ) -> ContextManager [None ]:
766+ modules = tuple (self .from_name (name ) for name in names )
767+
768+ for module in modules :
769+ module .unlock ()
770+
771+ self .unlock ().init_modules (* modules )
772+
773+ del module , modules
774+
775+ @contextmanager
776+ def cleaner () -> Iterator [None ]:
777+ yield
778+ self .unlock ().init_modules ()
779+
780+ return cleaner ()
781+
756782 async def all_ready (self ) -> None :
757783 for broker in self .__brokers :
758784 await broker .all_ready ()
@@ -913,20 +939,23 @@ class Arguments(NamedTuple):
913939class InjectMetadata [** P , T ](Caller [P , T ], EventListener ):
914940 __slots__ = (
915941 "__dependencies" ,
942+ "__lock" ,
916943 "__owner" ,
917944 "__signature" ,
918945 "__tasks" ,
919946 "__wrapped" ,
920947 )
921948
922949 __dependencies : Dependencies
950+ __lock : ContextManager [Any ]
923951 __owner : type | None
924952 __signature : Signature
925953 __tasks : deque [Callable [..., Any ]]
926954 __wrapped : Callable [P , T ]
927955
928- def __init__ (self , wrapped : Callable [P , T ], / ) -> None :
956+ def __init__ (self , wrapped : Callable [P , T ], / , threadsafe : bool ) -> None :
929957 self .__dependencies = Dependencies .empty ()
958+ self .__lock = Lock () if threadsafe else nullcontext ()
930959 self .__owner = None
931960 self .__tasks = deque ()
932961 self .__wrapped = wrapped
@@ -961,13 +990,17 @@ def bind(
961990 return self .__bind (args , kwargs , additional_arguments )
962991
963992 async def acall (self , / , * args : P .args , ** kwargs : P .kwargs ) -> T :
964- self .__run_tasks ()
965- arguments = await self .abind (args , kwargs )
993+ with self .__lock :
994+ self .__run_tasks ()
995+ arguments = await self .abind (args , kwargs )
996+
966997 return self .wrapped (* arguments .args , ** arguments .kwargs )
967998
968999 def call (self , / , * args : P .args , ** kwargs : P .kwargs ) -> T :
969- self .__run_tasks ()
970- arguments = self .bind (args , kwargs )
1000+ with self .__lock :
1001+ self .__run_tasks ()
1002+ arguments = self .bind (args , kwargs )
1003+
9711004 return self .wrapped (* arguments .args , ** arguments .kwargs )
9721005
9731006 def set_owner (self , owner : type ) -> Self :
0 commit comments