diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-07-29 12:49:33 +0200 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-07-29 12:49:33 +0200 |
commit | 9d8976897d1a550d3296809c08d4c6f6f04348c7 (patch) | |
tree | b9b9c5feb7df7ca5a7437dba7cf5cb6f9185e678 | |
parent | 606eaa3e036b348df9e07af0d4830cec2a6dc5cd (diff) | |
download | trollius-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.py | 15 | ||||
-rw-r--r-- | asyncio/futures.py | 26 | ||||
-rw-r--r-- | asyncio/tasks.py | 27 | ||||
-rw-r--r-- | asyncio/windows_events.py | 27 | ||||
-rw-r--r-- | tests/test_futures.py | 11 | ||||
-rw-r--r-- | tests/test_tasks.py | 6 |
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) |