1
- __all__ = 'coroutine' , ' iscoroutinefunction' , 'iscoroutine'
1
+ __all__ = 'iscoroutinefunction' , 'iscoroutine'
2
2
3
3
import collections .abc
4
- import functools
5
4
import inspect
6
5
import os
7
6
import sys
8
7
import traceback
9
8
import types
10
- import warnings
11
-
12
- from . import base_futures
13
- from . import constants
14
- from . import format_helpers
15
- from .log import logger
16
9
17
10
18
11
def _is_debug_mode ():
19
- # If you set _DEBUG to true, @coroutine will wrap the resulting
20
- # generator objects in a CoroWrapper instance (defined below). That
21
- # instance will log a message when the generator is never iterated
22
- # over, which may happen when you forget to use "await" or "yield from"
23
- # with a coroutine call.
24
- # Note that the value of the _DEBUG flag is taken
25
- # when the decorator is used, so to be of any use it must be set
26
- # before you define your coroutines. A downside of using this feature
27
- # is that tracebacks show entries for the CoroWrapper.__next__ method
28
- # when _DEBUG is true.
12
+ # See: https://docs.python.org/3/library/asyncio-dev.html#asyncio-debug-mode.
29
13
return sys .flags .dev_mode or (not sys .flags .ignore_environment and
30
14
bool (os .environ .get ('PYTHONASYNCIODEBUG' )))
31
15
32
16
33
- _DEBUG = _is_debug_mode ()
34
-
35
-
36
- class CoroWrapper :
37
- # Wrapper for coroutine object in _DEBUG mode.
38
-
39
- def __init__ (self , gen , func = None ):
40
- assert inspect .isgenerator (gen ) or inspect .iscoroutine (gen ), gen
41
- self .gen = gen
42
- self .func = func # Used to unwrap @coroutine decorator
43
- self ._source_traceback = format_helpers .extract_stack (sys ._getframe (1 ))
44
- self .__name__ = getattr (gen , '__name__' , None )
45
- self .__qualname__ = getattr (gen , '__qualname__' , None )
46
-
47
- def __repr__ (self ):
48
- coro_repr = _format_coroutine (self )
49
- if self ._source_traceback :
50
- frame = self ._source_traceback [- 1 ]
51
- coro_repr += f', created at { frame [0 ]} :{ frame [1 ]} '
52
-
53
- return f'<{ self .__class__ .__name__ } { coro_repr } >'
54
-
55
- def __iter__ (self ):
56
- return self
57
-
58
- def __next__ (self ):
59
- return self .gen .send (None )
60
-
61
- def send (self , value ):
62
- return self .gen .send (value )
63
-
64
- def throw (self , type , value = None , traceback = None ):
65
- return self .gen .throw (type , value , traceback )
66
-
67
- def close (self ):
68
- return self .gen .close ()
69
-
70
- @property
71
- def gi_frame (self ):
72
- return self .gen .gi_frame
73
-
74
- @property
75
- def gi_running (self ):
76
- return self .gen .gi_running
77
-
78
- @property
79
- def gi_code (self ):
80
- return self .gen .gi_code
81
-
82
- def __await__ (self ):
83
- return self
84
-
85
- @property
86
- def gi_yieldfrom (self ):
87
- return self .gen .gi_yieldfrom
88
-
89
- def __del__ (self ):
90
- # Be careful accessing self.gen.frame -- self.gen might not exist.
91
- gen = getattr (self , 'gen' , None )
92
- frame = getattr (gen , 'gi_frame' , None )
93
- if frame is not None and frame .f_lasti == - 1 :
94
- msg = f'{ self !r} was never yielded from'
95
- tb = getattr (self , '_source_traceback' , ())
96
- if tb :
97
- tb = '' .join (traceback .format_list (tb ))
98
- msg += (f'\n Coroutine object created at '
99
- f'(most recent call last, truncated to '
100
- f'{ constants .DEBUG_STACK_DEPTH } last lines):\n ' )
101
- msg += tb .rstrip ()
102
- logger .error (msg )
103
-
104
-
105
- def coroutine (func ):
106
- """Decorator to mark coroutines.
107
-
108
- If the coroutine is not yielded from before it is destroyed,
109
- an error message is logged.
110
- """
111
- warnings .warn ('"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead' ,
112
- DeprecationWarning ,
113
- stacklevel = 2 )
114
- if inspect .iscoroutinefunction (func ):
115
- # In Python 3.5 that's all we need to do for coroutines
116
- # defined with "async def".
117
- return func
118
-
119
- if inspect .isgeneratorfunction (func ):
120
- coro = func
121
- else :
122
- @functools .wraps (func )
123
- def coro (* args , ** kw ):
124
- res = func (* args , ** kw )
125
- if (base_futures .isfuture (res ) or inspect .isgenerator (res ) or
126
- isinstance (res , CoroWrapper )):
127
- res = yield from res
128
- else :
129
- # If 'res' is an awaitable, run it.
130
- try :
131
- await_meth = res .__await__
132
- except AttributeError :
133
- pass
134
- else :
135
- if isinstance (res , collections .abc .Awaitable ):
136
- res = yield from await_meth ()
137
- return res
138
-
139
- coro = types .coroutine (coro )
140
- if not _DEBUG :
141
- wrapper = coro
142
- else :
143
- @functools .wraps (func )
144
- def wrapper (* args , ** kwds ):
145
- w = CoroWrapper (coro (* args , ** kwds ), func = func )
146
- if w ._source_traceback :
147
- del w ._source_traceback [- 1 ]
148
- # Python < 3.5 does not implement __qualname__
149
- # on generator objects, so we set it manually.
150
- # We use getattr as some callables (such as
151
- # functools.partial may lack __qualname__).
152
- w .__name__ = getattr (func , '__name__' , None )
153
- w .__qualname__ = getattr (func , '__qualname__' , None )
154
- return w
155
-
156
- wrapper ._is_coroutine = _is_coroutine # For iscoroutinefunction().
157
- return wrapper
158
-
159
-
160
17
# A marker for iscoroutinefunction.
161
18
_is_coroutine = object ()
162
19
@@ -170,7 +27,7 @@ def iscoroutinefunction(func):
170
27
# Prioritize native coroutine check to speed-up
171
28
# asyncio.iscoroutine.
172
29
_COROUTINE_TYPES = (types .CoroutineType , types .GeneratorType ,
173
- collections .abc .Coroutine , CoroWrapper )
30
+ collections .abc .Coroutine )
174
31
_iscoroutine_typecache = set ()
175
32
176
33
@@ -193,16 +50,11 @@ def iscoroutine(obj):
193
50
def _format_coroutine (coro ):
194
51
assert iscoroutine (coro )
195
52
196
- is_corowrapper = isinstance (coro , CoroWrapper )
197
-
198
53
def get_name (coro ):
199
54
# Coroutines compiled with Cython sometimes don't have
200
55
# proper __qualname__ or __name__. While that is a bug
201
56
# in Cython, asyncio shouldn't crash with an AttributeError
202
57
# in its __repr__ functions.
203
- if is_corowrapper :
204
- return format_helpers ._format_callback (coro .func , (), {})
205
-
206
58
if hasattr (coro , '__qualname__' ) and coro .__qualname__ :
207
59
coro_name = coro .__qualname__
208
60
elif hasattr (coro , '__name__' ) and coro .__name__ :
@@ -247,18 +99,8 @@ def is_running(coro):
247
99
filename = coro_code .co_filename or '<empty co_filename>'
248
100
249
101
lineno = 0
250
- if (is_corowrapper and
251
- coro .func is not None and
252
- not inspect .isgeneratorfunction (coro .func )):
253
- source = format_helpers ._get_function_source (coro .func )
254
- if source is not None :
255
- filename , lineno = source
256
- if coro_frame is None :
257
- coro_repr = f'{ coro_name } done, defined at { filename } :{ lineno } '
258
- else :
259
- coro_repr = f'{ coro_name } running, defined at { filename } :{ lineno } '
260
102
261
- elif coro_frame is not None :
103
+ if coro_frame is not None :
262
104
lineno = coro_frame .f_lineno
263
105
coro_repr = f'{ coro_name } running at { filename } :{ lineno } '
264
106
0 commit comments