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
1af2bf75
Commit
1af2bf75
authored
May 11, 2015
by
Yury Selivanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
asyncio: Support PEP 492. Issue #24017.
parent
d7e19bb5
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
116 additions
and
27 deletions
+116
-27
Lib/asyncio/base_events.py
Lib/asyncio/base_events.py
+38
-9
Lib/asyncio/coroutines.py
Lib/asyncio/coroutines.py
+64
-13
Lib/asyncio/futures.py
Lib/asyncio/futures.py
+4
-0
Lib/asyncio/tasks.py
Lib/asyncio/tasks.py
+6
-2
Lib/test/test_asyncio/test_base_events.py
Lib/test/test_asyncio/test_base_events.py
+2
-1
Lib/test/test_asyncio/test_tasks.py
Lib/test/test_asyncio/test_tasks.py
+2
-2
No files found.
Lib/asyncio/base_events.py
View file @
1af2bf75
...
@@ -191,8 +191,8 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -191,8 +191,8 @@ class BaseEventLoop(events.AbstractEventLoop):
self
.
_thread_id
=
None
self
.
_thread_id
=
None
self
.
_clock_resolution
=
time
.
get_clock_info
(
'monotonic'
).
resolution
self
.
_clock_resolution
=
time
.
get_clock_info
(
'monotonic'
).
resolution
self
.
_exception_handler
=
None
self
.
_exception_handler
=
None
self
.
_debug
=
(
not
sys
.
flags
.
ignore_environment
self
.
set_debug
(
(
not
sys
.
flags
.
ignore_environment
and
bool
(
os
.
environ
.
get
(
'PYTHONASYNCIODEBUG'
)))
and
bool
(
os
.
environ
.
get
(
'PYTHONASYNCIODEBUG'
)
)))
# In debug mode, if the execution of a callback or a step of a task
# In debug mode, if the execution of a callback or a step of a task
# exceed this duration in seconds, the slow callback/task is logged.
# exceed this duration in seconds, the slow callback/task is logged.
self
.
slow_callback_duration
=
0.1
self
.
slow_callback_duration
=
0.1
...
@@ -360,13 +360,18 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -360,13 +360,18 @@ class BaseEventLoop(events.AbstractEventLoop):
return
return
if
self
.
_debug
:
if
self
.
_debug
:
logger
.
debug
(
"Close %r"
,
self
)
logger
.
debug
(
"Close %r"
,
self
)
self
.
_closed
=
True
try
:
self
.
_ready
.
clear
()
self
.
_closed
=
True
self
.
_scheduled
.
clear
()
self
.
_ready
.
clear
()
executor
=
self
.
_default_executor
self
.
_scheduled
.
clear
()
if
executor
is
not
None
:
executor
=
self
.
_default_executor
self
.
_default_executor
=
None
if
executor
is
not
None
:
executor
.
shutdown
(
wait
=
False
)
self
.
_default_executor
=
None
executor
.
shutdown
(
wait
=
False
)
finally
:
# It is important to unregister "sys.coroutine_wrapper"
# if it was registered.
self
.
set_debug
(
False
)
def
is_closed
(
self
):
def
is_closed
(
self
):
"""Returns True if the event loop was closed."""
"""Returns True if the event loop was closed."""
...
@@ -1199,3 +1204,27 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -1199,3 +1204,27 @@ class BaseEventLoop(events.AbstractEventLoop):
def
set_debug
(
self
,
enabled
):
def
set_debug
(
self
,
enabled
):
self
.
_debug
=
enabled
self
.
_debug
=
enabled
wrapper
=
coroutines
.
debug_wrapper
try
:
set_wrapper
=
sys
.
set_coroutine_wrapper
except
AttributeError
:
pass
else
:
current_wrapper
=
sys
.
get_coroutine_wrapper
()
if
enabled
:
if
current_wrapper
not
in
(
None
,
wrapper
):
warnings
.
warn
(
"loop.set_debug(True): cannot set debug coroutine "
"wrapper; another wrapper is already set %r"
%
current_wrapper
,
RuntimeWarning
)
else
:
set_wrapper
(
wrapper
)
else
:
if
current_wrapper
not
in
(
None
,
wrapper
):
warnings
.
warn
(
"loop.set_debug(False): cannot unset debug coroutine "
"wrapper; another wrapper was set %r"
%
current_wrapper
,
RuntimeWarning
)
else
:
set_wrapper
(
None
)
Lib/asyncio/coroutines.py
View file @
1af2bf75
...
@@ -14,6 +14,9 @@ from . import futures
...
@@ -14,6 +14,9 @@ from . import futures
from
.log
import
logger
from
.log
import
logger
_PY35
=
sys
.
version_info
>=
(
3
,
5
)
# Opcode of "yield from" instruction
# Opcode of "yield from" instruction
_YIELD_FROM
=
opcode
.
opmap
[
'YIELD_FROM'
]
_YIELD_FROM
=
opcode
.
opmap
[
'YIELD_FROM'
]
...
@@ -30,6 +33,27 @@ _DEBUG = (not sys.flags.ignore_environment
...
@@ -30,6 +33,27 @@ _DEBUG = (not sys.flags.ignore_environment
and
bool
(
os
.
environ
.
get
(
'PYTHONASYNCIODEBUG'
)))
and
bool
(
os
.
environ
.
get
(
'PYTHONASYNCIODEBUG'
)))
try
:
types
.
coroutine
except
AttributeError
:
native_coroutine_support
=
False
else
:
native_coroutine_support
=
True
try
:
_iscoroutinefunction
=
inspect
.
iscoroutinefunction
except
AttributeError
:
_iscoroutinefunction
=
lambda
func
:
False
try
:
inspect
.
CO_COROUTINE
except
AttributeError
:
_is_native_coro_code
=
lambda
code
:
False
else
:
_is_native_coro_code
=
lambda
code
:
(
code
.
co_flags
&
inspect
.
CO_COROUTINE
)
# Check for CPython issue #21209
# Check for CPython issue #21209
def
has_yield_from_bug
():
def
has_yield_from_bug
():
class
MyGen
:
class
MyGen
:
...
@@ -54,16 +78,27 @@ _YIELD_FROM_BUG = has_yield_from_bug()
...
@@ -54,16 +78,27 @@ _YIELD_FROM_BUG = has_yield_from_bug()
del
has_yield_from_bug
del
has_yield_from_bug
def
debug_wrapper
(
gen
):
# This function is called from 'sys.set_coroutine_wrapper'.
# We only wrap here coroutines defined via 'async def' syntax.
# Generator-based coroutines are wrapped in @coroutine
# decorator.
if
_is_native_coro_code
(
gen
.
gi_code
):
return
CoroWrapper
(
gen
,
None
)
else
:
return
gen
class
CoroWrapper
:
class
CoroWrapper
:
# Wrapper for coroutine object in _DEBUG mode.
# Wrapper for coroutine object in _DEBUG mode.
def
__init__
(
self
,
gen
,
func
):
def
__init__
(
self
,
gen
,
func
=
None
):
assert
inspect
.
isgenerator
(
gen
),
gen
assert
inspect
.
isgenerator
(
gen
)
or
inspect
.
iscoroutine
(
gen
)
,
gen
self
.
gen
=
gen
self
.
gen
=
gen
self
.
func
=
func
self
.
func
=
func
# Used to unwrap @coroutine decorator
self
.
_source_traceback
=
traceback
.
extract_stack
(
sys
.
_getframe
(
1
))
self
.
_source_traceback
=
traceback
.
extract_stack
(
sys
.
_getframe
(
1
))
# __name__, __qualname__, __doc__ attributes are set by the coroutine(
)
self
.
__name__
=
getattr
(
gen
,
'__name__'
,
None
)
# decorator
self
.
__qualname__
=
getattr
(
gen
,
'__qualname__'
,
None
)
def
__repr__
(
self
):
def
__repr__
(
self
):
coro_repr
=
_format_coroutine
(
self
)
coro_repr
=
_format_coroutine
(
self
)
...
@@ -75,6 +110,9 @@ class CoroWrapper:
...
@@ -75,6 +110,9 @@ class CoroWrapper:
def
__iter__
(
self
):
def
__iter__
(
self
):
return
self
return
self
if
_PY35
:
__await__
=
__iter__
# make compatible with 'await' expression
def
__next__
(
self
):
def
__next__
(
self
):
return
next
(
self
.
gen
)
return
next
(
self
.
gen
)
...
@@ -133,6 +171,14 @@ def coroutine(func):
...
@@ -133,6 +171,14 @@ def coroutine(func):
If the coroutine is not yielded from before it is destroyed,
If the coroutine is not yielded from before it is destroyed,
an error message is logged.
an error message is logged.
"""
"""
is_coroutine
=
_iscoroutinefunction
(
func
)
if
is_coroutine
and
_is_native_coro_code
(
func
.
__code__
):
# In Python 3.5 that's all we need to do for coroutines
# defiend with "async def".
# Wrapping in CoroWrapper will happen via
# 'sys.set_coroutine_wrapper' function.
return
func
if
inspect
.
isgeneratorfunction
(
func
):
if
inspect
.
isgeneratorfunction
(
func
):
coro
=
func
coro
=
func
else
:
else
:
...
@@ -144,18 +190,22 @@ def coroutine(func):
...
@@ -144,18 +190,22 @@ def coroutine(func):
return
res
return
res
if
not
_DEBUG
:
if
not
_DEBUG
:
wrapper
=
coro
if
native_coroutine_support
:
wrapper
=
types
.
coroutine
(
coro
)
else
:
wrapper
=
coro
else
:
else
:
@
functools
.
wraps
(
func
)
@
functools
.
wraps
(
func
)
def
wrapper
(
*
args
,
**
kwds
):
def
wrapper
(
*
args
,
**
kwds
):
w
=
CoroWrapper
(
coro
(
*
args
,
**
kwds
),
func
)
w
=
CoroWrapper
(
coro
(
*
args
,
**
kwds
),
func
=
func
)
if
w
.
_source_traceback
:
if
w
.
_source_traceback
:
del
w
.
_source_traceback
[
-
1
]
del
w
.
_source_traceback
[
-
1
]
if
hasattr
(
func
,
'__name__'
):
# Python < 3.5 does not implement __qualname__
w
.
__name__
=
func
.
__name__
# on generator objects, so we set it manually.
if
hasattr
(
func
,
'__qualname__'
):
# We use getattr as some callables (such as
w
.
__qualname__
=
func
.
__qualname__
# functools.partial may lack __qualname__).
w
.
__doc__
=
func
.
__doc__
w
.
__name__
=
getattr
(
func
,
'__name__'
,
None
)
w
.
__qualname__
=
getattr
(
func
,
'__qualname__'
,
None
)
return
w
return
w
wrapper
.
_is_coroutine
=
True
# For iscoroutinefunction().
wrapper
.
_is_coroutine
=
True
# For iscoroutinefunction().
...
@@ -164,7 +214,8 @@ def coroutine(func):
...
@@ -164,7 +214,8 @@ def coroutine(func):
def
iscoroutinefunction
(
func
):
def
iscoroutinefunction
(
func
):
"""Return True if func is a decorated coroutine function."""
"""Return True if func is a decorated coroutine function."""
return
getattr
(
func
,
'_is_coroutine'
,
False
)
return
(
getattr
(
func
,
'_is_coroutine'
,
False
)
or
_iscoroutinefunction
(
func
))
_COROUTINE_TYPES
=
(
types
.
GeneratorType
,
CoroWrapper
)
_COROUTINE_TYPES
=
(
types
.
GeneratorType
,
CoroWrapper
)
...
...
Lib/asyncio/futures.py
View file @
1af2bf75
...
@@ -19,6 +19,7 @@ _CANCELLED = 'CANCELLED'
...
@@ -19,6 +19,7 @@ _CANCELLED = 'CANCELLED'
_FINISHED
=
'FINISHED'
_FINISHED
=
'FINISHED'
_PY34
=
sys
.
version_info
>=
(
3
,
4
)
_PY34
=
sys
.
version_info
>=
(
3
,
4
)
_PY35
=
sys
.
version_info
>=
(
3
,
5
)
Error
=
concurrent
.
futures
.
_base
.
Error
Error
=
concurrent
.
futures
.
_base
.
Error
CancelledError
=
concurrent
.
futures
.
CancelledError
CancelledError
=
concurrent
.
futures
.
CancelledError
...
@@ -387,6 +388,9 @@ class Future:
...
@@ -387,6 +388,9 @@ class Future:
assert
self
.
done
(),
"yield from wasn't used with future"
assert
self
.
done
(),
"yield from wasn't used with future"
return
self
.
result
()
# May raise too.
return
self
.
result
()
# May raise too.
if
_PY35
:
__await__
=
__iter__
# make compatible with 'await' expression
def
wrap_future
(
fut
,
*
,
loop
=
None
):
def
wrap_future
(
fut
,
*
,
loop
=
None
):
"""Wrap concurrent.futures.Future object."""
"""Wrap concurrent.futures.Future object."""
...
...
Lib/asyncio/tasks.py
View file @
1af2bf75
...
@@ -11,6 +11,7 @@ import functools
...
@@ -11,6 +11,7 @@ import functools
import
inspect
import
inspect
import
linecache
import
linecache
import
sys
import
sys
import
types
import
traceback
import
traceback
import
warnings
import
warnings
import
weakref
import
weakref
...
@@ -73,7 +74,10 @@ class Task(futures.Future):
...
@@ -73,7 +74,10 @@ class Task(futures.Future):
super
().
__init__
(
loop
=
loop
)
super
().
__init__
(
loop
=
loop
)
if
self
.
_source_traceback
:
if
self
.
_source_traceback
:
del
self
.
_source_traceback
[
-
1
]
del
self
.
_source_traceback
[
-
1
]
self
.
_coro
=
iter
(
coro
)
# Use the iterator just in case.
if
coro
.
__class__
is
types
.
GeneratorType
:
self
.
_coro
=
coro
else
:
self
.
_coro
=
iter
(
coro
)
# Use the iterator just in case.
self
.
_fut_waiter
=
None
self
.
_fut_waiter
=
None
self
.
_must_cancel
=
False
self
.
_must_cancel
=
False
self
.
_loop
.
call_soon
(
self
.
_step
)
self
.
_loop
.
call_soon
(
self
.
_step
)
...
@@ -236,7 +240,7 @@ class Task(futures.Future):
...
@@ -236,7 +240,7 @@ class Task(futures.Future):
elif
value
is
not
None
:
elif
value
is
not
None
:
result
=
coro
.
send
(
value
)
result
=
coro
.
send
(
value
)
else
:
else
:
result
=
next
(
coro
)
result
=
coro
.
send
(
None
)
except
StopIteration
as
exc
:
except
StopIteration
as
exc
:
self
.
set_result
(
exc
.
value
)
self
.
set_result
(
exc
.
value
)
except
futures
.
CancelledError
as
exc
:
except
futures
.
CancelledError
as
exc
:
...
...
Lib/test/test_asyncio/test_base_events.py
View file @
1af2bf75
...
@@ -61,7 +61,8 @@ class BaseEventLoopTests(test_utils.TestCase):
...
@@ -61,7 +61,8 @@ class BaseEventLoopTests(test_utils.TestCase):
NotImplementedError
,
NotImplementedError
,
self
.
loop
.
_make_write_pipe_transport
,
m
,
m
)
self
.
loop
.
_make_write_pipe_transport
,
m
,
m
)
gen
=
self
.
loop
.
_make_subprocess_transport
(
m
,
m
,
m
,
m
,
m
,
m
,
m
)
gen
=
self
.
loop
.
_make_subprocess_transport
(
m
,
m
,
m
,
m
,
m
,
m
,
m
)
self
.
assertRaises
(
NotImplementedError
,
next
,
iter
(
gen
))
with
self
.
assertRaises
(
NotImplementedError
):
gen
.
send
(
None
)
def
test_close
(
self
):
def
test_close
(
self
):
self
.
assertFalse
(
self
.
loop
.
is_closed
())
self
.
assertFalse
(
self
.
loop
.
is_closed
())
...
...
Lib/test/test_asyncio/test_tasks.py
View file @
1af2bf75
...
@@ -1638,7 +1638,7 @@ class TaskTests(test_utils.TestCase):
...
@@ -1638,7 +1638,7 @@ class TaskTests(test_utils.TestCase):
return
a
return
a
def
call
(
arg
):
def
call
(
arg
):
cw
=
asyncio
.
coroutines
.
CoroWrapper
(
foo
()
,
foo
)
cw
=
asyncio
.
coroutines
.
CoroWrapper
(
foo
())
cw
.
send
(
None
)
cw
.
send
(
None
)
try
:
try
:
cw
.
send
(
arg
)
cw
.
send
(
arg
)
...
@@ -1653,7 +1653,7 @@ class TaskTests(test_utils.TestCase):
...
@@ -1653,7 +1653,7 @@ class TaskTests(test_utils.TestCase):
def
test_corowrapper_weakref
(
self
):
def
test_corowrapper_weakref
(
self
):
wd
=
weakref
.
WeakValueDictionary
()
wd
=
weakref
.
WeakValueDictionary
()
def
foo
():
yield
from
[]
def
foo
():
yield
from
[]
cw
=
asyncio
.
coroutines
.
CoroWrapper
(
foo
()
,
foo
)
cw
=
asyncio
.
coroutines
.
CoroWrapper
(
foo
())
wd
[
'cw'
]
=
cw
# Would fail without __weakref__ slot.
wd
[
'cw'
]
=
cw
# Would fail without __weakref__ slot.
cw
.
gen
=
None
# Suppress warning from __del__.
cw
.
gen
=
None
# Suppress warning from __del__.
...
...
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