@@ -15,8 +15,11 @@ import re
15
15
import sys
16
16
import threading
17
17
import warnings
18
+ from asyncio import ensure_future
18
19
from configparser import ConfigParser as IniConfigParser
20
+ from contextlib import asynccontextmanager, contextmanager
19
21
from contextvars import ContextVar
22
+ from inspect import isasyncgenfunction, isgeneratorfunction
20
23
21
24
try :
22
25
from inspect import _is_coroutine_mark as _is_coroutine_marker
@@ -3598,6 +3601,17 @@ cdef class Dict(Provider):
3598
3601
return __provide_keyword_args(kwargs, self ._kwargs, self ._kwargs_len, self ._async_mode)
3599
3602
3600
3603
3604
+ @cython.no_gc
3605
+ cdef class NullAwaitable:
3606
+ def __next__ (self ):
3607
+ raise StopIteration from None
3608
+
3609
+ def __await__ (self ):
3610
+ return self
3611
+
3612
+
3613
+ cdef NullAwaitable NULL_AWAITABLE = NullAwaitable()
3614
+
3601
3615
3602
3616
cdef class Resource(Provider):
3603
3617
""" Resource provider provides a component with initialization and shutdown."""
@@ -3653,6 +3667,12 @@ cdef class Resource(Provider):
3653
3667
def set_provides (self , provides ):
3654
3668
""" Set provider provides."""
3655
3669
provides = _resolve_string_import(provides)
3670
+
3671
+ if isasyncgenfunction(provides):
3672
+ provides = asynccontextmanager(provides)
3673
+ elif isgeneratorfunction(provides):
3674
+ provides = contextmanager(provides)
3675
+
3656
3676
self ._provides = provides
3657
3677
return self
3658
3678
@@ -3753,28 +3773,21 @@ cdef class Resource(Provider):
3753
3773
""" Shutdown resource."""
3754
3774
if not self ._initialized:
3755
3775
if self ._async_mode == ASYNC_MODE_ENABLED:
3756
- result = asyncio.Future()
3757
- result.set_result(None )
3758
- return result
3776
+ return NULL_AWAITABLE
3759
3777
return
3760
3778
3761
3779
if self ._shutdowner:
3762
- try :
3763
- shutdown = self ._shutdowner(self ._resource)
3764
- except StopIteration :
3765
- pass
3766
- else :
3767
- if inspect.isawaitable(shutdown):
3768
- return self ._create_shutdown_future(shutdown)
3780
+ future = self ._shutdowner(None , None , None )
3781
+
3782
+ if __is_future_or_coroutine(future):
3783
+ return ensure_future(self ._shutdown_async(future))
3769
3784
3770
3785
self ._resource = None
3771
3786
self ._initialized = False
3772
3787
self ._shutdowner = None
3773
3788
3774
3789
if self ._async_mode == ASYNC_MODE_ENABLED:
3775
- result = asyncio.Future()
3776
- result.set_result(None )
3777
- return result
3790
+ return NULL_AWAITABLE
3778
3791
3779
3792
@property
3780
3793
def related (self ):
@@ -3784,165 +3797,75 @@ cdef class Resource(Provider):
3784
3797
yield from filter (is_provider, self .kwargs.values())
3785
3798
yield from super ().related
3786
3799
3800
+ async def _shutdown_async(self , future) - > None :
3801
+ try :
3802
+ await future
3803
+ finally :
3804
+ self ._resource = None
3805
+ self ._initialized = False
3806
+ self ._shutdowner = None
3807
+
3808
+ async def _handle_async_cm(self , obj) - > None :
3809
+ try :
3810
+ self ._resource = resource = await obj.__aenter__()
3811
+ self ._shutdowner = obj.__aexit__
3812
+ return resource
3813
+ except :
3814
+ self ._initialized = False
3815
+ raise
3816
+
3817
+ async def _provide_async(self , future) - > None :
3818
+ try :
3819
+ obj = await future
3820
+
3821
+ if hasattr (obj, ' __aenter__' ) and hasattr (obj, ' __aexit__' ):
3822
+ self ._resource = await obj.__aenter__()
3823
+ self ._shutdowner = obj.__aexit__
3824
+ elif hasattr (obj, ' __enter__' ) and hasattr (obj, ' __exit__' ):
3825
+ self ._resource = obj.__enter__ ()
3826
+ self ._shutdowner = obj.__exit__
3827
+ else :
3828
+ self ._resource = obj
3829
+ self ._shutdowner = None
3830
+
3831
+ return self ._resource
3832
+ except :
3833
+ self ._initialized = False
3834
+ raise
3835
+
3787
3836
cpdef object _provide(self , tuple args, dict kwargs):
3788
3837
if self ._initialized:
3789
3838
return self ._resource
3790
3839
3791
- if self ._is_resource_subclass(self ._provides):
3792
- initializer = self ._provides()
3793
- self ._resource = __call(
3794
- initializer.init,
3795
- args,
3796
- self ._args,
3797
- self ._args_len,
3798
- kwargs,
3799
- self ._kwargs,
3800
- self ._kwargs_len,
3801
- self ._async_mode,
3802
- )
3803
- self ._shutdowner = initializer.shutdown
3804
- elif self ._is_async_resource_subclass(self ._provides):
3805
- initializer = self ._provides()
3806
- async_init = __call(
3807
- initializer.init,
3808
- args,
3809
- self ._args,
3810
- self ._args_len,
3811
- kwargs,
3812
- self ._kwargs,
3813
- self ._kwargs_len,
3814
- self ._async_mode,
3815
- )
3816
- self ._initialized = True
3817
- return self ._create_init_future(async_init, initializer.shutdown)
3818
- elif inspect.isgeneratorfunction(self ._provides):
3819
- initializer = __call(
3820
- self ._provides,
3821
- args,
3822
- self ._args,
3823
- self ._args_len,
3824
- kwargs,
3825
- self ._kwargs,
3826
- self ._kwargs_len,
3827
- self ._async_mode,
3828
- )
3829
- self ._resource = next(initializer)
3830
- self ._shutdowner = initializer.send
3831
- elif iscoroutinefunction(self ._provides):
3832
- initializer = __call(
3833
- self ._provides,
3834
- args,
3835
- self ._args,
3836
- self ._args_len,
3837
- kwargs,
3838
- self ._kwargs,
3839
- self ._kwargs_len,
3840
- self ._async_mode,
3841
- )
3840
+ obj = __call(
3841
+ self ._provides,
3842
+ args,
3843
+ self ._args,
3844
+ self ._args_len,
3845
+ kwargs,
3846
+ self ._kwargs,
3847
+ self ._kwargs_len,
3848
+ self ._async_mode,
3849
+ )
3850
+
3851
+ if __is_future_or_coroutine(obj):
3842
3852
self ._initialized = True
3843
- return self ._create_init_future(initializer)
3844
- elif isasyncgenfunction(self ._provides):
3845
- initializer = __call(
3846
- self ._provides,
3847
- args,
3848
- self ._args,
3849
- self ._args_len,
3850
- kwargs,
3851
- self ._kwargs,
3852
- self ._kwargs_len,
3853
- self ._async_mode,
3854
- )
3853
+ self ._resource = resource = ensure_future(self ._provide_async(obj))
3854
+ return resource
3855
+ elif hasattr (obj, ' __enter__' ) and hasattr (obj, ' __exit__' ):
3856
+ self ._resource = obj.__enter__ ()
3857
+ self ._shutdowner = obj.__exit__
3858
+ elif hasattr (obj, ' __aenter__' ) and hasattr (obj, ' __aexit__' ):
3855
3859
self ._initialized = True
3856
- return self ._create_async_gen_init_future(initializer)
3857
- elif callable (self ._provides):
3858
- self ._resource = __call(
3859
- self ._provides,
3860
- args,
3861
- self ._args,
3862
- self ._args_len,
3863
- kwargs,
3864
- self ._kwargs,
3865
- self ._kwargs_len,
3866
- self ._async_mode,
3867
- )
3860
+ self ._resource = resource = ensure_future(self ._handle_async_cm(obj))
3861
+ return resource
3868
3862
else :
3869
- raise Error(" Unknown type of resource initializer" )
3863
+ self ._resource = obj
3864
+ self ._shutdowner = None
3870
3865
3871
3866
self ._initialized = True
3872
3867
return self ._resource
3873
3868
3874
- def _create_init_future (self , future , shutdowner = None ):
3875
- callback = self ._async_init_callback
3876
- if shutdowner:
3877
- callback = functools.partial(callback, shutdowner = shutdowner)
3878
-
3879
- future = asyncio.ensure_future(future)
3880
- future.add_done_callback(callback)
3881
- self ._resource = future
3882
-
3883
- return future
3884
-
3885
- def _create_async_gen_init_future (self , initializer ):
3886
- if inspect.isasyncgen(initializer):
3887
- return self ._create_init_future(initializer.__anext__(), initializer.asend)
3888
-
3889
- future = asyncio.Future()
3890
-
3891
- create_initializer = asyncio.ensure_future(initializer)
3892
- create_initializer.add_done_callback(functools.partial(self ._async_create_gen_callback, future))
3893
- self ._resource = future
3894
-
3895
- return future
3896
-
3897
- def _async_init_callback (self , initializer , shutdowner = None ):
3898
- try :
3899
- resource = initializer.result()
3900
- except Exception :
3901
- self ._initialized = False
3902
- else :
3903
- self ._resource = resource
3904
- self ._shutdowner = shutdowner
3905
-
3906
- def _async_create_gen_callback (self , future , initializer_future ):
3907
- initializer = initializer_future.result()
3908
- init_future = self ._create_init_future(initializer.__anext__(), initializer.asend)
3909
- init_future.add_done_callback(functools.partial(self ._async_trigger_result, future))
3910
-
3911
- def _async_trigger_result (self , future , future_result ):
3912
- future.set_result(future_result.result())
3913
-
3914
- def _create_shutdown_future (self , shutdown_future ):
3915
- future = asyncio.Future()
3916
- shutdown_future = asyncio.ensure_future(shutdown_future)
3917
- shutdown_future.add_done_callback(functools.partial(self ._async_shutdown_callback, future))
3918
- return future
3919
-
3920
- def _async_shutdown_callback (self , future_result , shutdowner ):
3921
- try :
3922
- shutdowner.result()
3923
- except StopAsyncIteration:
3924
- pass
3925
-
3926
- self ._resource = None
3927
- self ._initialized = False
3928
- self ._shutdowner = None
3929
-
3930
- future_result.set_result(None )
3931
-
3932
- @staticmethod
3933
- def _is_resource_subclass (instance ):
3934
- if not isinstance (instance, type ):
3935
- return
3936
- from . import resources
3937
- return issubclass (instance, resources.Resource)
3938
-
3939
- @staticmethod
3940
- def _is_async_resource_subclass (instance ):
3941
- if not isinstance (instance, type ):
3942
- return
3943
- from . import resources
3944
- return issubclass (instance, resources.AsyncResource)
3945
-
3946
3869
3947
3870
cdef class Container(Provider):
3948
3871
""" Container provider provides an instance of declarative container.
@@ -4993,14 +4916,6 @@ def iscoroutinefunction(obj):
4993
4916
return False
4994
4917
4995
4918
4996
- def isasyncgenfunction (obj ):
4997
- """ Check if object is an asynchronous generator function."""
4998
- try :
4999
- return inspect.isasyncgenfunction(obj)
5000
- except AttributeError :
5001
- return False
5002
-
5003
-
5004
4919
def _resolve_string_import (provides ):
5005
4920
if provides is None :
5006
4921
return provides
0 commit comments