summaryrefslogtreecommitdiffstats
path: root/Lib/asyncio
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-06-25 19:41:58 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2014-06-25 19:41:58 (GMT)
commit975735f729df6b7767556d2d389b560dbc7500ac (patch)
tree8579740ca947c8323088a452d659e4c0cb130a2e /Lib/asyncio
parent65c623de74cba3eff6d8f5f4c37cfec89c0fde43 (diff)
downloadcpython-975735f729df6b7767556d2d389b560dbc7500ac.zip
cpython-975735f729df6b7767556d2d389b560dbc7500ac.tar.gz
cpython-975735f729df6b7767556d2d389b560dbc7500ac.tar.bz2
asyncio, Tulip issue 177: Rewite repr() of Future, Task, Handle and TimerHandle
- Uniformize repr() output to format "<Class ...>" - On Python 3.5+, repr(Task) uses the qualified name instead of the short name of the coroutine
Diffstat (limited to 'Lib/asyncio')
-rw-r--r--Lib/asyncio/events.py56
-rw-r--r--Lib/asyncio/futures.py48
-rw-r--r--Lib/asyncio/tasks.py51
3 files changed, 102 insertions, 53 deletions
diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py
index de161df..4054482 100644
--- a/Lib/asyncio/events.py
+++ b/Lib/asyncio/events.py
@@ -18,6 +18,7 @@ import sys
_PY34 = sys.version_info >= (3, 4)
+
def _get_function_source(func):
if _PY34:
func = inspect.unwrap(func)
@@ -33,6 +34,35 @@ def _get_function_source(func):
return None
+def _format_args(args):
+ # function formatting ('hello',) as ('hello')
+ args_repr = repr(args)
+ if len(args) == 1 and args_repr.endswith(',)'):
+ args_repr = args_repr[:-2] + ')'
+ return args_repr
+
+
+def _format_callback(func, args, suffix=''):
+ if isinstance(func, functools.partial):
+ if args is not None:
+ suffix = _format_args(args) + suffix
+ return _format_callback(func.func, func.args, suffix)
+
+ func_repr = getattr(func, '__qualname__', None)
+ if not func_repr:
+ func_repr = repr(func)
+
+ if args is not None:
+ func_repr += _format_args(args)
+ if suffix:
+ func_repr += suffix
+
+ source = _get_function_source(func)
+ if source:
+ func_repr += ' at %s:%s' % source
+ return func_repr
+
+
class Handle:
"""Object returned by callback registration methods."""
@@ -46,18 +76,11 @@ class Handle:
self._cancelled = False
def __repr__(self):
- cb_repr = getattr(self._callback, '__qualname__', None)
- if not cb_repr:
- cb_repr = str(self._callback)
-
- source = _get_function_source(self._callback)
- if source:
- cb_repr += ' at %s:%s' % source
-
- res = 'Handle({}, {})'.format(cb_repr, self._args)
+ info = []
if self._cancelled:
- res += '<cancelled>'
- return res
+ info.append('cancelled')
+ info.append(_format_callback(self._callback, self._args))
+ return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
def cancel(self):
self._cancelled = True
@@ -88,13 +111,12 @@ class TimerHandle(Handle):
self._when = when
def __repr__(self):
- res = 'TimerHandle({}, {}, {})'.format(self._when,
- self._callback,
- self._args)
+ info = []
if self._cancelled:
- res += '<cancelled>'
-
- return res
+ info.append('cancelled')
+ info.append('when=%s' % self._when)
+ info.append(_format_callback(self._callback, self._args))
+ return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
def __hash__(self):
return hash(self._when)
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 4edd2e5..3103fe1 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -150,24 +150,40 @@ class Future:
self._loop = loop
self._callbacks = []
+ def _format_callbacks(self):
+ cb = self._callbacks
+ size = len(cb)
+ if not size:
+ cb = ''
+
+ def format_cb(callback):
+ return events._format_callback(callback, ())
+
+ if size == 1:
+ cb = format_cb(cb[0])
+ elif size == 2:
+ cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
+ elif size > 2:
+ cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
+ size-2,
+ 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):
- res = self.__class__.__name__
+ info = [self._state.lower()]
if self._state == _FINISHED:
- if self._exception is not None:
- res += '<exception={!r}>'.format(self._exception)
- else:
- res += '<result={!r}>'.format(self._result)
- elif self._callbacks:
- size = len(self._callbacks)
- if size > 2:
- res += '<{}, [{}, <{} more>, {}]>'.format(
- self._state, self._callbacks[0],
- size-2, self._callbacks[-1])
- else:
- res += '<{}, {}>'.format(self._state, self._callbacks)
- else:
- res += '<{}>'.format(self._state)
- return res
+ info.append(self._format_result())
+ if self._callbacks:
+ info.append(self._format_callbacks())
+ return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
# 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
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index 3b41a21..52ca33a 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -132,6 +132,22 @@ def iscoroutine(obj):
return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj)
+def _format_coroutine(coro):
+ assert iscoroutine(coro)
+ if _PY35:
+ coro_name = coro.__qualname__
+ else:
+ coro_name = coro.__name__
+
+ filename = coro.gi_code.co_filename
+ if coro.gi_frame is not None:
+ lineno = coro.gi_frame.f_lineno
+ return '%s() at %s:%s' % (coro_name, filename, lineno)
+ else:
+ lineno = coro.gi_code.co_firstlineno
+ return '%s() done at %s:%s' % (coro_name, filename, lineno)
+
+
class Task(futures.Future):
"""A coroutine wrapped in a Future."""
@@ -195,26 +211,21 @@ class Task(futures.Future):
futures.Future.__del__(self)
def __repr__(self):
- res = super().__repr__()
- if (self._must_cancel and
- self._state == futures._PENDING and
- '<PENDING' in res):
- res = res.replace('<PENDING', '<CANCELLING', 1)
- i = res.find('<')
- if i < 0:
- i = len(res)
- text = self._coro.__name__
- coro = self._coro
- if iscoroutine(coro):
- filename = coro.gi_code.co_filename
- if coro.gi_frame is not None:
- lineno = coro.gi_frame.f_lineno
- text += ' at %s:%s' % (filename, lineno)
- else:
- lineno = coro.gi_code.co_firstlineno
- text += ' done at %s:%s' % (filename, lineno)
- res = res[:i] + '(<{}>)'.format(text) + res[i:]
- return res
+ info = []
+ if self._must_cancel:
+ info.append('cancelling')
+ else:
+ info.append(self._state.lower())
+
+ info.append(_format_coroutine(self._coro))
+
+ 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))
def get_stack(self, *, limit=None):
"""Return the list of stack frames for this task's coroutine.