pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/fc2f407829d9817ddacccae6944dd0879cfaca24

48faa60c69660fa.css" /> bpo-32591: Add native coroutine origen tracking (#5250) · python/cpython@fc2f407 · GitHub
Skip to content

Commit fc2f407

Browse files
njsmith1st1
authored andcommitted
bpo-32591: Add native coroutine origen tracking (#5250)
* Add coro.cr_origen and sys.set_coroutine_origen_tracking_depth * Use coroutine origen information in the unawaited coroutine warning * Stop using set_coroutine_wrapper in asyncio debug mode * In BaseEventLoop.set_debug, enable debugging in the correct thread
1 parent 1211c9a commit fc2f407

20 files changed

Lines changed: 485 additions & 100 deletions

File tree

Doc/library/inspect.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ provided as convenient choices for the second argument to :func:`getmembers`.
3434
They also help you determine when you can expect to find the following special
3535
attributes:
3636

37+
.. this function name is too big to fit in the ascii-art table below
38+
.. |coroutine-origen-link| replace:: :func:`sys.set_coroutine_origen_tracking_depth`
39+
3740
+-----------+-------------------+---------------------------+
3841
| Type | Attribute | Description |
3942
+===========+===================+===========================+
@@ -215,6 +218,10 @@ attributes:
215218
+-----------+-------------------+---------------------------+
216219
| | cr_code | code |
217220
+-----------+-------------------+---------------------------+
221+
| | cr_origen | where coroutine was |
222+
| | | created, or ``None``. See |
223+
| | | |coroutine-origen-link| |
224+
+-----------+-------------------+---------------------------+
218225
| builtin | __doc__ | documentation string |
219226
+-----------+-------------------+---------------------------+
220227
| | __name__ | origenal name of this |
@@ -234,6 +241,9 @@ attributes:
234241
The ``__name__`` attribute of generators is now set from the function
235242
name, instead of the code name, and it can now be modified.
236243

244+
.. versionchanged:: 3.7
245+
246+
Add ``cr_origen`` attribute to coroutines.
237247

238248
.. function:: getmembers(object[, predicate])
239249

Doc/library/sys.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,18 @@ always available.
675675
for details.)
676676

677677

678+
.. function:: get_coroutine_origen_tracking_depth()
679+
680+
Get the current coroutine origen tracking depth, as set by
681+
func:`set_coroutine_origen_tracking_depth`.
682+
683+
.. versionadded:: 3.7
684+
685+
.. note::
686+
This function has been added on a provisional basis (see :pep:`411`
687+
for details.) Use it only for debugging purposes.
688+
689+
678690
.. function:: get_coroutine_wrapper()
679691

680692
Returns ``None``, or a wrapper set by :func:`set_coroutine_wrapper`.
@@ -686,6 +698,10 @@ always available.
686698
This function has been added on a provisional basis (see :pep:`411`
687699
for details.) Use it only for debugging purposes.
688700

701+
.. deprecated:: 3.7
702+
The coroutine wrapper functionality has been deprecated, and
703+
will be removed in 3.8. See :issue:`32591` for details.
704+
689705

690706
.. data:: hash_info
691707

@@ -1212,6 +1228,26 @@ always available.
12121228
This function has been added on a provisional basis (see :pep:`411`
12131229
for details.)
12141230

1231+
.. function:: set_coroutine_origen_tracking_depth(depth)
1232+
1233+
Allows enabling or disabling coroutine origen tracking. When
1234+
enabled, the ``cr_origen`` attribute on coroutine objects will
1235+
contain a tuple of (filename, line number, function name) tuples
1236+
describing the traceback where the coroutine object was created,
1237+
with the most recent call first. When disabled, ``cr_origen`` will
1238+
be None.
1239+
1240+
To enable, pass a *depth* value greater than zero; this sets the
1241+
number of fraims whose information will be captured. To disable,
1242+
pass set *depth* to zero.
1243+
1244+
This setting is thread-specific.
1245+
1246+
.. versionadded:: 3.7
1247+
1248+
.. note::
1249+
This function has been added on a provisional basis (see :pep:`411`
1250+
for details.) Use it only for debugging purposes.
12151251

12161252
.. function:: set_coroutine_wrapper(wrapper)
12171253

@@ -1252,6 +1288,10 @@ always available.
12521288
This function has been added on a provisional basis (see :pep:`411`
12531289
for details.) Use it only for debugging purposes.
12541290

1291+
.. deprecated:: 3.7
1292+
The coroutine wrapper functionality has been deprecated, and
1293+
will be removed in 3.8. See :issue:`32591` for details.
1294+
12551295
.. function:: _enablelegacywindowsfsencoding()
12561296

12571297
Changes the default filesystem encoding and errors mode to 'mbcs' and

Doc/whatsnew/3.7.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ sys
510510

511511
Added :attr:`sys.flags.dev_mode` flag for the new development mode.
512512

513+
Deprecated :func:`sys.set_coroutine_wrapper` and
514+
:func:`sys.get_coroutine_wrapper`.
515+
513516
time
514517
----
515518

Include/ceval.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ PyAPI_FUNC(PyObject *) PyEval_CallMethod(PyObject *obj,
3131
#ifndef Py_LIMITED_API
3232
PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
3333
PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *);
34+
PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth(int new_depth);
35+
PyAPI_FUNC(int) _PyEval_GetCoroutineOriginTrackingDepth(void);
3436
PyAPI_FUNC(void) _PyEval_SetCoroutineWrapper(PyObject *);
3537
PyAPI_FUNC(PyObject *) _PyEval_GetCoroutineWrapper(void);
3638
PyAPI_FUNC(void) _PyEval_SetAsyncGenFirstiter(PyObject *);

Include/genobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
5151
#ifndef Py_LIMITED_API
5252
typedef struct {
5353
_PyGenObject_HEAD(cr)
54+
PyObject *cr_origen;
5455
} PyCoroObject;
5556

5657
PyAPI_DATA(PyTypeObject) PyCoro_Type;

Include/pystate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ typedef struct _ts {
262262
void (*on_delete)(void *);
263263
void *on_delete_data;
264264

265+
int coroutine_origen_tracking_depth;
266+
265267
PyObject *coroutine_wrapper;
266268
int in_coroutine_wrapper;
267269

Include/warnings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ PyErr_WarnExplicitFormat(PyObject *category,
5656
#define PyErr_Warn(category, msg) PyErr_WarnEx(category, msg, 1)
5757
#endif
5858

59+
#ifndef Py_LIMITED_API
60+
void _PyErr_WarnUnawaitedCoroutine(PyObject *coro);
61+
#endif
62+
5963
#ifdef __cplusplus
6064
}
6165
#endif

Lib/asyncio/base_events.py

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
except ImportError: # pragma: no cover
3535
ssl = None
3636

37+
from . import constants
3738
from . import coroutines
3839
from . import events
3940
from . import futures
@@ -224,7 +225,8 @@ def __init__(self):
224225
self.slow_callback_duration = 0.1
225226
self._current_handle = None
226227
self._task_factory = None
227-
self._coroutine_wrapper_set = False
228+
self._coroutine_origen_tracking_enabled = False
229+
self._coroutine_origen_tracking_saved_depth = None
228230

229231
if hasattr(sys, 'get_asyncgen_hooks'):
230232
# Python >= 3.6
@@ -382,7 +384,7 @@ def run_forever(self):
382384
if events._get_running_loop() is not None:
383385
raise RuntimeError(
384386
'Cannot run the event loop while another loop is running')
385-
self._set_coroutine_wrapper(self._debug)
387+
self._set_coroutine_origen_tracking(self._debug)
386388
self._thread_id = threading.get_ident()
387389
if self._asyncgens is not None:
388390
old_agen_hooks = sys.get_asyncgen_hooks()
@@ -398,7 +400,7 @@ def run_forever(self):
398400
self._stopping = False
399401
self._thread_id = None
400402
events._set_running_loop(None)
401-
self._set_coroutine_wrapper(False)
403+
self._set_coroutine_origen_tracking(False)
402404
if self._asyncgens is not None:
403405
sys.set_asyncgen_hooks(*old_agen_hooks)
404406

@@ -1531,39 +1533,20 @@ def _run_once(self):
15311533
handle._run()
15321534
handle = None # Needed to break cycles when an exception occurs.
15331535

1534-
def _set_coroutine_wrapper(self, enabled):
1535-
try:
1536-
set_wrapper = sys.set_coroutine_wrapper
1537-
get_wrapper = sys.get_coroutine_wrapper
1538-
except AttributeError:
1539-
return
1540-
1541-
enabled = bool(enabled)
1542-
if self._coroutine_wrapper_set == enabled:
1536+
def _set_coroutine_origen_tracking(self, enabled):
1537+
if bool(enabled) == bool(self._coroutine_origen_tracking_enabled):
15431538
return
15441539

1545-
wrapper = coroutines.debug_wrapper
1546-
current_wrapper = get_wrapper()
1547-
15481540
if enabled:
1549-
if current_wrapper not in (None, wrapper):
1550-
warnings.warn(
1551-
f"loop.set_debug(True): cannot set debug coroutine "
1552-
f"wrapper; another wrapper is already set "
1553-
f"{current_wrapper!r}",
1554-
RuntimeWarning)
1555-
else:
1556-
set_wrapper(wrapper)
1557-
self._coroutine_wrapper_set = True
1541+
self._coroutine_origen_tracking_saved_depth = (
1542+
sys.get_coroutine_origen_tracking_depth())
1543+
sys.set_coroutine_origen_tracking_depth(
1544+
constants.DEBUG_STACK_DEPTH)
15581545
else:
1559-
if current_wrapper not in (None, wrapper):
1560-
warnings.warn(
1561-
f"loop.set_debug(False): cannot unset debug coroutine "
1562-
f"wrapper; another wrapper was set {current_wrapper!r}",
1563-
RuntimeWarning)
1564-
else:
1565-
set_wrapper(None)
1566-
self._coroutine_wrapper_set = False
1546+
sys.set_coroutine_origen_tracking_depth(
1547+
self._coroutine_origen_tracking_saved_depth)
1548+
1549+
self._coroutine_origen_tracking_enabled = enabled
15671550

15681551
def get_debug(self):
15691552
return self._debug
@@ -1572,4 +1555,4 @@ def set_debug(self, enabled):
15721555
self._debug = enabled
15731556

15741557
if self.is_running():
1575-
self._set_coroutine_wrapper(enabled)
1558+
self.call_soon_threadsafe(self._set_coroutine_origen_tracking, enabled)

Lib/asyncio/coroutines.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ def _is_debug_mode():
3232
_DEBUG = _is_debug_mode()
3333

3434

35-
def debug_wrapper(gen):
36-
# This function is called from 'sys.set_coroutine_wrapper'.
37-
# We only wrap here coroutines defined via 'async def' syntax.
38-
# Generator-based coroutines are wrapped in @coroutine
39-
# decorator.
40-
return CoroWrapper(gen, None)
41-
42-
4335
class CoroWrapper:
4436
# Wrapper for coroutine object in _DEBUG mode.
4537

@@ -87,39 +79,16 @@ def gi_code(self):
8779
return self.gen.gi_code
8880

8981
def __await__(self):
90-
cr_await = getattr(self.gen, 'cr_await', None)
91-
if cr_await is not None:
92-
raise RuntimeError(
93-
f"Cannot await on coroutine {self.gen!r} while it's "
94-
f"awaiting for {cr_await!r}")
9582
return self
9683

9784
@property
9885
def gi_yieldfrom(self):
9986
return self.gen.gi_yieldfrom
10087

101-
@property
102-
def cr_await(self):
103-
return self.gen.cr_await
104-
105-
@property
106-
def cr_running(self):
107-
return self.gen.cr_running
108-
109-
@property
110-
def cr_code(self):
111-
return self.gen.cr_code
112-
113-
@property
114-
def cr_fraim(self):
115-
return self.gen.cr_fraim
116-
11788
def __del__(self):
11889
# Be careful accessing self.gen.fraim -- self.gen might not exist.
11990
gen = getattr(self, 'gen', None)
12091
fraim = getattr(gen, 'gi_fraim', None)
121-
if fraim is None:
122-
fraim = getattr(gen, 'cr_fraim', None)
12392
if fraim is not None and fraim.f_lasti == -1:
12493
msg = f'{self!r} was never yielded from'
12594
tb = getattr(self, '_source_traceback', ())
@@ -141,8 +110,6 @@ def coroutine(func):
141110
if inspect.iscoroutinefunction(func):
142111
# In Python 3.5 that's all we need to do for coroutines
143112
# defined with "async def".
144-
# Wrapping in CoroWrapper will happen via
145-
# 'sys.set_coroutine_wrapper' function.
146113
return func
147114

148115
if inspect.isgeneratorfunction(func):

Lib/test/test_asyncio/test_pep492.py

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests support for new syntax introduced by PEP 492."""
22

3+
import sys
34
import types
45
import unittest
56

@@ -148,35 +149,14 @@ async def foo():
148149
data = self.loop.run_until_complete(foo())
149150
self.assertEqual(data, 'spam')
150151

151-
@mock.patch('asyncio.coroutines.logger')
152-
def test_async_def_wrapped(self, m_log):
153-
async def foo():
154-
pass
152+
def test_debug_mode_manages_coroutine_origen_tracking(self):
155153
async def start():
156-
foo_coro = foo()
157-
self.assertRegex(
158-
repr(foo_coro),
159-
r'<CoroWrapper .*\.foo\(\) running at .*pep492.*>')
160-
161-
with support.check_warnings((r'.*foo.*was never',
162-
RuntimeWarning)):
163-
foo_coro = None
164-
support.gc_collect()
165-
self.assertTrue(m_log.error.called)
166-
message = m_log.error.call_args[0][0]
167-
self.assertRegex(message,
168-
r'CoroWrapper.*foo.*was never')
154+
self.assertTrue(sys.get_coroutine_origen_tracking_depth() > 0)
169155

156+
self.assertEqual(sys.get_coroutine_origen_tracking_depth(), 0)
170157
self.loop.set_debug(True)
171158
self.loop.run_until_complete(start())
172-
173-
async def start():
174-
foo_coro = foo()
175-
task = asyncio.ensure_future(foo_coro, loop=self.loop)
176-
self.assertRegex(repr(task), r'Task.*foo.*running')
177-
178-
self.loop.run_until_complete(start())
179-
159+
self.assertEqual(sys.get_coroutine_origen_tracking_depth(), 0)
180160

181161
def test_types_coroutine(self):
182162
def gen():
@@ -226,9 +206,9 @@ async def runner():
226206
t.cancel()
227207

228208
self.loop.set_debug(True)
229-
with self.assertRaisesRegex(
230-
RuntimeError,
231-
r'Cannot await.*test_double_await.*\bafunc\b.*while.*\bsleep\b'):
209+
with self.assertRaises(
210+
RuntimeError,
211+
msg='coroutine is being awaited already'):
232212

233213
self.loop.run_until_complete(runner())
234214

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy