Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 08a8a01

Browse files
Anselm KruisAnselm Kruis
Anselm Kruis
authored and
Anselm Kruis
committed
Merge commit 'fc2f407829d9' into master-slp
The outcome of this merge does not compile. Stackless issue #139 will fix this.
2 parents bc04b2f + fc2f407 commit 08a8a01

File tree

20 files changed

+485
-100
lines changed

20 files changed

+485
-100
lines changed

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-origin-link| replace:: :func:`sys.set_coroutine_origin_tracking_depth`
39+
3740
+-----------+-------------------+---------------------------+
3841
| Type | Attribute | Description |
3942
+===========+===================+===========================+
@@ -215,6 +218,10 @@ attributes:
215218
+-----------+-------------------+---------------------------+
216219
| | cr_code | code |
217220
+-----------+-------------------+---------------------------+
221+
| | cr_origin | where coroutine was |
222+
| | | created, or ``None``. See |
223+
| | | |coroutine-origin-link| |
224+
+-----------+-------------------+---------------------------+
218225
| builtin | __doc__ | documentation string |
219226
+-----------+-------------------+---------------------------+
220227
| | __name__ | original 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_origin`` 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_origin_tracking_depth()
679+
680+
Get the current coroutine origin tracking depth, as set by
681+
func:`set_coroutine_origin_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_origin_tracking_depth(depth)
1232+
1233+
Allows enabling or disabling coroutine origin tracking. When
1234+
enabled, the ``cr_origin`` 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_origin`` will
1238+
be None.
1239+
1240+
To enable, pass a *depth* value greater than zero; this sets the
1241+
number of frames 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_origin;
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
@@ -269,6 +269,8 @@ typedef struct _ts {
269269
void (*on_delete)(void *);
270270
void *on_delete_data;
271271

272+
int coroutine_origin_tracking_depth;
273+
272274
PyObject *coroutine_wrapper;
273275
int in_coroutine_wrapper;
274276

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_origin_tracking_enabled = False
229+
self._coroutine_origin_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_origin_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_origin_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_origin_tracking(self, enabled):
1537+
if bool(enabled) == bool(self._coroutine_origin_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_origin_tracking_saved_depth = (
1542+
sys.get_coroutine_origin_tracking_depth())
1543+
sys.set_coroutine_origin_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_origin_tracking_depth(
1547+
self._coroutine_origin_tracking_saved_depth)
1548+
1549+
self._coroutine_origin_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_origin_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_frame(self):
115-
return self.gen.cr_frame
116-
11788
def __del__(self):
11889
# Be careful accessing self.gen.frame -- self.gen might not exist.
11990
gen = getattr(self, 'gen', None)
12091
frame = getattr(gen, 'gi_frame', None)
121-
if frame is None:
122-
frame = getattr(gen, 'cr_frame', None)
12392
if frame is not None and frame.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_origin_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_origin_tracking_depth() > 0)
169155

156+
self.assertEqual(sys.get_coroutine_origin_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_origin_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)