diff options
-rw-r--r-- | Lib/asyncio/coroutines.py | 18 | ||||
-rw-r--r-- | Lib/asyncio/events.py | 14 | ||||
-rw-r--r-- | Lib/asyncio/futures.py | 2 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_tasks.py | 65 | ||||
-rw-r--r-- | Misc/NEWS | 27 |
5 files changed, 94 insertions, 32 deletions
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index a1b2875..c639461 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -151,7 +151,8 @@ def coroutine(func): w = CoroWrapper(coro(*args, **kwds), func) if w._source_traceback: del w._source_traceback[-1] - w.__name__ = func.__name__ + if hasattr(func, '__name__'): + w.__name__ = func.__name__ if hasattr(func, '__qualname__'): w.__qualname__ = func.__qualname__ w.__doc__ = func.__doc__ @@ -175,25 +176,30 @@ def iscoroutine(obj): def _format_coroutine(coro): assert iscoroutine(coro) - coro_name = getattr(coro, '__qualname__', coro.__name__) + + if isinstance(coro, CoroWrapper): + func = coro.func + else: + func = coro + coro_name = events._format_callback(func, ()) filename = coro.gi_code.co_filename if (isinstance(coro, CoroWrapper) and not inspect.isgeneratorfunction(coro.func)): filename, lineno = events._get_function_source(coro.func) if coro.gi_frame is None: - coro_repr = ('%s() done, defined at %s:%s' + coro_repr = ('%s done, defined at %s:%s' % (coro_name, filename, lineno)) else: - coro_repr = ('%s() running, defined at %s:%s' + coro_repr = ('%s running, defined at %s:%s' % (coro_name, filename, lineno)) elif coro.gi_frame is not None: lineno = coro.gi_frame.f_lineno - coro_repr = ('%s() running at %s:%s' + coro_repr = ('%s running at %s:%s' % (coro_name, filename, lineno)) else: lineno = coro.gi_code.co_firstlineno - coro_repr = ('%s() done, defined at %s:%s' + coro_repr = ('%s done, defined at %s:%s' % (coro_name, filename, lineno)) return coro_repr diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 8a7bb81..3b907c6 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -54,15 +54,21 @@ def _format_callback(func, args, suffix=''): suffix = _format_args(args) + suffix return _format_callback(func.func, func.args, suffix) - func_repr = getattr(func, '__qualname__', None) - if not func_repr: + if hasattr(func, '__qualname__'): + func_repr = getattr(func, '__qualname__') + elif hasattr(func, '__name__'): + func_repr = getattr(func, '__name__') + else: func_repr = repr(func) if args is not None: func_repr += _format_args(args) if suffix: func_repr += suffix + return func_repr +def _format_callback_source(func, args): + func_repr = _format_callback(func, args) source = _get_function_source(func) if source: func_repr += ' at %s:%s' % source @@ -92,7 +98,7 @@ class Handle: if self._cancelled: info.append('cancelled') if self._callback is not None: - info.append(_format_callback(self._callback, self._args)) + info.append(_format_callback_source(self._callback, self._args)) if self._source_traceback: frame = self._source_traceback[-1] info.append('created at %s:%s' % (frame[0], frame[1])) @@ -119,7 +125,7 @@ class Handle: try: self._callback(*self._args) except Exception as exc: - cb = _format_callback(self._callback, self._args) + cb = _format_callback_source(self._callback, self._args) msg = 'Exception in callback {}'.format(cb) context = { 'message': msg, diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 2c741fd..74a99ba 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -162,7 +162,7 @@ class Future: cb = '' def format_cb(callback): - return events._format_callback(callback, ()) + return events._format_callback_source(callback, ()) if size == 1: cb = format_cb(cb[0]) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 06447d7..ab61462 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -1,5 +1,7 @@ """Tests for tasks.py.""" +import contextlib +import functools import os import re import sys @@ -28,6 +30,19 @@ def coroutine_function(): pass +@contextlib.contextmanager +def set_coroutine_debug(enabled): + coroutines = asyncio.coroutines + + old_debug = coroutines._DEBUG + try: + coroutines._DEBUG = enabled + yield + finally: + coroutines._DEBUG = old_debug + + + def format_coroutine(qualname, state, src, source_traceback, generator=False): if generator: state = '%s' % state @@ -279,6 +294,29 @@ class TaskTests(test_utils.TestCase): fut.set_result(None) self.loop.run_until_complete(task) + def test_task_repr_partial_corowrapper(self): + # Issue #222: repr(CoroWrapper) must not fail in debug mode if the + # coroutine is a partial function + with set_coroutine_debug(True): + self.loop.set_debug(True) + + @asyncio.coroutine + def func(x, y): + yield from asyncio.sleep(0) + + partial_func = asyncio.coroutine(functools.partial(func, 1)) + task = self.loop.create_task(partial_func(2)) + + # make warnings quiet + task._log_destroy_pending = False + self.addCleanup(task._coro.close) + + coro_repr = repr(task._coro) + expected = ('<CoroWrapper TaskTests.test_task_repr_partial_corowrapper' + '.<locals>.func(1)() running, ') + self.assertTrue(coro_repr.startswith(expected), + coro_repr) + def test_task_basics(self): @asyncio.coroutine def outer(): @@ -1555,25 +1593,16 @@ class TaskTests(test_utils.TestCase): # The frame should have changed. self.assertIsNone(gen.gi_frame) - # Save debug flag. - old_debug = asyncio.coroutines._DEBUG - try: - # Test with debug flag cleared. - asyncio.coroutines._DEBUG = False + # Test with debug flag cleared. + with set_coroutine_debug(False): check() - # Test with debug flag set. - asyncio.coroutines._DEBUG = True + # Test with debug flag set. + with set_coroutine_debug(True): check() - finally: - # Restore original debug flag. - asyncio.coroutines._DEBUG = old_debug - def test_yield_from_corowrapper(self): - old_debug = asyncio.coroutines._DEBUG - asyncio.coroutines._DEBUG = True - try: + with set_coroutine_debug(True): @asyncio.coroutine def t1(): return (yield from t2()) @@ -1591,8 +1620,6 @@ class TaskTests(test_utils.TestCase): task = asyncio.Task(t1(), loop=self.loop) val = self.loop.run_until_complete(task) self.assertEqual(val, (1, 2, 3)) - finally: - asyncio.coroutines._DEBUG = old_debug def test_yield_from_corowrapper_send(self): def foo(): @@ -1663,14 +1690,10 @@ class TaskTests(test_utils.TestCase): @mock.patch('asyncio.coroutines.logger') def test_coroutine_never_yielded(self, m_log): - debug = asyncio.coroutines._DEBUG - try: - asyncio.coroutines._DEBUG = True + with set_coroutine_debug(True): @asyncio.coroutine def coro_noop(): pass - finally: - asyncio.coroutines._DEBUG = debug tb_filename = __file__ tb_lineno = sys._getframe().f_lineno + 2 @@ -101,6 +101,7 @@ Core and Builtins Library ------- +<<<<<<< local - Issue #16914: new debuglevel 2 in smtplib adds timestamps to debug output. - Issue #7159: urllib.request now supports sending auth credentials @@ -125,6 +126,32 @@ Library - Issue #21217: inspect.getsourcelines() now tries to compute the start and end lines from the code object, fixing an issue when a lambda function is used as decorator argument. Patch by Thomas Ballinger and Allison Kaptur. +======= +- Asyncio issue 222 / PR 231 (Victor Stinner) -- fix @coroutine + functions without __name__. + +- Issue #9246: On POSIX, os.getcwd() now supports paths longer than 1025 bytes. + Patch written by William Orr. + +- Issues #24099, #24100, and #24101: Fix free-after-use bug in heapq's siftup + and siftdown functions. + +- Backport collections.deque fixes from Python 3.5. Prevents reentrant badness + during deletion by deferring the decref until the container has been restored + to a consistent state. + +- Issue #23008: Fixed resolving attributes with boolean value is False in pydoc. + +- Fix asyncio issue 235: LifoQueue and PriorityQueue's put didn't + increment unfinished tasks (this bug was introduced in 3.4.3 when + JoinableQueue was merged with Queue). + +- Issue #23908: os functions now reject paths with embedded null character + on Windows instead of silently truncate them. + +- Issue #23728: binascii.crc_hqx() could return an integer outside of the range + 0-0xffff for empty data. +>>>>>>> other - Issue #23811: Add missing newline to the PyCompileError error message. Patch by Alex Shkop. |