Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
eb636455
Commit
eb636455
authored
Sep 08, 2016
by
Yury Selivanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #28003: Implement PEP 525 -- Asynchronous Generators.
parent
b96ef55d
Changes
27
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
2188 additions
and
95 deletions
+2188
-95
Include/ceval.h
Include/ceval.h
+4
-0
Include/code.h
Include/code.h
+1
-0
Include/genobject.h
Include/genobject.h
+31
-0
Include/pylifecycle.h
Include/pylifecycle.h
+1
-0
Include/pystate.h
Include/pystate.h
+3
-0
Include/symtable.h
Include/symtable.h
+1
-0
Lib/asyncio/base_events.py
Lib/asyncio/base_events.py
+55
-2
Lib/asyncio/coroutines.py
Lib/asyncio/coroutines.py
+4
-1
Lib/asyncio/events.py
Lib/asyncio/events.py
+4
-0
Lib/dis.py
Lib/dis.py
+1
-0
Lib/inspect.py
Lib/inspect.py
+7
-0
Lib/test/badsyntax_async6.py
Lib/test/badsyntax_async6.py
+0
-2
Lib/test/test_asyncgen.py
Lib/test/test_asyncgen.py
+823
-0
Lib/test/test_coroutines.py
Lib/test/test_coroutines.py
+0
-6
Lib/test/test_dis.py
Lib/test/test_dis.py
+1
-1
Lib/test/test_inspect.py
Lib/test/test_inspect.py
+11
-1
Lib/test/test_sys.py
Lib/test/test_sys.py
+26
-0
Lib/types.py
Lib/types.py
+5
-0
Misc/NEWS
Misc/NEWS
+3
-0
Modules/gcmodule.c
Modules/gcmodule.c
+1
-0
Objects/genobject.c
Objects/genobject.c
+986
-46
Python/ceval.c
Python/ceval.c
+83
-29
Python/compile.c
Python/compile.c
+8
-5
Python/pylifecycle.c
Python/pylifecycle.c
+1
-0
Python/pystate.c
Python/pystate.c
+5
-0
Python/symtable.c
Python/symtable.c
+4
-2
Python/sysmodule.c
Python/sysmodule.c
+119
-0
No files found.
Include/ceval.h
View file @
eb636455
...
...
@@ -25,6 +25,10 @@ PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
PyAPI_FUNC
(
void
)
PyEval_SetTrace
(
Py_tracefunc
,
PyObject
*
);
PyAPI_FUNC
(
void
)
_PyEval_SetCoroutineWrapper
(
PyObject
*
);
PyAPI_FUNC
(
PyObject
*
)
_PyEval_GetCoroutineWrapper
(
void
);
PyAPI_FUNC
(
void
)
_PyEval_SetAsyncGenFirstiter
(
PyObject
*
);
PyAPI_FUNC
(
PyObject
*
)
_PyEval_GetAsyncGenFirstiter
(
void
);
PyAPI_FUNC
(
void
)
_PyEval_SetAsyncGenFinalizer
(
PyObject
*
);
PyAPI_FUNC
(
PyObject
*
)
_PyEval_GetAsyncGenFinalizer
(
void
);
#endif
struct
_frame
;
/* Avoid including frameobject.h */
...
...
Include/code.h
View file @
eb636455
...
...
@@ -59,6 +59,7 @@ typedef struct {
``async def`` keywords) */
#define CO_COROUTINE 0x0080
#define CO_ITERABLE_COROUTINE 0x0100
#define CO_ASYNC_GENERATOR 0x0200
/* These are no longer used. */
#if 0
...
...
Include/genobject.h
View file @
eb636455
...
...
@@ -61,6 +61,37 @@ PyObject *_PyAIterWrapper_New(PyObject *aiter);
PyObject
*
_PyCoro_GetAwaitableIter
(
PyObject
*
o
);
PyAPI_FUNC
(
PyObject
*
)
PyCoro_New
(
struct
_frame
*
,
PyObject
*
name
,
PyObject
*
qualname
);
/* Asynchronous Generators */
typedef
struct
{
_PyGenObject_HEAD
(
ag
)
PyObject
*
ag_finalizer
;
/* Flag is set to 1 when hooks set up by sys.set_asyncgen_hooks
were called on the generator, to avoid calling them more
than once. */
int
ag_hooks_inited
;
/* Flag is set to 1 when aclose() is called for the first time, or
when a StopAsyncIteration exception is raised. */
int
ag_closed
;
}
PyAsyncGenObject
;
PyAPI_DATA
(
PyTypeObject
)
PyAsyncGen_Type
;
PyAPI_DATA
(
PyTypeObject
)
_PyAsyncGenASend_Type
;
PyAPI_DATA
(
PyTypeObject
)
_PyAsyncGenWrappedValue_Type
;
PyAPI_DATA
(
PyTypeObject
)
_PyAsyncGenAThrow_Type
;
PyAPI_FUNC
(
PyObject
*
)
PyAsyncGen_New
(
struct
_frame
*
,
PyObject
*
name
,
PyObject
*
qualname
);
#define PyAsyncGen_CheckExact(op) (Py_TYPE(op) == &PyAsyncGen_Type)
PyObject
*
_PyAsyncGenValueWrapperNew
(
PyObject
*
);
int
PyAsyncGen_ClearFreeLists
(
void
);
#endif
#undef _PyGenObject_HEAD
...
...
Include/pylifecycle.h
View file @
eb636455
...
...
@@ -107,6 +107,7 @@ PyAPI_FUNC(void) _PyGC_Fini(void);
PyAPI_FUNC
(
void
)
PySlice_Fini
(
void
);
PyAPI_FUNC
(
void
)
_PyType_Fini
(
void
);
PyAPI_FUNC
(
void
)
_PyRandom_Fini
(
void
);
PyAPI_FUNC
(
void
)
PyAsyncGen_Fini
(
void
);
PyAPI_DATA
(
PyThreadState
*
)
_Py_Finalizing
;
#endif
...
...
Include/pystate.h
View file @
eb636455
...
...
@@ -148,6 +148,9 @@ typedef struct _ts {
Py_ssize_t
co_extra_user_count
;
freefunc
co_extra_freefuncs
[
MAX_CO_EXTRA_USERS
];
PyObject
*
async_gen_firstiter
;
PyObject
*
async_gen_finalizer
;
/* XXX signal handlers should also be here */
}
PyThreadState
;
...
...
Include/symtable.h
View file @
eb636455
...
...
@@ -48,6 +48,7 @@ typedef struct _symtable_entry {
unsigned
ste_child_free
:
1
;
/* true if a child block has free vars,
including free refs to globals */
unsigned
ste_generator
:
1
;
/* true if namespace is a generator */
unsigned
ste_coroutine
:
1
;
/* true if namespace is a coroutine */
unsigned
ste_varargs
:
1
;
/* true if block has varargs */
unsigned
ste_varkeywords
:
1
;
/* true if block has varkeywords */
unsigned
ste_returns_value
:
1
;
/* true if namespace uses return with
...
...
Lib/asyncio/base_events.py
View file @
eb636455
...
...
@@ -13,7 +13,6 @@ conscious design decision, leaving the door open for keyword arguments
to modify the meaning of the API call itself.
"""
import
collections
import
concurrent.futures
import
heapq
...
...
@@ -28,6 +27,7 @@ import time
import
traceback
import
sys
import
warnings
import
weakref
from
.
import
compat
from
.
import
coroutines
...
...
@@ -242,6 +242,13 @@ class BaseEventLoop(events.AbstractEventLoop):
self
.
_task_factory
=
None
self
.
_coroutine_wrapper_set
=
False
# A weak set of all asynchronous generators that are being iterated
# by the loop.
self
.
_asyncgens
=
weakref
.
WeakSet
()
# Set to True when `loop.shutdown_asyncgens` is called.
self
.
_asyncgens_shutdown_called
=
False
def
__repr__
(
self
):
return
(
'<%s running=%s closed=%s debug=%s>'
%
(
self
.
__class__
.
__name__
,
self
.
is_running
(),
...
...
@@ -333,6 +340,46 @@ class BaseEventLoop(events.AbstractEventLoop):
if
self
.
_closed
:
raise
RuntimeError
(
'Event loop is closed'
)
def
_asyncgen_finalizer_hook
(
self
,
agen
):
self
.
_asyncgens
.
discard
(
agen
)
if
not
self
.
is_closed
():
self
.
create_task
(
agen
.
aclose
())
def
_asyncgen_firstiter_hook
(
self
,
agen
):
if
self
.
_asyncgens_shutdown_called
:
warnings
.
warn
(
"asynchronous generator {!r} was scheduled after "
"loop.shutdown_asyncgens() call"
.
format
(
agen
),
ResourceWarning
,
source
=
self
)
self
.
_asyncgens
.
add
(
agen
)
@
coroutine
def
shutdown_asyncgens
(
self
):
"""Shutdown all active asynchronous generators."""
self
.
_asyncgens_shutdown_called
=
True
if
not
len
(
self
.
_asyncgens
):
return
closing_agens
=
list
(
self
.
_asyncgens
)
self
.
_asyncgens
.
clear
()
shutdown_coro
=
tasks
.
gather
(
*
[
ag
.
aclose
()
for
ag
in
closing_agens
],
return_exceptions
=
True
,
loop
=
self
)
results
=
yield
from
shutdown_coro
for
result
,
agen
in
zip
(
results
,
closing_agens
):
if
isinstance
(
result
,
Exception
):
self
.
call_exception_handler
({
'message'
:
'an error occurred during closing of '
'asynchronous generator {!r}'
.
format
(
agen
),
'exception'
:
result
,
'asyncgen'
:
agen
})
def
run_forever
(
self
):
"""Run until stop() is called."""
self
.
_check_closed
()
...
...
@@ -340,6 +387,9 @@ class BaseEventLoop(events.AbstractEventLoop):
raise
RuntimeError
(
'Event loop is running.'
)
self
.
_set_coroutine_wrapper
(
self
.
_debug
)
self
.
_thread_id
=
threading
.
get_ident
()
old_agen_hooks
=
sys
.
get_asyncgen_hooks
()
sys
.
set_asyncgen_hooks
(
firstiter
=
self
.
_asyncgen_firstiter_hook
,
finalizer
=
self
.
_asyncgen_finalizer_hook
)
try
:
while
True
:
self
.
_run_once
()
...
...
@@ -349,6 +399,7 @@ class BaseEventLoop(events.AbstractEventLoop):
self
.
_stopping
=
False
self
.
_thread_id
=
None
self
.
_set_coroutine_wrapper
(
False
)
sys
.
set_asyncgen_hooks
(
*
old_agen_hooks
)
def
run_until_complete
(
self
,
future
):
"""Run until the Future is done.
...
...
@@ -1179,7 +1230,9 @@ class BaseEventLoop(events.AbstractEventLoop):
- 'handle' (optional): Handle instance;
- 'protocol' (optional): Protocol instance;
- 'transport' (optional): Transport instance;
- 'socket' (optional): Socket instance.
- 'socket' (optional): Socket instance;
- 'asyncgen' (optional): Asynchronous generator that caused
the exception.
New keys maybe introduced in the future.
...
...
Lib/asyncio/coroutines.py
View file @
eb636455
...
...
@@ -276,7 +276,10 @@ def _format_coroutine(coro):
try
:
coro_code
=
coro
.
gi_code
except
AttributeError
:
coro_code
=
coro
.
cr_code
try
:
coro_code
=
coro
.
cr_code
except
AttributeError
:
return
repr
(
coro
)
try
:
coro_frame
=
coro
.
gi_frame
...
...
Lib/asyncio/events.py
View file @
eb636455
...
...
@@ -248,6 +248,10 @@ class AbstractEventLoop:
"""
raise
NotImplementedError
def
shutdown_asyncgens
(
self
):
"""Shutdown all active asynchronous generators."""
raise
NotImplementedError
# Methods scheduling callbacks. All these return Handles.
def
_timer_handle_cancelled
(
self
,
handle
):
...
...
Lib/dis.py
View file @
eb636455
...
...
@@ -87,6 +87,7 @@ COMPILER_FLAG_NAMES = {
64
:
"NOFREE"
,
128
:
"COROUTINE"
,
256
:
"ITERABLE_COROUTINE"
,
512
:
"ASYNC_GENERATOR"
,
}
def
pretty_flags
(
flags
):
...
...
Lib/inspect.py
View file @
eb636455
...
...
@@ -185,6 +185,13 @@ def iscoroutinefunction(object):
return
bool
((
isfunction
(
object
)
or
ismethod
(
object
))
and
object
.
__code__
.
co_flags
&
CO_COROUTINE
)
def
isasyncgenfunction
(
object
):
return
bool
((
isfunction
(
object
)
or
ismethod
(
object
))
and
object
.
__code__
.
co_flags
&
CO_ASYNC_GENERATOR
)
def
isasyncgen
(
object
):
return
isinstance
(
object
,
types
.
AsyncGeneratorType
)
def
isgenerator
(
object
):
"""Return true if the object is a generator.
...
...
Lib/test/badsyntax_async6.py
deleted
100644 → 0
View file @
b96ef55d
async
def
foo
():
yield
Lib/test/test_asyncgen.py
0 → 100644
View file @
eb636455
This diff is collapsed.
Click to expand it.
Lib/test/test_coroutines.py
View file @
eb636455
...
...
@@ -88,12 +88,6 @@ class AsyncBadSyntaxTest(unittest.TestCase):
with
self
.
assertRaisesRegex
(
SyntaxError
,
'invalid syntax'
):
import
test.badsyntax_async5
def
test_badsyntax_6
(
self
):
with
self
.
assertRaisesRegex
(
SyntaxError
,
"'yield' inside async function"
):
import
test.badsyntax_async6
def
test_badsyntax_7
(
self
):
with
self
.
assertRaisesRegex
(
SyntaxError
,
"'yield from' inside async function"
):
...
...
Lib/test/test_dis.py
View file @
eb636455
...
...
@@ -574,7 +574,7 @@ Argument count: 0
Kw-only arguments: 0
Number of locals: 2
Stack size: 17
Flags: OPTIMIZED, NEWLOCALS,
GENERATOR,
NOFREE, COROUTINE
Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE
Constants:
0: None
1: 1"""
...
...
Lib/test/test_inspect.py
View file @
eb636455
...
...
@@ -65,7 +65,8 @@ class IsTestBase(unittest.TestCase):
inspect
.
isframe
,
inspect
.
isfunction
,
inspect
.
ismethod
,
inspect
.
ismodule
,
inspect
.
istraceback
,
inspect
.
isgenerator
,
inspect
.
isgeneratorfunction
,
inspect
.
iscoroutine
,
inspect
.
iscoroutinefunction
])
inspect
.
iscoroutine
,
inspect
.
iscoroutinefunction
,
inspect
.
isasyncgen
,
inspect
.
isasyncgenfunction
])
def
istest
(
self
,
predicate
,
exp
):
obj
=
eval
(
exp
)
...
...
@@ -73,6 +74,7 @@ class IsTestBase(unittest.TestCase):
for
other
in
self
.
predicates
-
set
([
predicate
]):
if
(
predicate
==
inspect
.
isgeneratorfunction
or
\
predicate
==
inspect
.
isasyncgenfunction
or
\
predicate
==
inspect
.
iscoroutinefunction
)
and
\
other
==
inspect
.
isfunction
:
continue
...
...
@@ -82,6 +84,10 @@ def generator_function_example(self):
for
i
in
range
(
2
):
yield
i
async
def
async_generator_function_example
(
self
):
async
for
i
in
range
(
2
):
yield
i
async
def
coroutine_function_example
(
self
):
return
'spam'
...
...
@@ -122,6 +128,10 @@ class TestPredicates(IsTestBase):
self
.
istest
(
inspect
.
isdatadescriptor
,
'collections.defaultdict.default_factory'
)
self
.
istest
(
inspect
.
isgenerator
,
'(x for x in range(2))'
)
self
.
istest
(
inspect
.
isgeneratorfunction
,
'generator_function_example'
)
self
.
istest
(
inspect
.
isasyncgen
,
'async_generator_function_example(1)'
)
self
.
istest
(
inspect
.
isasyncgenfunction
,
'async_generator_function_example'
)
with
warnings
.
catch_warnings
():
warnings
.
simplefilter
(
"ignore"
)
...
...
Lib/test/test_sys.py
View file @
eb636455
...
...
@@ -1192,6 +1192,32 @@ class SizeofTest(unittest.TestCase):
# sys.flags
check
(
sys
.
flags
,
vsize
(
''
)
+
self
.
P
*
len
(
sys
.
flags
))
def
test_asyncgen_hooks
(
self
):
old
=
sys
.
get_asyncgen_hooks
()
self
.
assertIsNone
(
old
.
firstiter
)
self
.
assertIsNone
(
old
.
finalizer
)
firstiter
=
lambda
*
a
:
None
sys
.
set_asyncgen_hooks
(
firstiter
=
firstiter
)
hooks
=
sys
.
get_asyncgen_hooks
()
self
.
assertIs
(
hooks
.
firstiter
,
firstiter
)
self
.
assertIs
(
hooks
[
0
],
firstiter
)
self
.
assertIs
(
hooks
.
finalizer
,
None
)
self
.
assertIs
(
hooks
[
1
],
None
)
finalizer
=
lambda
*
a
:
None
sys
.
set_asyncgen_hooks
(
finalizer
=
finalizer
)
hooks
=
sys
.
get_asyncgen_hooks
()
self
.
assertIs
(
hooks
.
firstiter
,
firstiter
)
self
.
assertIs
(
hooks
[
0
],
firstiter
)
self
.
assertIs
(
hooks
.
finalizer
,
finalizer
)
self
.
assertIs
(
hooks
[
1
],
finalizer
)
sys
.
set_asyncgen_hooks
(
*
old
)
cur
=
sys
.
get_asyncgen_hooks
()
self
.
assertIsNone
(
cur
.
firstiter
)
self
.
assertIsNone
(
cur
.
finalizer
)
def
test_main
():
test
.
support
.
run_unittest
(
SysModuleTest
,
SizeofTest
)
...
...
Lib/types.py
View file @
eb636455
...
...
@@ -24,6 +24,11 @@ _c = _c()
CoroutineType
=
type
(
_c
)
_c
.
close
()
# Prevent ResourceWarning
async
def
_ag
():
yield
_ag
=
_ag
()
AsyncGeneratorType
=
type
(
_ag
)
class
_C
:
def
_m
(
self
):
pass
MethodType
=
type
(
_C
().
_m
)
...
...
Misc/NEWS
View file @
eb636455
...
...
@@ -103,6 +103,9 @@ Core and Builtins
-
Issue
#
27985
:
Implement
PEP
526
--
Syntax
for
Variable
Annotations
.
Patch
by
Ivan
Levkivskyi
.
-
Issue
#
28003
:
Implement
PEP
525
--
Asynchronous
Generators
.
Library
-------
...
...
Modules/gcmodule.c
View file @
eb636455
...
...
@@ -892,6 +892,7 @@ clear_freelists(void)
(
void
)
PyList_ClearFreeList
();
(
void
)
PyDict_ClearFreeList
();
(
void
)
PySet_ClearFreeList
();
(
void
)
PyAsyncGen_ClearFreeLists
();
}
/* This is the main function. Read this to understand how the
...
...
Objects/genobject.c
View file @
eb636455
This diff is collapsed.
Click to expand it.
Python/ceval.c
View file @
eb636455
...
...
@@ -1204,7 +1204,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
f
->
f_stacktop
=
NULL
;
/* remains NULL unless yield suspends frame */
f
->
f_executing
=
1
;
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
))
{
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
|
CO_ASYNC_GENERATOR
))
{
if
(
!
throwflag
&&
f
->
f_exc_type
!=
NULL
&&
f
->
f_exc_type
!=
Py_None
)
{
/* We were in an except handler when we left,
restore the exception state which was put aside
...
...
@@ -2083,36 +2083,45 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
PyObject
*
aiter
=
TOP
();
PyTypeObject
*
type
=
Py_TYPE
(
aiter
);
if
(
type
->
tp_as_async
!=
NULL
)
getter
=
type
->
tp_as_async
->
am_anext
;
if
(
PyAsyncGen_CheckExact
(
aiter
))
{
awaitable
=
type
->
tp_as_async
->
am_anext
(
aiter
);
if
(
awaitable
==
NULL
)
{
goto
error
;
}
}
else
{
if
(
type
->
tp_as_async
!=
NULL
){
getter
=
type
->
tp_as_async
->
am_anext
;
}
if
(
getter
!=
NULL
)
{
next_iter
=
(
*
getter
)(
aiter
);
if
(
next_iter
==
NULL
)
{
if
(
getter
!=
NULL
)
{
next_iter
=
(
*
getter
)(
aiter
);
if
(
next_iter
==
NULL
)
{
goto
error
;
}
}
else
{
PyErr_Format
(
PyExc_TypeError
,
"'async for' requires an iterator with "
"__anext__ method, got %.100s"
,
type
->
tp_name
);
goto
error
;
}
}
else
{
PyErr_Format
(
PyExc_TypeError
,
"'async for' requires an iterator with "
"__anext__ method, got %.100s"
,
type
->
tp_name
);
goto
error
;
}
awaitable
=
_PyCoro_GetAwaitableIter
(
next_iter
);
if
(
awaitable
==
NULL
)
{
PyErr_Format
(
PyExc_TypeError
,
"'async for' received an invalid object "
"from __anext__: %.100s"
,
Py_TYPE
(
next_iter
)
->
tp_name
);
awaitable
=
_PyCoro_GetAwaitableIter
(
next_iter
);
if
(
awaitable
==
NULL
)
{
PyErr_Format
(
PyExc_TypeError
,
"'async for' received an invalid object "
"from __anext__: %.100s"
,
Py_TYPE
(
next_iter
)
->
tp_name
);
Py_DECREF
(
next_iter
);
goto
error
;
}
else
Py_DECREF
(
next_iter
);
Py_DECREF
(
next_iter
);
goto
error
;
}
else
{
Py_DECREF
(
next_iter
);
}
}
PUSH
(
awaitable
);
PREDICT
(
LOAD_CONST
);
...
...
@@ -2187,6 +2196,17 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET
(
YIELD_VALUE
)
{
retval
=
POP
();
if
(
co
->
co_flags
&
CO_ASYNC_GENERATOR
)
{
PyObject
*
w
=
_PyAsyncGenValueWrapperNew
(
retval
);
Py_DECREF
(
retval
);
if
(
w
==
NULL
)
{
retval
=
NULL
;
goto
error
;
}
retval
=
w
;
}
f
->
f_stacktop
=
stack_pointer
;
why
=
WHY_YIELD
;
goto
fast_yield
;
...
...
@@ -3712,7 +3732,7 @@ fast_block_end:
assert
((
retval
!=
NULL
)
^
(
PyErr_Occurred
()
!=
NULL
));
fast_yield:
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
))
{
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
|
CO_ASYNC_GENERATOR
))
{
/* The purpose of this block is to put aside the generator's exception
state and restore that of the calling frame. If the current
...
...
@@ -4156,8 +4176,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
freevars
[
PyTuple_GET_SIZE
(
co
->
co_cellvars
)
+
i
]
=
o
;
}
/* Handle generator/coroutine */
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
))
{
/* Handle generator/coroutine
/asynchronous generator
*/
if
(
co
->
co_flags
&
(
CO_GENERATOR
|
CO_COROUTINE
|
CO_ASYNC_GENERATOR
))
{
PyObject
*
gen
;
PyObject
*
coro_wrapper
=
tstate
->
coroutine_wrapper
;
int
is_coro
=
co
->
co_flags
&
CO_COROUTINE
;
...
...
@@ -4182,6 +4202,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
* and return that as the value. */
if
(
is_coro
)
{
gen
=
PyCoro_New
(
f
,
name
,
qualname
);
}
else
if
(
co
->
co_flags
&
CO_ASYNC_GENERATOR
)
{
gen
=
PyAsyncGen_New
(
f
,
name
,
qualname
);
}
else
{
gen
=
PyGen_NewWithQualName
(
f
,
name
,
qualname
);
}
...
...
@@ -4660,6 +4682,38 @@ _PyEval_GetCoroutineWrapper(void)
return
tstate
->
coroutine_wrapper
;
}
void
_PyEval_SetAsyncGenFirstiter
(
PyObject
*
firstiter
)
{
PyThreadState
*
tstate
=
PyThreadState_GET
();
Py_XINCREF
(
firstiter
);
Py_XSETREF
(
tstate
->
async_gen_firstiter
,
firstiter
);
}
PyObject
*
_PyEval_GetAsyncGenFirstiter
(
void
)
{
PyThreadState
*
tstate
=
PyThreadState_GET
();
return
tstate
->
async_gen_firstiter
;
}
void
_PyEval_SetAsyncGenFinalizer
(
PyObject
*
finalizer
)
{
PyThreadState
*
tstate
=
PyThreadState_GET
();
Py_XINCREF
(
finalizer
);
Py_XSETREF
(
tstate
->
async_gen_finalizer
,
finalizer
);
}
PyObject
*
_PyEval_GetAsyncGenFinalizer
(
void
)
{
PyThreadState
*
tstate
=
PyThreadState_GET
();
return
tstate
->
async_gen_finalizer
;
}
PyObject
*
PyEval_GetBuiltins
(
void
)
{
...
...
Python/compile.c
View file @
eb636455
...
...
@@ -1886,8 +1886,6 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
return
0
;
}
if
(
is_async
)
co
->
co_flags
|=
CO_COROUTINE
;
compiler_make_closure
(
c
,
co
,
funcflags
,
qualname
);
Py_DECREF
(
qualname
);
Py_DECREF
(
co
);
...
...
@@ -2801,6 +2799,9 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
if
(
c
->
u
->
u_ste
->
ste_type
!=
FunctionBlock
)
return
compiler_error
(
c
,
"'return' outside function"
);
if
(
s
->
v
.
Return
.
value
)
{
if
(
c
->
u
->
u_ste
->
ste_coroutine
&&
c
->
u
->
u_ste
->
ste_generator
)
return
compiler_error
(
c
,
"'return' with value in async generator"
);
VISIT
(
c
,
expr
,
s
->
v
.
Return
.
value
);
}
else
...
...
@@ -4115,8 +4116,6 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
case
Yield_kind
:
if
(
c
->
u
->
u_ste
->
ste_type
!=
FunctionBlock
)
return
compiler_error
(
c
,
"'yield' outside function"
);
if
(
c
->
u
->
u_scope_type
==
COMPILER_SCOPE_ASYNC_FUNCTION
)
return
compiler_error
(
c
,
"'yield' inside async function"
);
if
(
e
->
v
.
Yield
.
value
)
{
VISIT
(
c
,
expr
,
e
->
v
.
Yield
.
value
);
}
...
...
@@ -4992,8 +4991,12 @@ compute_code_flags(struct compiler *c)
flags
|=
CO_NEWLOCALS
|
CO_OPTIMIZED
;
if
(
ste
->
ste_nested
)
flags
|=
CO_NESTED
;
if
(
ste
->
ste_generator
)
if
(
ste
->
ste_generator
&&
!
ste
->
ste_coroutine
)
flags
|=
CO_GENERATOR
;
if
(
!
ste
->
ste_generator
&&
ste
->
ste_coroutine
)
flags
|=
CO_COROUTINE
;
if
(
ste
->
ste_generator
&&
ste
->
ste_coroutine
)
flags
|=
CO_ASYNC_GENERATOR
;
if
(
ste
->
ste_varargs
)
flags
|=
CO_VARARGS
;
if
(
ste
->
ste_varkeywords
)
...
...
Python/pylifecycle.c
View file @
eb636455
...
...
@@ -694,6 +694,7 @@ Py_FinalizeEx(void)
_PyGC_Fini
();
_PyRandom_Fini
();
_PyArg_Fini
();
PyAsyncGen_Fini
();
/* Cleanup Unicode implementation */
_PyUnicode_Fini
();
...
...
Python/pystate.c
View file @
eb636455
...
...
@@ -229,6 +229,9 @@ new_threadstate(PyInterpreterState *interp, int init)
tstate
->
in_coroutine_wrapper
=
0
;
tstate
->
co_extra_user_count
=
0
;
tstate
->
async_gen_firstiter
=
NULL
;
tstate
->
async_gen_finalizer
=
NULL
;
if
(
init
)
_PyThreadState_Init
(
tstate
);
...
...
@@ -408,6 +411,8 @@ PyThreadState_Clear(PyThreadState *tstate)
Py_CLEAR
(
tstate
->
c_traceobj
);
Py_CLEAR
(
tstate
->
coroutine_wrapper
);
Py_CLEAR
(
tstate
->
async_gen_firstiter
);
Py_CLEAR
(
tstate
->
async_gen_finalizer
);
}
...
...
Python/symtable.c
View file @
eb636455
...
...
@@ -63,6 +63,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste
->
ste_nested
=
1
;
ste
->
ste_child_free
=
0
;
ste
->
ste_generator
=
0
;
ste
->
ste_coroutine
=
0
;
ste
->
ste_returns_value
=
0
;
ste
->
ste_needs_class_closure
=
0
;
...
...
@@ -378,7 +379,7 @@ error_at_directive(PySTEntryObject *ste, PyObject *name)
PyLong_AsLong
(
PyTuple_GET_ITEM
(
data
,
2
)));
return
0
;
}
}
}
PyErr_SetString
(
PyExc_RuntimeError
,
"BUG: internal directive bookkeeping broken"
);
...
...
@@ -1397,6 +1398,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
FunctionBlock
,
(
void
*
)
s
,
s
->
lineno
,
s
->
col_offset
))
VISIT_QUIT
(
st
,
0
);
st
->
st_cur
->
ste_coroutine
=
1
;
VISIT
(
st
,
arguments
,
s
->
v
.
AsyncFunctionDef
.
args
);
VISIT_SEQ
(
st
,
stmt
,
s
->
v
.
AsyncFunctionDef
.
body
);
if
(
!
symtable_exit_block
(
st
,
s
))
...
...
@@ -1492,7 +1494,7 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
break
;
case
Await_kind
:
VISIT
(
st
,
expr
,
e
->
v
.
Await
.
value
);
st
->
st_cur
->
ste_
generator
=
1
;
st
->
st_cur
->
ste_
coroutine
=
1
;
break
;
case
Compare_kind
:
VISIT
(
st
,
expr
,
e
->
v
.
Compare
.
left
);
...
...
Python/sysmodule.c
View file @
eb636455
...
...
@@ -717,6 +717,113 @@ Return the wrapper for coroutine objects set by sys.set_coroutine_wrapper."
);
static
PyTypeObject
AsyncGenHooksType
;
PyDoc_STRVAR
(
asyncgen_hooks_doc
,
"asyncgen_hooks
\n
\
\n
\
A struct sequence providing information about asynhronous
\n
\
generators hooks. The attributes are read only."
);
static
PyStructSequence_Field
asyncgen_hooks_fields
[]
=
{
{
"firstiter"
,
"Hook to intercept first iteration"
},
{
"finalizer"
,
"Hook to intercept finalization"
},
{
0
}
};
static
PyStructSequence_Desc
asyncgen_hooks_desc
=
{
"asyncgen_hooks"
,
/* name */
asyncgen_hooks_doc
,
/* doc */
asyncgen_hooks_fields
,
/* fields */
2
};
static
PyObject
*
sys_set_asyncgen_hooks
(
PyObject
*
self
,
PyObject
*
args
,
PyObject
*
kw
)
{
static
char
*
keywords
[]
=
{
"firstiter"
,
"finalizer"
,
NULL
};
PyObject
*
firstiter
=
NULL
;
PyObject
*
finalizer
=
NULL
;
if
(
!
PyArg_ParseTupleAndKeywords
(
args
,
kw
,
"|OO"
,
keywords
,
&
firstiter
,
&
finalizer
))
{
return
NULL
;
}
if
(
finalizer
&&
finalizer
!=
Py_None
)
{
if
(
!
PyCallable_Check
(
finalizer
))
{
PyErr_Format
(
PyExc_TypeError
,
"callable finalizer expected, got %.50s"
,
Py_TYPE
(
finalizer
)
->
tp_name
);
return
NULL
;
}
_PyEval_SetAsyncGenFinalizer
(
finalizer
);
}
else
if
(
finalizer
==
Py_None
)
{
_PyEval_SetAsyncGenFinalizer
(
NULL
);
}
if
(
firstiter
&&
firstiter
!=
Py_None
)
{
if
(
!
PyCallable_Check
(
firstiter
))
{
PyErr_Format
(
PyExc_TypeError
,
"callable firstiter expected, got %.50s"
,
Py_TYPE
(
firstiter
)
->
tp_name
);
return
NULL
;
}
_PyEval_SetAsyncGenFirstiter
(
firstiter
);
}
else
if
(
firstiter
==
Py_None
)
{
_PyEval_SetAsyncGenFirstiter
(
NULL
);
}
Py_RETURN_NONE
;
}
PyDoc_STRVAR
(
set_asyncgen_hooks_doc
,
"set_asyncgen_hooks(*, firstiter=None, finalizer=None)
\n
\
\n
\
Set a finalizer for async generators objects."
);
static
PyObject
*
sys_get_asyncgen_hooks
(
PyObject
*
self
,
PyObject
*
args
)
{
PyObject
*
res
;
PyObject
*
firstiter
=
_PyEval_GetAsyncGenFirstiter
();
PyObject
*
finalizer
=
_PyEval_GetAsyncGenFinalizer
();
res
=
PyStructSequence_New
(
&
AsyncGenHooksType
);
if
(
res
==
NULL
)
{
return
NULL
;
}
if
(
firstiter
==
NULL
)
{
firstiter
=
Py_None
;
}
if
(
finalizer
==
NULL
)
{
finalizer
=
Py_None
;
}
Py_INCREF
(
firstiter
);
PyStructSequence_SET_ITEM
(
res
,
0
,
firstiter
);
Py_INCREF
(
finalizer
);
PyStructSequence_SET_ITEM
(
res
,
1
,
finalizer
);
return
res
;
}
PyDoc_STRVAR
(
get_asyncgen_hooks_doc
,
"get_asyncgen_hooks()
\n
\
\n
\
Return a namedtuple of installed asynchronous generators hooks \
(firstiter, finalizer)."
);
static
PyTypeObject
Hash_InfoType
;
PyDoc_STRVAR
(
hash_info_doc
,
...
...
@@ -1315,6 +1422,10 @@ static PyMethodDef sys_methods[] = {
set_coroutine_wrapper_doc
},
{
"get_coroutine_wrapper"
,
sys_get_coroutine_wrapper
,
METH_NOARGS
,
get_coroutine_wrapper_doc
},
{
"set_asyncgen_hooks"
,
sys_set_asyncgen_hooks
,
METH_VARARGS
|
METH_KEYWORDS
,
set_asyncgen_hooks_doc
},
{
"get_asyncgen_hooks"
,
sys_get_asyncgen_hooks
,
METH_NOARGS
,
get_asyncgen_hooks_doc
},
{
NULL
,
NULL
}
/* sentinel */
};
...
...
@@ -1950,6 +2061,14 @@ _PySys_Init(void)
SET_SYS_FROM_STRING
(
"thread_info"
,
PyThread_GetInfo
());
#endif
/* initialize asyncgen_hooks */
if
(
AsyncGenHooksType
.
tp_name
==
NULL
)
{
if
(
PyStructSequence_InitType2
(
&
AsyncGenHooksType
,
&
asyncgen_hooks_desc
)
<
0
)
{
return
NULL
;
}
}
#undef SET_SYS_FROM_STRING
#undef SET_SYS_FROM_STRING_BORROW
if
(
PyErr_Occurred
())
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment