From 7eb3be330d2bceb5587ab4abdcfeaa77fabd1f93 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 2 Mar 2016 11:17:01 -0500 Subject: asyncio: Remove duplicate bind addresses in create_server. Patch by Sebastien Bourdeauducq (issue #26338) --- Lib/asyncio/base_events.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 4505732f9a..9d07673fba 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -880,7 +880,10 @@ class BaseEventLoop(events.AbstractEventLoop): to host and port. The host parameter can also be a sequence of strings and in that case - the TCP server is bound to all hosts of the sequence. + the TCP server is bound to all hosts of the sequence. If a host + appears multiple times (possibly indirectly e.g. when hostnames + resolve to the same IP address), the server is only bound once to that + host. Return a Server object which can be used to stop the service. @@ -909,7 +912,7 @@ class BaseEventLoop(events.AbstractEventLoop): flags=flags) for host in hosts] infos = yield from tasks.gather(*fs, loop=self) - infos = itertools.chain.from_iterable(infos) + infos = set(itertools.chain.from_iterable(infos)) completed = False try: -- cgit v1.2.1 From f7d1ae10f3cb453a78ce7b4612148efb281e34ae Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 23 Mar 2016 00:28:08 +0100 Subject: Add a source parameter to warnings.warn() Issue #26604: * Add a new optional source parameter to _warnings.warn() and warnings.warn() * Modify asyncore, asyncio and _pyio modules to set the source parameter when logging a ResourceWarning warning --- Lib/asyncio/base_events.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9d07673fba..3a42b10cb1 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -412,7 +412,8 @@ class BaseEventLoop(events.AbstractEventLoop): if compat.PY34: def __del__(self): if not self.is_closed(): - warnings.warn("unclosed event loop %r" % self, ResourceWarning) + warnings.warn("unclosed event loop %r" % self, ResourceWarning, + source=self) if not self.is_running(): self.close() -- cgit v1.2.1 From 6fae08cd104afd15190d836823a0dab7b228d0e6 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Wed, 31 Aug 2016 08:22:29 +0100 Subject: Closes #27904: Improved logging statements to defer formatting until needed. --- Lib/asyncio/base_events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 0916da8b0a..918b869526 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1069,7 +1069,7 @@ class BaseEventLoop(events.AbstractEventLoop): transport = yield from self._make_subprocess_transport( protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs) if self._debug: - logger.info('%s: %r' % (debug_log, transport)) + logger.info('%s: %r', debug_log, transport) return transport, protocol @coroutine @@ -1099,7 +1099,7 @@ class BaseEventLoop(events.AbstractEventLoop): protocol, popen_args, False, stdin, stdout, stderr, bufsize, **kwargs) if self._debug: - logger.info('%s: %r' % (debug_log, transport)) + logger.info('%s: %r', debug_log, transport) return transport, protocol def get_exception_handler(self): -- cgit v1.2.1 From 17668cfa5e0e6f50376cc233d9b063b908a19845 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 22:01:51 -0700 Subject: Issue #28003: Implement PEP 525 -- Asynchronous Generators. --- Lib/asyncio/base_events.py | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 918b869526..b420586a13 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -13,7 +13,6 @@ conscious design decision, leaving the door open for keyword arguments to modify the meaning of the API call itself. """ - import collections import concurrent.futures import heapq @@ -28,6 +27,7 @@ import time import traceback import sys import warnings +import weakref from . import compat from . import coroutines @@ -242,6 +242,13 @@ class BaseEventLoop(events.AbstractEventLoop): self._task_factory = None self._coroutine_wrapper_set = False + # A weak set of all asynchronous generators that are being iterated + # by the loop. + self._asyncgens = weakref.WeakSet() + + # Set to True when `loop.shutdown_asyncgens` is called. + self._asyncgens_shutdown_called = False + def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' % (self.__class__.__name__, self.is_running(), @@ -333,6 +340,46 @@ class BaseEventLoop(events.AbstractEventLoop): if self._closed: raise RuntimeError('Event loop is closed') + def _asyncgen_finalizer_hook(self, agen): + self._asyncgens.discard(agen) + if not self.is_closed(): + self.create_task(agen.aclose()) + + def _asyncgen_firstiter_hook(self, agen): + if self._asyncgens_shutdown_called: + warnings.warn( + "asynchronous generator {!r} was scheduled after " + "loop.shutdown_asyncgens() call".format(agen), + ResourceWarning, source=self) + + self._asyncgens.add(agen) + + @coroutine + def shutdown_asyncgens(self): + """Shutdown all active asynchronous generators.""" + self._asyncgens_shutdown_called = True + + if not len(self._asyncgens): + return + + closing_agens = list(self._asyncgens) + self._asyncgens.clear() + + shutdown_coro = tasks.gather( + *[ag.aclose() for ag in closing_agens], + return_exceptions=True, + loop=self) + + results = yield from shutdown_coro + for result, agen in zip(results, closing_agens): + if isinstance(result, Exception): + self.call_exception_handler({ + 'message': 'an error occurred during closing of ' + 'asynchronous generator {!r}'.format(agen), + 'exception': result, + 'asyncgen': agen + }) + def run_forever(self): """Run until stop() is called.""" self._check_closed() @@ -340,6 +387,9 @@ class BaseEventLoop(events.AbstractEventLoop): raise RuntimeError('Event loop is running.') self._set_coroutine_wrapper(self._debug) self._thread_id = threading.get_ident() + old_agen_hooks = sys.get_asyncgen_hooks() + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, + finalizer=self._asyncgen_finalizer_hook) try: while True: self._run_once() @@ -349,6 +399,7 @@ class BaseEventLoop(events.AbstractEventLoop): self._stopping = False self._thread_id = None self._set_coroutine_wrapper(False) + sys.set_asyncgen_hooks(*old_agen_hooks) def run_until_complete(self, future): """Run until the Future is done. @@ -1179,7 +1230,9 @@ class BaseEventLoop(events.AbstractEventLoop): - 'handle' (optional): Handle instance; - 'protocol' (optional): Protocol instance; - 'transport' (optional): Transport instance; - - 'socket' (optional): Socket instance. + - 'socket' (optional): Socket instance; + - 'asyncgen' (optional): Asynchronous generator that caused + the exception. New keys maybe introduced in the future. -- cgit v1.2.1 From 7ae56e9058eb9fc24a8d203f913be6e8ae7e6823 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 15 Sep 2016 13:24:03 -0400 Subject: Merge 3.5 (asyncio) --- Lib/asyncio/base_events.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9c2fa12486..154fd95513 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -242,9 +242,13 @@ class BaseEventLoop(events.AbstractEventLoop): self._task_factory = None self._coroutine_wrapper_set = False - # A weak set of all asynchronous generators that are being iterated - # by the loop. - self._asyncgens = weakref.WeakSet() + if hasattr(sys, 'get_asyncgen_hooks'): + # Python >= 3.6 + # A weak set of all asynchronous generators that are + # being iterated by the loop. + self._asyncgens = weakref.WeakSet() + else: + self._asyncgens = None # Set to True when `loop.shutdown_asyncgens` is called. self._asyncgens_shutdown_called = False @@ -359,7 +363,9 @@ class BaseEventLoop(events.AbstractEventLoop): """Shutdown all active asynchronous generators.""" self._asyncgens_shutdown_called = True - if not len(self._asyncgens): + if self._asyncgens is None or not len(self._asyncgens): + # If Python version is <3.6 or we don't have any asynchronous + # generators alive. return closing_agens = list(self._asyncgens) @@ -387,9 +393,10 @@ class BaseEventLoop(events.AbstractEventLoop): raise RuntimeError('Event loop is running.') self._set_coroutine_wrapper(self._debug) self._thread_id = threading.get_ident() - old_agen_hooks = sys.get_asyncgen_hooks() - sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, - finalizer=self._asyncgen_finalizer_hook) + if self._asyncgens is not None: + old_agen_hooks = sys.get_asyncgen_hooks() + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, + finalizer=self._asyncgen_finalizer_hook) try: while True: self._run_once() @@ -399,7 +406,8 @@ class BaseEventLoop(events.AbstractEventLoop): self._stopping = False self._thread_id = None self._set_coroutine_wrapper(False) - sys.set_asyncgen_hooks(*old_agen_hooks) + if self._asyncgens is not None: + sys.set_asyncgen_hooks(*old_agen_hooks) def run_until_complete(self, future): """Run until the Future is done. -- cgit v1.2.1 From 223140a058475830a2b00723ff03fefe2589db77 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 21 Oct 2016 17:13:40 -0400 Subject: Issue #28500: Fix asyncio to handle async gens GC from another thread. --- Lib/asyncio/base_events.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 5b5fcde438..e59b2b75ac 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -351,6 +351,9 @@ class BaseEventLoop(events.AbstractEventLoop): self._asyncgens.discard(agen) if not self.is_closed(): self.create_task(agen.aclose()) + # Wake up the loop if the finalizer was called from + # a different thread. + self._write_to_self() def _asyncgen_firstiter_hook(self, agen): if self._asyncgens_shutdown_called: -- cgit v1.2.1 From 4b9446e7b8800e914a2180cfb8f701f7e6a382d1 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 28 Oct 2016 12:52:37 -0400 Subject: Issue #28544: Implement asyncio.Task in C. This implementation provides additional 10-20% speed boost for asyncio programs. The patch also fixes _asynciomodule.c to use Arguments Clinic, and makes '_schedule_callbacks' an overridable method (as it was in 3.5). --- Lib/asyncio/base_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib/asyncio/base_events.py') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 58800617d9..cc9994d66f 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -57,7 +57,7 @@ _FATAL_ERROR_IGNORE = (BrokenPipeError, def _format_handle(handle): cb = handle._callback - if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + if isinstance(getattr(cb, '__self__', None), tasks.Task): # format the task return repr(cb.__self__) else: -- cgit v1.2.1