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
82f9feaf
Commit
82f9feaf
authored
Nov 19, 2015
by
Guido van Rossum
Browse files
Options
Browse Files
Download
Plain Diff
Issue #25593: Change semantics of EventLoop.stop(). (Merge 3.4->3.5)
parents
557d1eb0
41f69f4c
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
87 additions
and
28 deletions
+87
-28
Doc/library/asyncio-eventloop.rst
Doc/library/asyncio-eventloop.rst
+16
-6
Doc/library/asyncio-protocol.rst
Doc/library/asyncio-protocol.rst
+1
-1
Lib/asyncio/base_events.py
Lib/asyncio/base_events.py
+9
-16
Lib/asyncio/test_utils.py
Lib/asyncio/test_utils.py
+6
-5
Lib/test/test_asyncio/test_base_events.py
Lib/test/test_asyncio/test_base_events.py
+53
-0
Misc/NEWS
Misc/NEWS
+2
-0
No files found.
Doc/library/asyncio-eventloop.rst
View file @
82f9feaf
...
@@ -29,7 +29,16 @@ Run an event loop
...
@@ -29,7 +29,16 @@ Run an event loop
.. method:: BaseEventLoop.run_forever()
.. method:: BaseEventLoop.run_forever()
Run until :meth:`stop` is called.
Run until :meth:`stop` is called. If :meth:`stop` is called before
:meth:`run_forever()` is called, this polls the I/O selector once
with a timeout of zero, runs all callbacks scheduled in response to
I/O events (and those that were already scheduled), and then exits.
If :meth:`stop` is called while :meth:`run_forever` is running,
this will run the current batch of callbacks and then exit. Note
that callbacks scheduled by callbacks will not run in that case;
they will run the next time :meth:`run_forever` is called.
.. versionchanged:: 3.5.1
.. method:: BaseEventLoop.run_until_complete(future)
.. method:: BaseEventLoop.run_until_complete(future)
...
@@ -48,10 +57,10 @@ Run an event loop
...
@@ -48,10 +57,10 @@ Run an event loop
Stop running the event loop.
Stop running the event loop.
Every callback scheduled before :meth:`stop` is called will run.
This causes :meth:`run_forever` to exit at the next suitable
Callbacks scheduled after :meth:`stop` is called will not run
.
opportunity (see there for more details)
.
However, those callbacks will run if :meth:`run_forever` is called
again later.
.. versionchanged:: 3.5.1
.. method:: BaseEventLoop.is_closed()
.. method:: BaseEventLoop.is_closed()
...
@@ -61,7 +70,8 @@ Run an event loop
...
@@ -61,7 +70,8 @@ Run an event loop
.. method:: BaseEventLoop.close()
.. method:: BaseEventLoop.close()
Close the event loop. The loop must not be running.
Close the event loop. The loop must not be running. Pending
callbacks will be lost.
This clears the queues and shuts down the executor, but does not wait for
This clears the queues and shuts down the executor, but does not wait for
the executor to finish.
the executor to finish.
...
...
Doc/library/asyncio-protocol.rst
View file @
82f9feaf
...
@@ -494,7 +494,7 @@ data and wait until the connection is closed::
...
@@ -494,7 +494,7 @@ data and wait until the connection is closed::
def connection_lost(self, exc):
def connection_lost(self, exc):
print('The server closed the connection')
print('The server closed the connection')
print('Stop the event lop')
print('Stop the event lo
o
p')
self.loop.stop()
self.loop.stop()
loop = asyncio.get_event_loop()
loop = asyncio.get_event_loop()
...
...
Lib/asyncio/base_events.py
View file @
82f9feaf
...
@@ -70,10 +70,6 @@ def _format_pipe(fd):
...
@@ -70,10 +70,6 @@ def _format_pipe(fd):
return
repr
(
fd
)
return
repr
(
fd
)
class
_StopError
(
BaseException
):
"""Raised to stop the event loop."""
def
_check_resolved_address
(
sock
,
address
):
def
_check_resolved_address
(
sock
,
address
):
# Ensure that the address is already resolved to avoid the trap of hanging
# Ensure that the address is already resolved to avoid the trap of hanging
# the entire event loop when the address requires doing a DNS lookup.
# the entire event loop when the address requires doing a DNS lookup.
...
@@ -118,9 +114,6 @@ def _check_resolved_address(sock, address):
...
@@ -118,9 +114,6 @@ def _check_resolved_address(sock, address):
"got host %r: %s"
"got host %r: %s"
%
(
host
,
err
))
%
(
host
,
err
))
def
_raise_stop_error
(
*
args
):
raise
_StopError
def
_run_until_complete_cb
(
fut
):
def
_run_until_complete_cb
(
fut
):
exc
=
fut
.
_exception
exc
=
fut
.
_exception
...
@@ -129,7 +122,7 @@ def _run_until_complete_cb(fut):
...
@@ -129,7 +122,7 @@ def _run_until_complete_cb(fut):
# Issue #22429: run_forever() already finished, no need to
# Issue #22429: run_forever() already finished, no need to
# stop it.
# stop it.
return
return
_raise_stop_error
()
fut
.
_loop
.
stop
()
class
Server
(
events
.
AbstractServer
):
class
Server
(
events
.
AbstractServer
):
...
@@ -184,6 +177,7 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -184,6 +177,7 @@ class BaseEventLoop(events.AbstractEventLoop):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
_timer_cancelled_count
=
0
self
.
_timer_cancelled_count
=
0
self
.
_closed
=
False
self
.
_closed
=
False
self
.
_stopping
=
False
self
.
_ready
=
collections
.
deque
()
self
.
_ready
=
collections
.
deque
()
self
.
_scheduled
=
[]
self
.
_scheduled
=
[]
self
.
_default_executor
=
None
self
.
_default_executor
=
None
...
@@ -298,11 +292,11 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -298,11 +292,11 @@ class BaseEventLoop(events.AbstractEventLoop):
self
.
_thread_id
=
threading
.
get_ident
()
self
.
_thread_id
=
threading
.
get_ident
()
try
:
try
:
while
True
:
while
True
:
try
:
self
.
_run_once
()
self
.
_run_once
()
except
_StopError
:
if
self
.
_stopping
:
break
break
finally
:
finally
:
self
.
_stopping
=
False
self
.
_thread_id
=
None
self
.
_thread_id
=
None
self
.
_set_coroutine_wrapper
(
False
)
self
.
_set_coroutine_wrapper
(
False
)
...
@@ -345,11 +339,10 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -345,11 +339,10 @@ class BaseEventLoop(events.AbstractEventLoop):
def
stop
(
self
):
def
stop
(
self
):
"""Stop running the event loop.
"""Stop running the event loop.
Every callback scheduled before stop() is called will run. Callbacks
Every callback already scheduled will still run. This simply informs
scheduled after stop() is called will not run. However, those callbacks
run_forever to stop looping after a complete iteration.
will run if run_forever is called again later.
"""
"""
self
.
call_soon
(
_raise_stop_error
)
self
.
_stopping
=
True
def
close
(
self
):
def
close
(
self
):
"""Close the event loop.
"""Close the event loop.
...
@@ -1194,7 +1187,7 @@ class BaseEventLoop(events.AbstractEventLoop):
...
@@ -1194,7 +1187,7 @@ class BaseEventLoop(events.AbstractEventLoop):
handle
.
_scheduled
=
False
handle
.
_scheduled
=
False
timeout
=
None
timeout
=
None
if
self
.
_ready
:
if
self
.
_ready
or
self
.
_stopping
:
timeout
=
0
timeout
=
0
elif
self
.
_scheduled
:
elif
self
.
_scheduled
:
# Compute the desired timeout.
# Compute the desired timeout.
...
...
Lib/asyncio/test_utils.py
View file @
82f9feaf
...
@@ -71,12 +71,13 @@ def run_until(loop, pred, timeout=30):
...
@@ -71,12 +71,13 @@ def run_until(loop, pred, timeout=30):
def
run_once
(
loop
):
def
run_once
(
loop
):
"""loop.stop() schedules _raise_stop_error()
"""Legacy API to run once through the event loop.
and run_forever() runs until _raise_stop_error() callback.
this wont work if test waits for some IO events, because
This is the recommended pattern for test code. It will poll the
_raise_stop_error() runs before any of io events callbacks.
selector once and run all callbacks scheduled in response to I/O
events.
"""
"""
loop
.
stop
(
)
loop
.
call_soon
(
loop
.
stop
)
loop
.
run_forever
()
loop
.
run_forever
()
...
...
Lib/test/test_asyncio/test_base_events.py
View file @
82f9feaf
...
@@ -757,6 +757,59 @@ class BaseEventLoopTests(test_utils.TestCase):
...
@@ -757,6 +757,59 @@ class BaseEventLoopTests(test_utils.TestCase):
pass
pass
self
.
assertTrue
(
func
.
called
)
self
.
assertTrue
(
func
.
called
)
def
test_single_selecter_event_callback_after_stopping
(
self
):
# Python issue #25593: A stopped event loop may cause event callbacks
# to run more than once.
event_sentinel
=
object
()
callcount
=
0
doer
=
None
def
proc_events
(
event_list
):
nonlocal
doer
if
event_sentinel
in
event_list
:
doer
=
self
.
loop
.
call_soon
(
do_event
)
def
do_event
():
nonlocal
callcount
callcount
+=
1
self
.
loop
.
call_soon
(
clear_selector
)
def
clear_selector
():
doer
.
cancel
()
self
.
loop
.
_selector
.
select
.
return_value
=
()
self
.
loop
.
_process_events
=
proc_events
self
.
loop
.
_selector
.
select
.
return_value
=
(
event_sentinel
,)
for
i
in
range
(
1
,
3
):
with
self
.
subTest
(
'Loop %d/2'
%
i
):
self
.
loop
.
call_soon
(
self
.
loop
.
stop
)
self
.
loop
.
run_forever
()
self
.
assertEqual
(
callcount
,
1
)
def
test_run_once
(
self
):
# Simple test for test_utils.run_once(). It may seem strange
# to have a test for this (the function isn't even used!) but
# it's a de-factor standard API for library tests. This tests
# the idiom: loop.call_soon(loop.stop); loop.run_forever().
count
=
0
def
callback
():
nonlocal
count
count
+=
1
self
.
loop
.
_process_events
=
mock
.
Mock
()
self
.
loop
.
call_soon
(
callback
)
test_utils
.
run_once
(
self
.
loop
)
self
.
assertEqual
(
count
,
1
)
def
test_run_forever_pre_stopped
(
self
):
# Test that the old idiom for pre-stopping the loop works.
self
.
loop
.
_process_events
=
mock
.
Mock
()
self
.
loop
.
stop
()
self
.
loop
.
run_forever
()
self
.
loop
.
_selector
.
select
.
assert_called_once_with
(
0
)
class
MyProto
(
asyncio
.
Protocol
):
class
MyProto
(
asyncio
.
Protocol
):
done
=
None
done
=
None
...
...
Misc/NEWS
View file @
82f9feaf
...
@@ -77,6 +77,8 @@ Core and Builtins
...
@@ -77,6 +77,8 @@ Core and Builtins
Library
Library
-------
-------
-
Issue
#
25593
:
Change
semantics
of
EventLoop
.
stop
()
in
asyncio
.
-
Issue
#
6973
:
When
we
know
a
subprocess
.
Popen
process
has
died
,
do
-
Issue
#
6973
:
When
we
know
a
subprocess
.
Popen
process
has
died
,
do
not
allow
the
send_signal
(),
terminate
(),
or
kill
()
methods
to
do
not
allow
the
send_signal
(),
terminate
(),
or
kill
()
methods
to
do
anything
as
they
could
potentially
signal
a
different
process
.
anything
as
they
could
potentially
signal
a
different
process
.
...
...
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