@@ -3186,7 +3186,7 @@ cdef class ThreadLocalSingleton(BaseSingleton):
31863186 return future_result
31873187
31883188 self ._storage.instance = instance
3189-
3189+
31903190 return instance
31913191
31923192 def _async_init_instance (self , future_result , result ):
@@ -3867,6 +3867,133 @@ cdef class Resource(Provider):
38673867 return self ._resource
38683868
38693869
3870+ cdef class ContextLocalResource(Resource):
3871+ _none = object ()
3872+
3873+ def __init__ (self , provides = None , *args , **kwargs ):
3874+ self ._resource_context_var = ContextVar(" _resource_context_var" , default = self ._none)
3875+ self ._shutdowner_context_var = ContextVar(" _shutdowner_context_var" , default = self ._none)
3876+ super ().__init__(provides, * args, ** kwargs)
3877+
3878+ def __deepcopy__ (self , memo ):
3879+ """ Create and return full copy of provider."""
3880+ copied = memo.get(id (self ))
3881+ if copied is not None :
3882+ return copied
3883+
3884+ if self ._resource_context_var.get() != self ._none:
3885+ raise Error(" Can not copy initialized resource" )
3886+ copied = _memorized_duplicate(self , memo)
3887+ copied.set_provides(_copy_if_provider(self .provides, memo))
3888+ copied.set_args(* deepcopy_args(self , self .args, memo))
3889+ copied.set_kwargs(** deepcopy_kwargs(self , self .kwargs, memo))
3890+
3891+ self ._copy_overridings(copied, memo)
3892+
3893+ return copied
3894+
3895+ @property
3896+ def initialized (self ):
3897+ """ Check if resource is initialized."""
3898+ return self ._resource_context_var.get() != self ._none
3899+
3900+
3901+ def shutdown (self ):
3902+ """ Shutdown resource."""
3903+ if self ._resource_context_var.get() == self ._none :
3904+ self ._reset_all_contex_vars()
3905+ if self ._async_mode == ASYNC_MODE_ENABLED:
3906+ return NULL_AWAITABLE
3907+ return
3908+ if self ._shutdowner_context_var.get():
3909+ future = self ._shutdowner_context_var.get()(None , None , None )
3910+ if __is_future_or_coroutine(future):
3911+ self ._reset_all_contex_vars()
3912+ return ensure_future(self ._shutdown_async(future))
3913+
3914+
3915+ self ._reset_all_contex_vars()
3916+ if self ._async_mode == ASYNC_MODE_ENABLED:
3917+ return NULL_AWAITABLE
3918+
3919+ def _reset_all_contex_vars (self ):
3920+ self ._resource_context_var.set(self ._none)
3921+ self ._shutdowner_context_var.set(self ._none)
3922+
3923+
3924+ async def _shutdown_async(self , future) - > None :
3925+ await future
3926+
3927+
3928+ async def _handle_async_cm(self , obj) - > None :
3929+ resource = await obj.__aenter__()
3930+ return resource
3931+
3932+ async def _provide_async(self , future):
3933+ try :
3934+ obj = await future
3935+
3936+ if hasattr (obj, ' __aenter__' ) and hasattr (obj, ' __aexit__' ):
3937+ resource = await obj.__aenter__()
3938+ shutdowner = obj.__aexit__
3939+ elif hasattr (obj, ' __enter__' ) and hasattr (obj, ' __exit__' ):
3940+ resource = obj.__enter__ ()
3941+ shutdowner = obj.__exit__
3942+ else :
3943+ resource = obj
3944+ shutdowner = None
3945+
3946+ return resource, shutdowner
3947+ except :
3948+ raise
3949+
3950+ cpdef object _provide(self , tuple args, dict kwargs):
3951+ if self ._resource_context_var.get() != self ._none:
3952+ return self ._resource_context_var.get()
3953+ obj = __call(
3954+ self ._provides,
3955+ args,
3956+ self ._args,
3957+ self ._args_len,
3958+ kwargs,
3959+ self ._kwargs,
3960+ self ._kwargs_len,
3961+ self ._async_mode,
3962+ )
3963+
3964+ if __is_future_or_coroutine(obj):
3965+ future_result = asyncio.Future()
3966+ future = ensure_future(self ._provide_async(obj))
3967+ future.add_done_callback(functools.partial(self ._async_init_instance, future_result))
3968+ return future_result
3969+ elif hasattr (obj, ' __enter__' ) and hasattr (obj, ' __exit__' ):
3970+ resource = obj.__enter__ ()
3971+ self ._resource_context_var.set(resource)
3972+ self ._shutdowner_context_var.set(obj.__exit__ )
3973+ elif hasattr (obj, ' __aenter__' ) and hasattr (obj, ' __aexit__' ):
3974+ resource = ensure_future(self ._handle_async_cm(obj))
3975+ self ._resource_context_var.set(resource)
3976+ self ._shutdowner_context_var.set(obj.__aexit__)
3977+ return resource
3978+ else :
3979+ self ._resource_context_var.set(obj)
3980+ self ._shutdowner_context_var.set(None )
3981+
3982+ return self ._resource_context_var.get()
3983+
3984+ def _async_init_instance (self , future_result , result ):
3985+ try :
3986+ resource, shutdowner = result.result()
3987+ except Exception as exception:
3988+ self ._resource_context_var.set(self ._none)
3989+ self ._shutdowner_context_var.set(self ._none)
3990+ future_result.set_exception(exception)
3991+ else :
3992+ self ._resource_context_var.set(resource)
3993+ self ._shutdowner_context_var.set(shutdowner)
3994+ future_result.set_result(resource)
3995+
3996+
38703997cdef class Container(Provider):
38713998 """ Container provider provides an instance of declarative container.
38723999
0 commit comments