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

Commit 5376ba9

Browse files
committed
Issue python#24400: Introduce a distinct type for 'async def' coroutines.
Summary of changes: 1. Coroutines now have a distinct, separate from generators type at the C level: PyGen_Type, and a new typedef PyCoroObject. PyCoroObject shares the initial segment of struct layout with PyGenObject, making it possible to reuse existing generators machinery. The new type is exposed as 'types.CoroutineType'. As a consequence of having a new type, CO_GENERATOR flag is no longer applied to coroutines. 2. Having a separate type for coroutines made it possible to add an __await__ method to the type. Although it is not used by the interpreter (see details on that below), it makes coroutines naturally (without using __instancecheck__) conform to collections.abc.Coroutine and collections.abc.Awaitable ABCs. [The __instancecheck__ is still used for generator-based coroutines, as we don't want to add __await__ for generators.] 3. Add new opcode: GET_YIELD_FROM_ITER. The opcode is needed to allow passing native coroutines to the YIELD_FROM opcode. Before this change, 'yield from o' expression was compiled to: (o) GET_ITER LOAD_CONST YIELD_FROM Now, we use GET_YIELD_FROM_ITER instead of GET_ITER. The reason for adding a new opcode is that GET_ITER is used in some contexts (such as 'for .. in' loops) where passing a coroutine object is invalid. 4. Add two new introspection functions to the inspec module: getcoroutinestate(c) and getcoroutinelocals(c). 5. inspect.iscoroutine(o) is updated to test if 'o' is a native coroutine object. Before this commit it used abc.Coroutine, and it was requested to update inspect.isgenerator(o) to use abc.Generator; it was decided, however, that inspect functions should really be tailored for checking for native types. 6. sys.set_coroutine_wrapper(w) API is updated to work with only native coroutines. Since types.coroutine decorator supports any type of callables now, it would be confusing that it does not work for all types of coroutines. 7. Exceptions logic in generators C implementation was updated to raise clearer messages for coroutines: Before: TypeError("generator raised StopIteration") After: TypeError("coroutine raised StopIteration")
1 parent cd881b8 commit 5376ba9

29 files changed

+1015
-379
lines changed

Doc/c-api/concrete.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,6 @@ Other Objects
112112
weakref.rst
113113
capsule.rst
114114
gen.rst
115+
coro.rst
115116
datetime.rst
116117

Doc/c-api/coro.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.. highlightlang:: c
2+
3+
.. _coro-objects:
4+
5+
Coroutine Objects
6+
-----------------
7+
8+
.. versionadded:: 3.5
9+
10+
Coroutine objects are what functions declared with an ``async`` keyword
11+
return.
12+
13+
14+
.. c:type:: PyCoroObject
15+
16+
The C structure used for coroutine objects.
17+
18+
19+
.. c:var:: PyTypeObject PyCoro_Type
20+
21+
The type object corresponding to coroutine objects.
22+
23+
24+
.. c:function:: int PyCoro_CheckExact(PyObject *ob)
25+
26+
Return true if *ob*'s type is *PyCoro_Type*; *ob* must not be *NULL*.
27+
28+
29+
.. c:function:: PyObject* PyCoro_New(PyFrameObject *frame, PyObject *name, PyObject *qualname)
30+
31+
Create and return a new coroutine object based on the *frame* object,
32+
with ``__name__`` and ``__qualname__`` set to *name* and *qualname*.
33+
A reference to *frame* is stolen by this function. The *frame* argument
34+
must not be *NULL*.

Doc/c-api/gen.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Generator Objects
77

88
Generator objects are what Python uses to implement generator iterators. They
99
are normally created by iterating over a function that yields values, rather
10-
than explicitly calling :c:func:`PyGen_New`.
10+
than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`.
1111

1212

1313
.. c:type:: PyGenObject
@@ -20,19 +20,25 @@ than explicitly calling :c:func:`PyGen_New`.
2020
The type object corresponding to generator objects
2121

2222

23-
.. c:function:: int PyGen_Check(ob)
23+
.. c:function:: int PyGen_Check(PyObject *ob)
2424
2525
Return true if *ob* is a generator object; *ob* must not be *NULL*.
2626
2727
28-
.. c:function:: int PyGen_CheckExact(ob)
28+
.. c:function:: int PyGen_CheckExact(PyObject *ob)
2929
30-
Return true if *ob*'s type is *PyGen_Type* is a generator object; *ob* must not
31-
be *NULL*.
30+
Return true if *ob*'s type is *PyGen_Type*; *ob* must not be *NULL*.
3231
3332
3433
.. c:function:: PyObject* PyGen_New(PyFrameObject *frame)
3534
36-
Create and return a new generator object based on the *frame* object. A
37-
reference to *frame* is stolen by this function. The parameter must not be
35+
Create and return a new generator object based on the *frame* object.
36+
A reference to *frame* is stolen by this function. The argument must not be
3837
*NULL*.
38+
39+
.. c:function:: PyObject* PyGen_NewWithQualName(PyFrameObject *frame, PyObject *name, PyObject *qualname)
40+
41+
Create and return a new generator object based on the *frame* object,
42+
with ``__name__`` and ``__qualname__`` set to *name* and *qualname*.
43+
A reference to *frame* is stolen by this function. The *frame* argument
44+
must not be *NULL*.

Doc/data/refcounts.dat

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,12 @@ PyFunction_SetDefaults:PyObject*:defaults:+1:
491491
PyGen_New:PyObject*::+1:
492492
PyGen_New:PyFrameObject*:frame:0:
493493

494+
PyGen_NewWithQualName:PyObject*::+1:
495+
PyGen_NewWithQualName:PyFrameObject*:frame:0:
496+
497+
PyCoro_New:PyObject*::+1:
498+
PyCoro_New:PyFrameObject*:frame:0:
499+
494500
Py_InitModule:PyObject*::0:
495501
Py_InitModule:const char*:name::
496502
Py_InitModule:PyMethodDef[]:methods::

Doc/glossary.rst

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,23 @@ Glossary
333333
.. index:: single: generator
334334

335335
generator
336-
A function which returns an iterator. It looks like a normal function
337-
except that it contains :keyword:`yield` statements for producing a series
338-
of values usable in a for-loop or that can be retrieved one at a time with
339-
the :func:`next` function. Each :keyword:`yield` temporarily suspends
340-
processing, remembering the location execution state (including local
341-
variables and pending try-statements). When the generator resumes, it
342-
picks-up where it left-off (in contrast to functions which start fresh on
343-
every invocation).
336+
A function which returns a :term:`generator iterator`. It looks like a
337+
normal function except that it contains :keyword:`yield` expressions
338+
for producing a series of values usable in a for-loop or that can be
339+
retrieved one at a time with the :func:`next` function.
340+
341+
Usually refers to a generator function, but may refer to a
342+
*generator iterator* in some contexts. In cases where the intended
343+
meaning isn't clear, using the full terms avoids ambiguity.
344+
345+
generator iterator
346+
An object created by a :term:`generator` function.
347+
348+
Each :keyword:`yield` temporarily suspends processing, remembering the
349+
location execution state (including local variables and pending
350+
try-statements). When the *generator iterator* resumes, it picks-up where
351+
it left-off (in contrast to functions which start fresh on every
352+
invocation).
344353

345354
.. index:: single: generator expression
346355

Doc/library/dis.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,14 @@ result back on the stack.
346346
Implements ``TOS = iter(TOS)``.
347347

348348

349+
.. opcode:: GET_YIELD_FROM_ITER
350+
351+
If ``TOS`` is a :term:`generator iterator` or :term:`coroutine` object
352+
it is left as is. Otherwise, implements ``TOS = iter(TOS)``.
353+
354+
.. versionadded:: 3.5
355+
356+
349357
**Binary operations**
350358

351359
Binary operations remove the top of the stack (TOS) and the second top-most

Doc/library/inspect.rst

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ attributes:
178178
+-----------+-----------------+---------------------------+
179179
| | gi_code | code |
180180
+-----------+-----------------+---------------------------+
181+
| coroutine | __name__ | name |
182+
+-----------+-----------------+---------------------------+
183+
| | __qualname__ | qualified name |
184+
+-----------+-----------------+---------------------------+
185+
| | cr_frame | frame |
186+
+-----------+-----------------+---------------------------+
187+
| | cr_running | is the coroutine running? |
188+
+-----------+-----------------+---------------------------+
189+
| | cr_code | code |
190+
+-----------+-----------------+---------------------------+
181191
| builtin | __doc__ | documentation string |
182192
+-----------+-----------------+---------------------------+
183193
| | __name__ | original name of this |
@@ -279,29 +289,16 @@ attributes:
279289

280290
.. function:: iscoroutinefunction(object)
281291

282-
Return true if the object is a :term:`coroutine function`.
283-
284-
Coroutine functions are defined with an ``async def`` syntax,
285-
or are generators decorated with :func:`types.coroutine`
286-
or :func:`asyncio.coroutine`.
287-
288-
The function will return false for plain Python generator
289-
functions.
292+
Return true if the object is a :term:`coroutine function`
293+
(a function defined with an :keyword:`async def` syntax).
290294

291295
.. versionadded:: 3.5
292296

293297

294298
.. function:: iscoroutine(object)
295299

296-
Return true if the object is a :term:`coroutine`.
297-
298-
Coroutines are results of calls of coroutine functions or
299-
generator functions decorated with :func:`types.coroutine`
300-
or :func:`asyncio.coroutine`.
301-
302-
The function will return false for plain python generators.
303-
304-
See also :class:`collections.abc.Coroutine`.
300+
Return true if the object is a :term:`coroutine` created by an
301+
:keyword:`async def` function.
305302

306303
.. versionadded:: 3.5
307304

@@ -1116,8 +1113,8 @@ code execution::
11161113
pass
11171114

11181115

1119-
Current State of a Generator
1120-
----------------------------
1116+
Current State of Generators and Coroutines
1117+
------------------------------------------
11211118

11221119
When implementing coroutine schedulers and for other advanced uses of
11231120
generators, it is useful to determine whether a generator is currently
@@ -1137,6 +1134,21 @@ generator to be determined easily.
11371134

11381135
.. versionadded:: 3.2
11391136

1137+
.. function:: getcoroutinestate(coroutine)
1138+
1139+
Get current state of a coroutine object. The function is intended to be
1140+
used with coroutine objects created by :keyword:`async def` functions, but
1141+
will accept any coroutine-like object that has ``cr_running`` and
1142+
``cr_frame`` attributes.
1143+
1144+
Possible states are:
1145+
* CORO_CREATED: Waiting to start execution.
1146+
* CORO_RUNNING: Currently being executed by the interpreter.
1147+
* CORO_SUSPENDED: Currently suspended at an await expression.
1148+
* CORO_CLOSED: Execution has completed.
1149+
1150+
.. versionadded:: 3.5
1151+
11401152
The current internal state of the generator can also be queried. This is
11411153
mostly useful for testing purposes, to ensure that internal state is being
11421154
updated as expected:
@@ -1161,6 +1173,13 @@ updated as expected:
11611173

11621174
.. versionadded:: 3.3
11631175

1176+
.. function:: getcoroutinelocals(coroutine)
1177+
1178+
This function is analogous to :func:`~inspect.getgeneratorlocals`, but
1179+
works for coroutine objects created by :keyword:`async def` functions.
1180+
1181+
.. versionadded:: 3.5
1182+
11641183

11651184
.. _inspect-module-cli:
11661185

Doc/library/sys.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,10 @@ always available.
10751075

10761076
.. function:: set_coroutine_wrapper(wrapper)
10771077

1078-
Allows to intercept creation of :term:`coroutine` objects.
1078+
Allows intercepting creation of :term:`coroutine` objects (only ones that
1079+
are created by an :keyword:`async def` function; generators decorated with
1080+
:func:`types.coroutine` or :func:`asyncio.coroutine` will not be
1081+
intercepted).
10791082

10801083
*wrapper* must be either:
10811084

Doc/library/types.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ Standard names are defined for the following types:
9090
generator function.
9191

9292

93+
.. data:: CoroutineType
94+
95+
The type of :term:`coroutine` objects, produced by calling a
96+
function defined with an :keyword:`async def` statement.
97+
98+
.. versionadded:: 3.5
99+
100+
93101
.. data:: CodeType
94102

95103
.. index:: builtin: compile

Doc/reference/compound_stmts.rst

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,15 @@ can be used to create instance variables with different implementation details.
666666
Coroutines
667667
==========
668668

669+
.. index::
670+
statement: async def
671+
statement: async for
672+
statement: async with
673+
keyword: async
674+
keyword: await
675+
676+
.. versionadded:: 3.5
677+
669678
.. _`async def`:
670679

671680
Coroutine function definition
@@ -683,7 +692,11 @@ even if they do not contain ``await`` or ``async`` keywords.
683692

684693
It is a :exc:`SyntaxError` to use :keyword:`yield` expressions in coroutines.
685694

686-
.. versionadded:: 3.5
695+
An example of a coroutine function::
696+
697+
async def func(param1, param2):
698+
do_stuff()
699+
await some_coroutine()
687700

688701

689702
.. _`async for`:
@@ -725,7 +738,8 @@ Is semantically equivalent to::
725738

726739
See also :meth:`__aiter__` and :meth:`__anext__` for details.
727740

728-
.. versionadded:: 3.5
741+
It is a :exc:`SyntaxError` to use ``async for`` statement outside of an
742+
:keyword:`async def` function.
729743

730744

731745
.. _`async with`:
@@ -762,7 +776,8 @@ Is semantically equivalent to::
762776

763777
See also :meth:`__aenter__` and :meth:`__aexit__` for details.
764778

765-
.. versionadded:: 3.5
779+
It is a :exc:`SyntaxError` to use ``async with`` statement outside of an
780+
:keyword:`async def` function.
766781

767782
.. seealso::
768783

Doc/whatsnew/3.5.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,9 @@ inspect
531531
and :func:`~inspect.isawaitable` functions. (Contributed by Yury Selivanov
532532
in :issue:`24017`.)
533533

534+
* New :func:`~inspect.getcoroutinelocals` and :func:`~inspect.getcoroutinestate`
535+
functions. (Contributed by Yury Selivanov in :issue:`24400`.)
536+
534537
ipaddress
535538
---------
536539

@@ -734,6 +737,9 @@ types
734737
* New :func:`~types.coroutine` function. (Contributed by Yury Selivanov
735738
in :issue:`24017`.)
736739

740+
* New :class:`~types.CoroutineType`. (Contributed by Yury Selivanov
741+
in :issue:`24400`.)
742+
737743
urllib
738744
------
739745

@@ -1101,6 +1107,7 @@ Changes in the C API
11011107
the :attr:`__module__` attribute. Would be an AttributeError in future.
11021108
(:issue:`20204`)
11031109

1104-
* As part of PEP 492 implementation, ``tp_reserved`` slot of
1110+
* As part of :pep:`492` implementation, ``tp_reserved`` slot of
11051111
:c:type:`PyTypeObject` was replaced with a
1106-
:c:member:`PyTypeObject.tp_as_async` slot.
1112+
:c:member:`PyTypeObject.tp_as_async` slot. Refer to :ref:`coro-objects` for
1113+
new types, structures and functions.

0 commit comments

Comments
 (0)