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
a02f81ff
Commit
a02f81ff
authored
Jun 24, 2014
by
Victor Stinner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
asyncio: Log an error if a Task is destroyed while it is still pending
parent
4c945fe9
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
60 additions
and
4 deletions
+60
-4
Lib/asyncio/futures.py
Lib/asyncio/futures.py
+3
-0
Lib/asyncio/tasks.py
Lib/asyncio/tasks.py
+13
-0
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
+42
-3
No files found.
Lib/asyncio/futures.py
View file @
a02f81ff
...
...
@@ -169,6 +169,9 @@ class Future:
res
+=
'<{}>'
.
format
(
self
.
_state
)
return
res
# On Python 3.3 or older, objects with a destructor part of a reference
# cycle are never destroyed. It's not more the case on Python 3.4 thanks to
# the PEP 442.
if
_PY34
:
def
__del__
(
self
):
if
not
self
.
_log_traceback
:
...
...
Lib/asyncio/tasks.py
View file @
a02f81ff
...
...
@@ -32,6 +32,7 @@ from .log import logger
_DEBUG
=
(
not
sys
.
flags
.
ignore_environment
and
bool
(
os
.
environ
.
get
(
'PYTHONASYNCIODEBUG'
)))
_PY34
=
(
sys
.
version_info
>=
(
3
,
4
))
_PY35
=
(
sys
.
version_info
>=
(
3
,
5
))
...
...
@@ -181,6 +182,18 @@ class Task(futures.Future):
self
.
_loop
.
call_soon
(
self
.
_step
)
self
.
__class__
.
_all_tasks
.
add
(
self
)
# On Python 3.3 or older, objects with a destructor part of a reference
# cycle are never destroyed. It's not more the case on Python 3.4 thanks to
# the PEP 442.
if
_PY34
:
def
__del__
(
self
):
if
self
.
_state
==
futures
.
_PENDING
:
self
.
_loop
.
call_exception_handler
({
'task'
:
self
,
'message'
:
'Task was destroyed but it is pending!'
,
})
futures
.
Future
.
__del__
(
self
)
def
__repr__
(
self
):
res
=
super
().
__repr__
()
if
(
self
.
_must_cancel
and
...
...
Lib/test/test_asyncio/test_base_events.py
View file @
a02f81ff
...
...
@@ -244,7 +244,8 @@ class BaseEventLoopTests(test_utils.TestCase):
@
mock
.
patch
(
'asyncio.base_events.logger'
)
def
test__run_once_logging
(
self
,
m_logger
):
def
slow_select
(
timeout
):
time
.
sleep
(
1.0
)
# Sleep a bit longer than a second to avoid timer resolution issues.
time
.
sleep
(
1.1
)
return
[]
# logging needs debug flag
...
...
Lib/test/test_asyncio/test_tasks.py
View file @
a02f81ff
...
...
@@ -5,13 +5,16 @@ import sys
import
types
import
unittest
import
weakref
from
test
import
support
from
test.script_helper
import
assert_python_ok
from
unittest
import
mock
import
asyncio
from
asyncio
import
tasks
from
asyncio
import
test_utils
PY34
=
(
sys
.
version_info
>=
(
3
,
4
))
PY35
=
(
sys
.
version_info
>=
(
3
,
5
))
...
...
@@ -1501,9 +1504,45 @@ class TaskTests(test_utils.TestCase):
def
test_corowrapper_weakref
(
self
):
wd
=
weakref
.
WeakValueDictionary
()
def
foo
():
yield
from
[]
cw
=
asyncio
.
tasks
.
CoroWrapper
(
foo
(),
foo
)
wd
[
'cw'
]
=
cw
# Would fail without __weakref__ slot.
cw
.
gen
=
None
# Suppress warning from __del__.
@
unittest
.
skipUnless
(
PY34
,
'need python 3.4 or later'
)
def
test_log_destroyed_pending_task
(
self
):
@
asyncio
.
coroutine
def
kill_me
(
loop
):
future
=
asyncio
.
Future
(
loop
=
loop
)
yield
from
future
# at this point, the only reference to kill_me() task is
# the Task._wakeup() method in future._callbacks
raise
Exception
(
"code never reached"
)
mock_handler
=
mock
.
Mock
()
self
.
loop
.
set_exception_handler
(
mock_handler
)
# schedule the task
coro
=
kill_me
(
self
.
loop
)
task
=
asyncio
.
async
(
coro
,
loop
=
self
.
loop
)
self
.
assertEqual
(
asyncio
.
Task
.
all_tasks
(
loop
=
self
.
loop
),
{
task
})
# execute the task so it waits for future
self
.
loop
.
_run_once
()
self
.
assertEqual
(
len
(
self
.
loop
.
_ready
),
0
)
# remove the future used in kill_me(), and references to the task
del
coro
.
gi_frame
.
f_locals
[
'future'
]
coro
=
None
task
=
None
# no more reference to kill_me() task: the task is destroyed by the GC
support
.
gc_collect
()
self
.
assertEqual
(
asyncio
.
Task
.
all_tasks
(
loop
=
self
.
loop
),
set
())
mock_handler
.
assert_called_with
(
self
.
loop
,
{
'message'
:
'Task was destroyed but it is pending!'
,
'task'
:
mock
.
ANY
,
})
mock_handler
.
reset_mock
()
class
GatherTestsBase
:
...
...
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