summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-07-29 12:49:33 +0200
committerVictor Stinner <victor.stinner@gmail.com>2014-07-29 12:49:33 +0200
commit9d8976897d1a550d3296809c08d4c6f6f04348c7 (patch)
treeb9b9c5feb7df7ca5a7437dba7cf5cb6f9185e678
parent606eaa3e036b348df9e07af0d4830cec2a6dc5cd (diff)
downloadtrollius-9d8976897d1a550d3296809c08d4c6f6f04348c7.tar.gz
Enhance representation of Future and Future subclasses
* Add "created at filename:lineno" in the representation * Add Future._repr_info() method which can be more easily overriden than Future.__repr__(). It should now be more easy to enhance Future representation without having to modify each subclass. For example, _OverlappedFuture and _WaitHandleFuture get the new "created at" information. * Use reprlib to format Future result, and function arguments when formatting a callback, to limit the length of the representation.
-rw-r--r--asyncio/events.py15
-rw-r--r--asyncio/futures.py26
-rw-r--r--asyncio/tasks.py27
-rw-r--r--asyncio/windows_events.py27
-rw-r--r--tests/test_futures.py11
-rw-r--r--tests/test_tasks.py6
6 files changed, 60 insertions, 52 deletions
diff --git a/asyncio/events.py b/asyncio/events.py
index bddd7e3..3c7a36d 100644
--- a/asyncio/events.py
+++ b/asyncio/events.py
@@ -10,11 +10,12 @@ __all__ = ['AbstractEventLoopPolicy',
import functools
import inspect
-import subprocess
-import traceback
-import threading
+import reprlib
import socket
+import subprocess
import sys
+import threading
+import traceback
_PY34 = sys.version_info >= (3, 4)
@@ -36,8 +37,12 @@ def _get_function_source(func):
def _format_args(args):
- # function formatting ('hello',) as ('hello')
- args_repr = repr(args)
+ """Format function arguments.
+
+ Special case for a single parameter: ('hello',) is formatted as ('hello').
+ """
+ # use reprlib to limit the length of the output
+ args_repr = reprlib.repr(args)
if len(args) == 1 and args_repr.endswith(',)'):
args_repr = args_repr[:-2] + ')'
return args_repr
diff --git a/asyncio/futures.py b/asyncio/futures.py
index 022fef7..7998fbb 100644
--- a/asyncio/futures.py
+++ b/asyncio/futures.py
@@ -7,6 +7,7 @@ __all__ = ['CancelledError', 'TimeoutError',
import concurrent.futures._base
import logging
+import reprlib
import sys
import traceback
@@ -175,20 +176,25 @@ class Future:
format_cb(cb[-1]))
return 'cb=[%s]' % cb
- def _format_result(self):
- if self._state != _FINISHED:
- return None
- elif self._exception is not None:
- return 'exception={!r}'.format(self._exception)
- else:
- return 'result={!r}'.format(self._result)
-
- def __repr__(self):
+ def _repr_info(self):
info = [self._state.lower()]
if self._state == _FINISHED:
- info.append(self._format_result())
+ if self._exception is not None:
+ info.append('exception={!r}'.format(self._exception))
+ else:
+ # use reprlib to limit the length of the output, especially
+ # for very long strings
+ result = reprlib.repr(self._result)
+ info.append('result={}'.format(result))
if self._callbacks:
info.append(self._format_callbacks())
+ if self._source_traceback:
+ frame = self._source_traceback[-1]
+ info.append('created at %s:%s' % (frame[0], frame[1]))
+ return info
+
+ def __repr__(self):
+ info = self._repr_info()
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
# On Python 3.3 or older, objects with a destructor part of a reference
diff --git a/asyncio/tasks.py b/asyncio/tasks.py
index 07952c9..9207016 100644
--- a/asyncio/tasks.py
+++ b/asyncio/tasks.py
@@ -92,30 +92,19 @@ class Task(futures.Future):
self._loop.call_exception_handler(context)
futures.Future.__del__(self)
- def __repr__(self):
- info = []
+ def _repr_info(self):
+ info = super()._repr_info()
+
if self._must_cancel:
- info.append('cancelling')
- else:
- info.append(self._state.lower())
+ # replace status
+ info[0] = 'cancelling'
coro = coroutines._format_coroutine(self._coro)
- info.append('coro=<%s>' % coro)
-
- if self._source_traceback:
- frame = self._source_traceback[-1]
- info.append('created at %s:%s' % (frame[0], frame[1]))
-
- if self._state == futures._FINISHED:
- info.append(self._format_result())
-
- if self._callbacks:
- info.append(self._format_callbacks())
+ info.insert(1, 'coro=<%s>' % coro)
if self._fut_waiter is not None:
- info.append('wait_for=%r' % self._fut_waiter)
-
- return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
+ info.insert(2, 'wait_for=%r' % self._fut_waiter)
+ return info
def get_stack(self, *, limit=None):
"""Return the list of stack frames for this task's coroutine.
diff --git a/asyncio/windows_events.py b/asyncio/windows_events.py
index 6d9feab..1db255e 100644
--- a/asyncio/windows_events.py
+++ b/asyncio/windows_events.py
@@ -42,16 +42,12 @@ class _OverlappedFuture(futures.Future):
del self._source_traceback[-1]
self._ov = ov
- def __repr__(self):
- info = [self._state.lower()]
+ def _repr_info(self):
+ info = super()._repr_info()
if self._ov is not None:
state = 'pending' if self._ov.pending else 'completed'
- info.append('overlapped=<%s, %#x>' % (state, self._ov.address))
- if self._state == futures._FINISHED:
- info.append(self._format_result())
- if self._callbacks:
- info.append(self._format_callbacks())
- return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
+ info.insert(1, 'overlapped=<%s, %#x>' % (state, self._ov.address))
+ return info
def _cancel_overlapped(self):
if self._ov is None:
@@ -95,17 +91,14 @@ class _WaitHandleFuture(futures.Future):
return (_winapi.WaitForSingleObject(self._handle, 0) ==
_winapi.WAIT_OBJECT_0)
- def __repr__(self):
- info = [self._state.lower()]
+ def _repr_info(self):
+ info = super()._repr_info()
+ info.insert(1, 'handle=%#x' % self._handle)
if self._wait_handle:
state = 'pending' if self._poll() else 'completed'
- info.append('wait_handle=<%s, %#x>' % (state, self._wait_handle))
- info.append('handle=<%#x>' % self._handle)
- if self._state == futures._FINISHED:
- info.append(self._format_result())
- if self._callbacks:
- info.append(self._format_callbacks())
- return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
+ info.insert(1, 'wait_handle=<%s, %#x>'
+ % (state, self._wait_handle))
+ return info
def _unregister(self):
if self._wait_handle is None:
diff --git a/tests/test_futures.py b/tests/test_futures.py
index 50e9414..3029a9c 100644
--- a/tests/test_futures.py
+++ b/tests/test_futures.py
@@ -105,6 +105,15 @@ class FutureTests(test_utils.TestCase):
self.assertEqual(next(g), ('C', 42)) # yield 'C', y.
def test_future_repr(self):
+ self.loop.set_debug(True)
+ f_pending_debug = asyncio.Future(loop=self.loop)
+ frame = f_pending_debug._source_traceback[-1]
+ self.assertEqual(repr(f_pending_debug),
+ '<Future pending created at %s:%s>'
+ % (frame[0], frame[1]))
+ f_pending_debug.cancel()
+
+ self.loop.set_debug(False)
f_pending = asyncio.Future(loop=self.loop)
self.assertEqual(repr(f_pending), '<Future pending>')
f_pending.cancel()
@@ -324,7 +333,7 @@ class FutureTests(test_utils.TestCase):
if sys.version_info >= (3, 4):
frame = source_traceback[-1]
regex = (r'^Future exception was never retrieved\n'
- r'future: <Future finished exception=MemoryError\(\)>\n'
+ r'future: <Future finished exception=MemoryError\(\) created at {filename}:{lineno}>\n'
r'source_traceback: Object created at \(most recent call last\):\n'
r' File'
r'.*\n'
diff --git a/tests/test_tasks.py b/tests/test_tasks.py
index 7b93a0e..95cba54 100644
--- a/tests/test_tasks.py
+++ b/tests/test_tasks.py
@@ -132,6 +132,8 @@ class TaskTests(test_utils.TestCase):
asyncio.async('ok')
def test_task_repr(self):
+ self.loop.set_debug(False)
+
@asyncio.coroutine
def notmuch():
yield from []
@@ -189,6 +191,8 @@ class TaskTests(test_utils.TestCase):
"<Task finished %s result='abc'>" % coro)
def test_task_repr_coro_decorator(self):
+ self.loop.set_debug(False)
+
@asyncio.coroutine
def notmuch():
# notmuch() function doesn't use yield from: it will be wrapped by
@@ -252,6 +256,8 @@ class TaskTests(test_utils.TestCase):
self.loop.run_until_complete(t)
def test_task_repr_wait_for(self):
+ self.loop.set_debug(False)
+
@asyncio.coroutine
def wait_for(fut):
return (yield from fut)