[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: a7c7af98d755f34e84d1f0f847e2c0d5cc5b7e2f Author: Zac Medico gentoo org> AuthorDate: Wed Jul 18 03:36:59 2018 + Commit: Zac Medico gentoo org> CommitDate: Wed Jul 18 03:40:05 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a7c7af98 EventLoop: raise TypeError for unexpected call_* keyword args pym/portage/util/_eventloop/EventLoop.py | 24 1 file changed, 24 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 084ff0c18..ffd12cff9 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -859,11 +859,23 @@ class EventLoop(object): @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ + try: + unexpected = next(key for key in kwargs if key != 'context') + except StopIteration: + pass + else: + raise TypeError("call_soon() got an unexpected keyword argument '%s'" % unexpected) return self._handle(self._idle_add( self._call_soon_callback(callback, args)), self) def call_soon_threadsafe(self, callback, *args, **kwargs): """Like call_soon(), but thread safe.""" + try: + unexpected = next(key for key in kwargs if key != 'context') + except StopIteration: + pass + else: + raise TypeError("call_soon_threadsafe() got an unexpected keyword argument '%s'" % unexpected) # idle_add provides thread safety return self._handle(self.idle_add( self._call_soon_callback(callback, args)), self) @@ -909,6 +921,12 @@ class EventLoop(object): @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ + try: + unexpected = next(key for key in kwargs if key != 'context') + except StopIteration: + pass + else: + raise TypeError("call_later() got an unexpected keyword argument '%s'" % unexpected) return self._handle(self.timeout_add( delay * 1000, self._call_soon_callback(callback, args)), self) @@ -936,6 +954,12 @@ class EventLoop(object): @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ + try: + unexpected = next(key for key in kwargs if key != 'context') + except StopIteration: + pass + else: + raise TypeError("call_at() got an unexpected keyword argument '%s'" % unexpected) delta = when - self.time() return self.call_later(delta if delta > 0 else 0, callback, *args)
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: e46dd735cd4dde58cf3f8ef3cd2b8b29561f5b3e Author: Zac Medico gentoo org> AuthorDate: Tue Jul 17 19:27:28 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Jul 17 19:27:28 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=e46dd735 EventLoop: use python2.7 compatible **kwargs for call_* context arg Since python2.7 does not allow positional default arguments after *args, use **kwargs instead. Fixes: ae8cc32ccd81 ("EventLoop: add call_* context arg for python3.7 compat") pym/portage/util/_eventloop/EventLoop.py | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 69ccbac2c..084ff0c18 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -832,7 +832,7 @@ class EventLoop(object): return future.result() - def call_soon(self, callback, *args, context=None): + def call_soon(self, callback, *args, **kwargs): """ Arrange for a callback to be called as soon as possible. The callback is called after call_soon() returns, when control returns to the event @@ -862,7 +862,7 @@ class EventLoop(object): return self._handle(self._idle_add( self._call_soon_callback(callback, args)), self) - def call_soon_threadsafe(self, callback, *args, context=None): + def call_soon_threadsafe(self, callback, *args, **kwargs): """Like call_soon(), but thread safe.""" # idle_add provides thread safety return self._handle(self.idle_add( @@ -877,7 +877,7 @@ class EventLoop(object): """ return monotonic() - def call_later(self, delay, callback, *args, context=None): + def call_later(self, delay, callback, *args, **kwargs): """ Arrange for the callback to be called after the given delay seconds (either an int or float). @@ -912,7 +912,7 @@ class EventLoop(object): return self._handle(self.timeout_add( delay * 1000, self._call_soon_callback(callback, args)), self) - def call_at(self, when, callback, *args, context=None): + def call_at(self, when, callback, *args, **kwargs): """ Arrange for the callback to be called at the given absolute timestamp when (an int or float), using the same time reference as
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: ae8cc32ccd812661650647feffa1b10fc3ab5837 Author: Zac Medico gentoo org> AuthorDate: Tue Jul 17 19:04:28 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Jul 17 19:05:38 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ae8cc32c EventLoop: add call_* context arg for python3.7 compat The context argument currently does nothing, but exists for minimal interoperability with Future instances that require it for PEP 567. pym/portage/util/_eventloop/EventLoop.py | 26 ++ 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index df76374d9..69ccbac2c 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -832,7 +832,7 @@ class EventLoop(object): return future.result() - def call_soon(self, callback, *args): + def call_soon(self, callback, *args, context=None): """ Arrange for a callback to be called as soon as possible. The callback is called after call_soon() returns, when control returns to the event @@ -844,18 +844,25 @@ class EventLoop(object): Any positional arguments after the callback will be passed to the callback when it is called. + The context argument currently does nothing, but exists for minimal + interoperability with Future instances that require it for PEP 567. + An object compatible with asyncio.Handle is returned, which can be used to cancel the callback. @type callback: callable @param callback: a function to call + @type context: contextvars.Context + @param context: An optional keyword-only context argument allows + specifying a custom contextvars.Context for the callback to run + in. The current context is used when no context is provided. @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ return self._handle(self._idle_add( self._call_soon_callback(callback, args)), self) - def call_soon_threadsafe(self, callback, *args): + def call_soon_threadsafe(self, callback, *args, context=None): """Like call_soon(), but thread safe.""" # idle_add provides thread safety return self._handle(self.idle_add( @@ -870,7 +877,7 @@ class EventLoop(object): """ return monotonic() - def call_later(self, delay, callback, *args): + def call_later(self, delay, callback, *args, context=None): """ Arrange for the callback to be called after the given delay seconds (either an int or float). @@ -886,19 +893,26 @@ class EventLoop(object): it is called. If you want the callback to be called with some named arguments, use a closure or functools.partial(). + The context argument currently does nothing, but exists for minimal + interoperability with Future instances that require it for PEP 567. + Use functools.partial to pass keywords to the callback. @type delay: int or float @param delay: delay seconds @type callback: callable @param callback: a function to call + @type context: contextvars.Context + @param context: An optional keyword-only context argument allows + specifying a custom contextvars.Context for the callback to run + in. The current context is used when no context is provided. @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ return self._handle(self.timeout_add( delay * 1000, self._call_soon_callback(callback, args)), self) - def call_at(self, when, callback, *args): + def call_at(self, when, callback, *args, context=None): """ Arrange for the callback to be called at the given absolute timestamp when (an int or float), using the same time reference as @@ -915,6 +929,10 @@ class EventLoop(object): @param when: absolute timestamp when to call callback @type callback: callable @param callback: a function to call + @type context: contextvars.Context + @param context: An optional keyword-only context argument allows + specifying a custom contextvars.Context for the callback to run +
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 6b50ba69f5a8e311fcddfb2e5c203631bd292c71 Author: Zac Medico gentoo org> AuthorDate: Thu Jun 21 21:03:38 2018 + Commit: Zac Medico gentoo org> CommitDate: Wed Jun 27 03:04:38 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6b50ba69 AsyncioEventLoop: exit after unhandled exception (bug 658684) Fix portage commands to exit immediately for any unhandled exceptions that are raised while the asyncio event loop is running without a tty. If we have a tty then start the debugger, since in might aid in diagnosis of the problem. In order to avoid potential interference with API consumers, do not call set_exception_handler unless portage._internal_caller is True. Bug: https://bugs.gentoo.org/658684 pym/portage/util/_eventloop/asyncio_event_loop.py | 31 +++ 1 file changed, 31 insertions(+) diff --git a/pym/portage/util/_eventloop/asyncio_event_loop.py b/pym/portage/util/_eventloop/asyncio_event_loop.py index c07b71103..ea0e03b23 100644 --- a/pym/portage/util/_eventloop/asyncio_event_loop.py +++ b/pym/portage/util/_eventloop/asyncio_event_loop.py @@ -2,7 +2,9 @@ # Distributed under the terms of the GNU General Public License v2 import os +import pdb import signal +import sys try: import asyncio as _real_asyncio @@ -53,6 +55,35 @@ class AsyncioEventLoop(_AbstractEventLoop): self.get_debug = loop.get_debug self._wakeup_fd = -1 + if portage._internal_caller: + loop.set_exception_handler(self._internal_caller_exception_handler) + + @staticmethod + def _internal_caller_exception_handler(loop, context): + """ + An exception handler which drops to a pdb shell if std* streams + refer to a tty, and otherwise kills the process with SIGTERM. + + In order to avoid potential interference with API consumers, this + implementation is only used when portage._internal_caller is True. + """ + loop.default_exception_handler(context) + if 'exception' in context: + # If we have a tty then start the debugger, since in might + # aid in diagnosis of the problem. If there's no tty, then + # exit immediately. + if all(s.isatty() for s in (sys.stdout, sys.stderr, sys.stdin)): + pdb.set_trace() + else: + # Normally emerge will wait for all coroutines to complete + # after SIGTERM has been received. However, an unhandled + # exception will prevent the interrupted coroutine from + # completing, therefore use the default SIGTERM handler + # in order to ensure that emerge exits immediately (though + # uncleanly). + signal.signal(signal.SIGTERM, signal.SIG_DFL) + os.kill(os.getpid(), signal.SIGTERM) + def _create_future(self): """ Provide AbstractEventLoop.create_future() for python3.4.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 4fb5ef2ce2cb27ae155a25bfa5a4666597afb6ac Author: Zac Medico gentoo org> AuthorDate: Sat May 26 20:20:06 2018 + Commit: Zac Medico gentoo org> CommitDate: Sat May 26 20:20:06 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=4fb5ef2c EventLoop.close: fix 'AttributeError: close' for python2 For python2 without epoll, fix handling of missing 'close' attribute on self._poll_obj. Fixes: 4095be74985c ("Add ForkExecutor (bug 649588)") Reported-by: Brian Evans gentoo.org> pym/portage/util/_eventloop/EventLoop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index de0795224..df76374d9 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -968,7 +968,7 @@ class EventLoop(object): executor.shutdown(wait=True) if self._poll_obj is not None: - close = getattr(self._poll_obj, 'close') + close = getattr(self._poll_obj, 'close', None) if close is not None: close() self._poll_obj = None
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 8a8f527c7587bf388645ad703e0305797a26c3b4 Author: Zac Medico gentoo org> AuthorDate: Fri May 25 03:06:08 2018 + Commit: Zac Medico gentoo org> CommitDate: Fri May 25 03:06:32 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=8a8f527c AsyncioEventLoop: remove redundant set_wakeup_fd call pym/portage/util/_eventloop/asyncio_event_loop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pym/portage/util/_eventloop/asyncio_event_loop.py b/pym/portage/util/_eventloop/asyncio_event_loop.py index 65b354544..c07b71103 100644 --- a/pym/portage/util/_eventloop/asyncio_event_loop.py +++ b/pym/portage/util/_eventloop/asyncio_event_loop.py @@ -104,5 +104,3 @@ class AsyncioEventLoop(_AbstractEventLoop): return self._loop.run_until_complete(future) finally: self._wakeup_fd = signal.set_wakeup_fd(-1) - if self._wakeup_fd != -1: - signal.set_wakeup_fd(-1)
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 65379f436759dfbc4d56e52f1a145950779ebb60 Author: Zac Medico gentoo org> AuthorDate: Sun May 13 16:45:27 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun May 13 16:57:38 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=65379f43 AsyncioEventLoop: suppress BlockingIOError warning during loop close (bug 655656) Disable the asyncio event loop's SIGCHLD handler before attempting to close it, in order to suppress a harmless BlockingIOError warning during loop close. Closes: https://bugs.gentoo.org/655656 Reported-by: Helmut Jarausch igpm.rwth-aachen.de> pym/portage/util/_eventloop/asyncio_event_loop.py | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pym/portage/util/_eventloop/asyncio_event_loop.py b/pym/portage/util/_eventloop/asyncio_event_loop.py index b365939b0..bf5937de8 100644 --- a/pym/portage/util/_eventloop/asyncio_event_loop.py +++ b/pym/portage/util/_eventloop/asyncio_event_loop.py @@ -1,6 +1,8 @@ # Copyright 2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +import signal + try: import asyncio as _real_asyncio from asyncio.events import AbstractEventLoop as _AbstractEventLoop @@ -31,7 +33,6 @@ class AsyncioEventLoop(_AbstractEventLoop): self.call_at = loop.call_at self.is_running = loop.is_running self.is_closed = loop.is_closed - self.close = loop.close self.create_future = (loop.create_future if hasattr(loop, 'create_future') else self._create_future) self.create_task = loop.create_task @@ -75,3 +76,10 @@ class AsyncioEventLoop(_AbstractEventLoop): @return: the internal event loop's AbstractEventLoop interface """ return self + + def close(self): + # Suppress spurious error messages like the following for bug 655656: + # Exception ignored when trying to write to the signal wakeup fd: + # BlockingIOError: [Errno 11] Resource temporarily unavailable + self._loop.remove_signal_handler(signal.SIGCHLD) + self._loop.close()
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: a1360e017b7ee8156ad4ad850e2f8ea40228ca1a Author: Zac Medico gentoo org> AuthorDate: Mon May 7 00:06:07 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon May 7 00:19:54 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a1360e01 EventLoop.run_in_executor: use asyncio.wrap_future Since executors from the concurrent.futures package return concurrent.futures.Future, it's necessary to wrap them. pym/portage/util/_eventloop/EventLoop.py | 11 ++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index fc7380b03..de0795224 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -13,6 +13,11 @@ import signal import sys import traceback +try: + import asyncio as _real_asyncio +except ImportError: + _real_asyncio = None + try: import fcntl except ImportError: @@ -937,7 +942,11 @@ class EventLoop(object): if executor is None: executor = ForkExecutor(loop=self) self._default_executor = executor - return executor.submit(func, *args) + future = executor.submit(func, *args) + if _real_asyncio is not None: + future = _real_asyncio.wrap_future(future, + loop=self._asyncio_wrapper) + return future def is_running(self): """Return whether the event loop is currently running."""
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: da5efb5e677d12c850ff02140e24a095a8efdafb Author: Zac Medico gentoo org> AuthorDate: Thu Apr 19 06:36:32 2018 + Commit: Zac Medico gentoo org> CommitDate: Thu Apr 19 06:36:32 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=da5efb5e EventLoop.run_until_complete: remove unneeded partial Fixes: 25245d7eb86e ("EventLoop.run_until_complete: wait for done callbacks") pym/portage/util/_eventloop/EventLoop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 33fae26f2..6a8b906ed 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -818,7 +818,7 @@ class EventLoop(object): # is easily achieved by registering a done callback and waiting for # it to execute. waiter = self.create_future() - future.add_done_callback(functools.partial(waiter.set_result)) + future.add_done_callback(waiter.set_result) while not waiter.done(): self.iteration()
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 25245d7eb86ed197b7d7cfead0dbe4ce8ad4bc5b Author: Zac Medico gentoo org> AuthorDate: Thu Apr 19 06:13:33 2018 + Commit: Zac Medico gentoo org> CommitDate: Thu Apr 19 06:13:33 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=25245d7e EventLoop.run_until_complete: wait for done callbacks Since done callbacks are executed via call_soon, it's desirable to continue iterating until those callbacks have executed, which is easily achieved by registering a done callback and waiting for it to execute. pym/portage/util/_eventloop/EventLoop.py | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 7208c3aa1..33fae26f2 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -812,7 +812,14 @@ class EventLoop(object): @raise: the Future's exception """ future = asyncio.ensure_future(future, loop=self._asyncio_wrapper) - while not future.done(): + + # Since done callbacks are executed via call_soon, it's desirable + # to continue iterating until those callbacks have executed, which + # is easily achieved by registering a done callback and waiting for + # it to execute. + waiter = self.create_future() + future.add_done_callback(functools.partial(waiter.set_result)) + while not waiter.done(): self.iteration() return future.result()
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: b443b87c5397867c287f3bc4c5f1f4fa5e234d0a Author: Zac Medico gentoo org> AuthorDate: Wed Apr 18 21:38:33 2018 + Commit: Zac Medico gentoo org> CommitDate: Wed Apr 18 21:49:42 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=b443b87c EventLoop._run_idle_callbacks: support recursive calls (bug 653508) Since recursive calls must be supported until all consumers of the AsynchronousLock.unlock() method have been migrated to use the async_unlock() method (bug 614108.), support recursive calls by using a self._idle_callbacks_remaining loop control variable that is reset by each recursive call. Fixes: 9772f8f2a58a (EventLoop._idle_add: use thread-safe deque append) Bug: https://bugs.gentoo.org/653508 pym/portage/util/_eventloop/EventLoop.py | 60 ++-- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 895303699..7208c3aa1 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -145,7 +145,7 @@ class EventLoop(object): # Use deque, with thread-safe append, in order to emulate the FIFO # queue behavior of the AbstractEventLoop.call_soon method. self._idle_callbacks = collections.deque() - self._idle_callbacks_running = False + self._idle_callbacks_remaining = 0 self._timeout_handlers = {} self._timeout_interval = None self._default_executor = None @@ -534,26 +534,29 @@ class EventLoop(object): reschedule = [] # Use remaining count to avoid calling any newly scheduled callbacks, # since self._idle_callbacks can be modified during the exection of - # these callbacks. - remaining = len(self._idle_callbacks) - try: - while remaining: - remaining -= 1 - try: - x = self._idle_callbacks.popleft() # thread-safe - except IndexError: - break - if x._cancelled: - # it got cancelled while executing another callback - continue - if x._callback(*x._args): - reschedule.append(x) - else: - x._cancelled = True - state_change += 1 - finally: - # Reschedule those that were not cancelled. - self._idle_callbacks.extend(reschedule) + # these callbacks. The remaining count can be reset by recursive + # calls to this method. Recursion must remain supported until all + # consumers of AsynchronousLock.unlock() have been migrated to the + # async_unlock() method, see bug 614108. + self._idle_callbacks_remaining = len(self._idle_callbacks) + + while self._idle_callbacks_remaining: + self._idle_callbacks_remaining -= 1 + try: + x = self._idle_callbacks.popleft() # thread-safe + except IndexError: + break + if x._cancelled: + # it got cancelled while executing another callback + continue + if x._callback(*x._args): + # Reschedule, but not until after it's called, since + # we don't want it to call itself in a recursive call + # to this method. + self._idle_callbacks.append(x) + else: + x._cancelled = True + state_change += 1 return bool(state_change) @@ -587,19 +590,8 @@ class EventLoop(object): with self._thread_rlock: - if self._idle_callbacks_running: - # The caller should use call_soon in order to - # prevent recursion here. Raise an error because - # _run_idle_callbacks has an internal remaining - # count that recursion would render meaningless. - raise AssertionError('idle callback recursion') - - self._idle_callbacks_running = True - try: - if self._run_idle_callbacks(): -
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: d36f8b2c9c43311f4c1333afe7ce1cc7a147b836 Author: Zac Medico gentoo org> AuthorDate: Tue Apr 17 18:19:02 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Apr 17 18:19:02 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d36f8b2c EventLoop._run_idle_callbacks: make recursive calls fatal The caller should use call_soon in order to prevent recursion here. Raise an error because _run_idle_callbacks has an internal remaining count that recursion would render meaningless. Fixes: 9772f8f2a58a (EventLoop._idle_add: use thread-safe deque append) pym/portage/util/_eventloop/EventLoop.py | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index a61a3d74a..895303699 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -145,6 +145,7 @@ class EventLoop(object): # Use deque, with thread-safe append, in order to emulate the FIFO # queue behavior of the AbstractEventLoop.call_soon method. self._idle_callbacks = collections.deque() + self._idle_callbacks_running = False self._timeout_handlers = {} self._timeout_interval = None self._default_executor = None @@ -586,8 +587,19 @@ class EventLoop(object): with self._thread_rlock: - if self._run_idle_callbacks(): - calls += 1 + if self._idle_callbacks_running: + # The caller should use call_soon in order to + # prevent recursion here. Raise an error because + # _run_idle_callbacks has an internal remaining + # count that recursion would render meaningless. + raise AssertionError('idle callback recursion') + + self._idle_callbacks_running = True + try: + if self._run_idle_callbacks(): + calls += 1 + finally: + self._idle_callbacks_running = False if not self._timeout_handlers: return bool(calls)
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 0f7c9a73a805af5ec70da587b3c7d7f59dabe5ce Author: Zac Medico gentoo org> AuthorDate: Tue Apr 17 17:57:04 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Apr 17 17:59:10 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=0f7c9a73 EventLoop._run_idle_callbacks: remove recursion check This recursion check does not really work because callbacks are removed from self._idle_callbacks before recursion would occur. Fixes: 9772f8f2a58a (EventLoop._idle_add: use thread-safe deque append) pym/portage/util/_eventloop/EventLoop.py | 22 ++ 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index c38a4defd..a61a3d74a 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -55,7 +55,7 @@ class EventLoop(object): __slots__ = ("callback", "data", "pid", "source_id") class _idle_callback_class(SlotObject): - __slots__ = ("_args", "_callback", "_calling", "_cancelled") + __slots__ = ("_args", "_callback", "_cancelled") class _io_handler_class(SlotObject): __slots__ = ("args", "callback", "f", "source_id") @@ -545,21 +545,11 @@ class EventLoop(object): if x._cancelled: # it got cancelled while executing another callback continue - if x._calling: - # The caller should use call_soon in order to prevent - # recursion here. Raise an error because recursive - # calls would make the remaining count for this loop - # meaningless. - raise AssertionError('recursive idle callback') - x._calling = True - try: - if x._callback(*x._args): - reschedule.append(x) - else: - x._cancelled = True - state_change += 1 - finally: - x._calling = False + if x._callback(*x._args): + reschedule.append(x) + else: + x._cancelled = True + state_change += 1 finally: # Reschedule those that were not cancelled. self._idle_callbacks.extend(reschedule)
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 0f8e3cd3cc695e721a8b1f7cfc56c53aca19fe4d Author: Zac Medico gentoo org> AuthorDate: Tue Apr 17 06:20:45 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Apr 17 06:20:45 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=0f8e3cd3 EventLoop._run_idle_callbacks: make recursive callbacks fatal The caller should use call_soon in order to prevent recursion here. Raise an error because recursive calls would make the remaining count for this loop meaningless. Fixes: 9772f8f2a58a (EventLoop._idle_add: use thread-safe deque append) pym/portage/util/_eventloop/EventLoop.py | 7 +-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index d4f20c6ed..c38a4defd 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -546,8 +546,11 @@ class EventLoop(object): # it got cancelled while executing another callback continue if x._calling: - # don't call it recursively - continue + # The caller should use call_soon in order to prevent + # recursion here. Raise an error because recursive + # calls would make the remaining count for this loop + # meaningless. + raise AssertionError('recursive idle callback') x._calling = True try: if x._callback(*x._args):
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 9772f8f2a58a858a80ad1542d1ce46193616be67 Author: Zac Medico gentoo org> AuthorDate: Mon Apr 16 23:55:14 2018 + Commit: Zac Medico gentoo org> CommitDate: Tue Apr 17 00:49:09 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9772f8f2 EventLoop._idle_add: use thread-safe deque append This fixes previously unsafe usage of self._idle_callbacks when it was a dictionary. The deque append is thread-safe, but it does *not* notify the loop's thread, so the caller must notify if appropriate. Fixes: 1ee8971ba1cb ("EventLoop: eliminate thread safety from call_soon") pym/portage/util/_eventloop/EventLoop.py | 90 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index ae5a0a70a..d4f20c6ed 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -3,6 +3,7 @@ from __future__ import division +import collections import errno import functools import logging @@ -30,7 +31,6 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures.unix_events:_PortageEventLoop,_PortageChildWatcher', ) -from portage import OrderedDict from portage.util import writemsg_level from portage.util.monotonic import monotonic from ..SlotObject import SlotObject @@ -55,7 +55,7 @@ class EventLoop(object): __slots__ = ("callback", "data", "pid", "source_id") class _idle_callback_class(SlotObject): - __slots__ = ("args", "callback", "calling", "source_id") + __slots__ = ("_args", "_callback", "_calling", "_cancelled") class _io_handler_class(SlotObject): __slots__ = ("args", "callback", "f", "source_id") @@ -141,10 +141,10 @@ class EventLoop(object): # If this attribute has changed since the last time that the # call_soon callbacks have been called, then it's not safe to # wait on self._thread_condition without a timeout. - self._call_soon_id = 0 - # Use OrderedDict in order to emulate the FIFO queue behavior - # of the AbstractEventLoop.call_soon method. - self._idle_callbacks = OrderedDict() + self._call_soon_id = None + # Use deque, with thread-safe append, in order to emulate the FIFO + # queue behavior of the AbstractEventLoop.call_soon method. + self._idle_callbacks = collections.deque() self._timeout_handlers = {} self._timeout_interval = None self._default_executor = None @@ -298,7 +298,10 @@ class EventLoop(object): events_handled += 1 timeouts_checked = True - call_soon = prev_call_soon_id != self._call_soon_id + call_soon = prev_call_soon_id is not self._call_soon_id + if self._call_soon_id is not None and self._call_soon_id._cancelled: + # Allow garbage collection of cancelled callback. + self._call_soon_id = None if (not call_soon and not event_handlers and not events_handled and may_block): @@ -501,8 +504,9 @@ class EventLoop(object): @type callback: callable @param callback: a function to call - @rtype: int - @return: an integer ID + @return: a handle which can be used to cancel the callback + via the source_remove method + @rtype: object """ with self._thread_condition: source_id = self._idle_add(callback, *args) @@ -511,32 +515,51 @@ class EventLoop(object): def _idle_add(self, callback, *args): """Like idle_add(), but without thread safety.""" - source_id = self._call_soon_id = self._new_source_id() - self._idle_callbacks[source_id] = self._idle_callback_class( - args=args, callback=callback, source_id=source_id) - return source_id + # Hold self._thread_condition when assigning self._call_soon_id, + # since it might be modified via a thread-safe method. + with self._thread_condition: + handle = self._call_soon_id = self._idle_callback_class( + _args=args, _callback=callback) + # This deque append is thread-safe, but it does *not* notify the + # loop's thread, so the caller must notify if appropriate. + self._idle_callbacks.append(handle) + return handle def
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/portage/util/futures/_asyncio/
commit: f0db495139aa96803ec61259f3367564f6199466 Author: Zac Medico gentoo org> AuthorDate: Mon Apr 16 19:26:12 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Apr 16 19:26:47 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=f0db4951 EventLoop: refer to asyncio.Future (uses compat shim in python2) pym/portage/util/_eventloop/EventLoop.py | 3 +-- pym/portage/util/futures/_asyncio/__init__.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 4ef600a5b..ae5a0a70a 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -26,7 +26,6 @@ except ImportError: import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures:asyncio', - 'portage.util.futures.futures:Future', 'portage.util.futures.executor.fork:ForkExecutor', 'portage.util.futures.unix_events:_PortageEventLoop,_PortageChildWatcher', ) @@ -207,7 +206,7 @@ class EventLoop(object): """ Create a Future object attached to the loop. """ - return Future(loop=self._asyncio_wrapper) + return asyncio.Future(loop=self._asyncio_wrapper) def _new_source_id(self): """ diff --git a/pym/portage/util/futures/_asyncio/__init__.py b/pym/portage/util/futures/_asyncio/__init__.py index eca4ea284..d1584fdea 100644 --- a/pym/portage/util/futures/_asyncio/__init__.py +++ b/pym/portage/util/futures/_asyncio/__init__.py @@ -6,6 +6,7 @@ __all__ = ( 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ensure_future', + 'Future', 'get_child_watcher', 'get_event_loop', 'set_child_watcher',
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 1ee8971ba1cb34e6b3cd3d5fda23066b24630e3a Author: Zac Medico gentoo org> AuthorDate: Mon Apr 16 08:29:36 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Apr 16 08:46:21 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=1ee8971b EventLoop: eliminate thread safety from call_soon The call_soon method is used heavily by asyncio.Task to execute coroutine steps, so it's important to eliminate the overhead associated with thread safety. pym/portage/util/_eventloop/EventLoop.py | 20 ++-- 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 38e735999..4ef600a5b 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -506,12 +506,17 @@ class EventLoop(object): @return: an integer ID """ with self._thread_condition: - source_id = self._call_soon_id = self._new_source_id() - self._idle_callbacks[source_id] = self._idle_callback_class( - args=args, callback=callback, source_id=source_id) + source_id = self._idle_add(callback, *args) self._thread_condition.notify() return source_id + def _idle_add(self, callback, *args): + """Like idle_add(), but without thread safety.""" + source_id = self._call_soon_id = self._new_source_id() + self._idle_callbacks[source_id] = self._idle_callback_class( + args=args, callback=callback, source_id=source_id) + return source_id + def _run_idle_callbacks(self): # assumes caller has acquired self._thread_rlock if not self._idle_callbacks: @@ -810,11 +815,14 @@ class EventLoop(object): @return: a handle which can be used to cancel the callback @rtype: asyncio.Handle (or compatible) """ - return self._handle(self.idle_add( + return self._handle(self._idle_add( self._call_soon_callback(callback, args)), self) - # The call_soon method inherits thread safety from the idle_add method. - call_soon_threadsafe = call_soon + def call_soon_threadsafe(self, callback, *args): + """Like call_soon(), but thread safe.""" + # idle_add provides thread safety + return self._handle(self.idle_add( + self._call_soon_callback(callback, args)), self) def time(self): """Return the time according to the event loop's clock.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: dc7faa37c3b5b9cbb9736d4b1669510db1b953d6 Author: Zac Medico gentoo org> AuthorDate: Mon Apr 16 06:40:16 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Apr 16 06:40:16 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=dc7faa37 EventLoop: fix add_reader/writer to call source_remove The previous instance of self._selector_callback has to be removed before replacing it with a new instance. Fixes: 2b6e90fadfb1 ("EventLoop: support add_reader/writer fd overlap (bug 649588)") pym/portage/util/_eventloop/EventLoop.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 0024d162c..38e735999 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -620,6 +620,7 @@ class EventLoop(object): if mask != self._EVENT_READ: selector_mask |= mask callbacks.append(item) + self.source_remove(handler.source_id) self.io_add_watch(fd, selector_mask, self._selector_callback(callbacks)) def remove_reader(self, fd): @@ -667,6 +668,7 @@ class EventLoop(object): if mask != self._EVENT_WRITE: selector_mask |= mask callbacks.append(item) + self.source_remove(handler.source_id) self.io_add_watch(fd, selector_mask, self._selector_callback(callbacks)) def remove_writer(self, fd):
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/portage/util/futures/
commit: 9ebbe247f9c92bc042a67d24cadb314f4f0a5319 Author: Zac Medico gentoo org> AuthorDate: Mon Apr 16 06:13:09 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Apr 16 06:17:52 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9ebbe247 Eventloop.run_until_complete: call asyncio.ensure_future This eliminates the need for _PortageEventLoop to override the Eventloop.run_until_complete method. pym/portage/util/_eventloop/EventLoop.py | 2 ++ pym/portage/util/futures/unix_events.py | 15 +-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 35d0a35ba..0024d162c 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -25,6 +25,7 @@ except ImportError: import portage portage.proxy.lazyimport.lazyimport(globals(), + 'portage.util.futures:asyncio', 'portage.util.futures.futures:Future', 'portage.util.futures.executor.fork:ForkExecutor', 'portage.util.futures.unix_events:_PortageEventLoop,_PortageChildWatcher', @@ -781,6 +782,7 @@ class EventLoop(object): @return: the Future's result @raise: the Future's exception """ + future = asyncio.ensure_future(future, loop=self._asyncio_wrapper) while not future.done(): self.iteration() diff --git a/pym/portage/util/futures/unix_events.py b/pym/portage/util/futures/unix_events.py index 9d84ab6aa..d69b13718 100644 --- a/pym/portage/util/futures/unix_events.py +++ b/pym/portage/util/futures/unix_events.py @@ -30,7 +30,6 @@ from portage.util.futures import ( asyncio, events, ) -from portage.util.futures.futures import Future class _PortageEventLoop(events.AbstractEventLoop): @@ -45,6 +44,7 @@ class _PortageEventLoop(events.AbstractEventLoop): @param loop: an instance of portage's internal event loop """ self._loop = loop + self.run_until_complete = loop.run_until_complete self.call_soon = loop.call_soon self.call_soon_threadsafe = loop.call_soon_threadsafe self.call_later = loop.call_later @@ -64,19 +64,6 @@ class _PortageEventLoop(events.AbstractEventLoop): self.set_debug = loop.set_debug self.get_debug = loop.get_debug - def run_until_complete(self, future): - """ - Run the event loop until a Future is done. - - @type future: asyncio.Future - @param future: a Future to wait for - @rtype: object - @return: the Future's result - @raise: the Future's exception - """ - return self._loop.run_until_complete( - asyncio.ensure_future(future, loop=self)) - def create_task(self, coro): """ Schedule a coroutine object.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 2b6e90fadfb1adcd8ccd2f313aa009b3d19ffefe Author: Zac Medico gentoo org> AuthorDate: Sun Apr 15 23:42:49 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun Apr 15 23:58:05 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=2b6e90fa EventLoop: support add_reader/writer fd overlap (bug 649588) The AbstractEventLoop add_reader and add_writer methods need to support simultaneous registration of reader and writer callbacks for the same fd. For example, this feature is used by the standard library's asyncio.unix_events._UnixWritePipeTransport class, which is used to implement AbstractEventLoop.subprocess_exec(stdin=subprocess.PIPE). Bug: https://bugs.gentoo.org/649588 pym/portage/util/_eventloop/EventLoop.py | 83 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 32dc2fc9d..35d0a35ba 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -4,6 +4,7 @@ from __future__ import division import errno +import functools import logging import os import select @@ -95,19 +96,20 @@ class EventLoop(object): self._callback(*self._args) return False - class _repeat_callback(object): + class _selector_callback(object): """ Wraps an callback, and always returns True, for callbacks that are supposed to run repeatedly. """ - __slots__ = ("_args", "_callback") + __slots__ = ("_args", "_callbacks") - def __init__(self, callback, args): - self._callback = callback - self._args = args + def __init__(self, callbacks): + self._callbacks = callbacks def __call__(self, fd, event): - self._callback(*self._args) + for callback, mask in self._callbacks: + if event & mask: + callback() return True def __init__(self, main=True): @@ -189,6 +191,9 @@ class EventLoop(object): self.IO_OUT = PollConstants.POLLOUT self.IO_PRI = PollConstants.POLLPRI + self._EVENT_READ = self.IO_IN | self.IO_HUP + self._EVENT_WRITE = self.IO_OUT + self._child_handlers = {} self._sigchld_read = None self._sigchld_write = None @@ -602,7 +607,19 @@ class EventLoop(object): Use functools.partial to pass keywords to the callback. """ - self.io_add_watch(fd, self.IO_IN, self._repeat_callback(callback, args)) + handler = self._poll_event_handlers.get(fd) + callbacks = [(functools.partial(callback, *args), self._EVENT_READ)] + selector_mask = self._EVENT_READ + if handler is not None: + if not isinstance(handler.callback, self._selector_callback): + raise AssertionError("add_reader called with fd " + "registered directly via io_add_watch") + for item in handler.callback._callbacks: + callback, mask = item + if mask != self._EVENT_READ: + selector_mask |= mask + callbacks.append(item) + self.io_add_watch(fd, selector_mask, self._selector_callback(callbacks)) def remove_reader(self, fd): """ @@ -610,7 +627,24 @@ class EventLoop(object): """ handler = self._poll_event_handlers.get(fd) if handler is not None: - return self.source_remove(handler.source_id) + if not isinstance(handler.callback, self._selector_callback): + raise AssertionError("remove_reader called with fd " + "registered directly via io_add_watch") + callbacks = [] + selector_mask = 0 + removed = False + for item in handler.callback._callbacks: + callback, mask = item + if mask == self._EVENT_READ: + removed = True + else: + selector_mask |= mask + callbacks.append(item) + self.source_remove(handler.source_id) + if callbacks: +
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 18d8abb063d7730fbb86d451489dc2acf36c1327 Author: Zac Medico gentoo org> AuthorDate: Sun Apr 15 22:25:03 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun Apr 15 22:31:07 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=18d8abb0 EventLoop: fix AttributeError in add/remove_reader Fixes: 24f861173ebe ("EventLoop: implement add/remove_reader/writer for asyncio compat (bug 649588)") pym/portage/util/_eventloop/EventLoop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index a928f3138..32dc2fc9d 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -609,7 +609,7 @@ class EventLoop(object): Stop watching the file descriptor for read availability. """ handler = self._poll_event_handlers.get(fd) - if fd is not None: + if handler is not None: return self.source_remove(handler.source_id) return False @@ -627,7 +627,7 @@ class EventLoop(object): Stop watching the file descriptor for write availability. """ handler = self._poll_event_handlers.get(fd) - if fd is not None: + if handler is not None: return self.source_remove(handler.source_id) return False
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 3b1c182750d82dc55b96ee111e356968ca9a9fb7 Author: Zac Medico gentoo org> AuthorDate: Mon Apr 9 01:25:25 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Apr 9 01:25:52 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3b1c1827 EventLoop: add call_at method for asyncio compat (bug 591760) Bug: https://bugs.gentoo.org/591760 pym/portage/util/_eventloop/EventLoop.py | 23 +++ 1 file changed, 23 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 00568c997..72eb407fc 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -775,6 +775,29 @@ class EventLoop(object): return self._handle(self.timeout_add( delay * 1000, self._call_soon_callback(callback, args)), self) + def call_at(self, when, callback, *args): + """ + Arrange for the callback to be called at the given absolute + timestamp when (an int or float), using the same time reference as + AbstractEventLoop.time(). + + This method's behavior is the same as call_later(). + + An instance of asyncio.Handle is returned, which can be used to + cancel the callback. + + Use functools.partial to pass keywords to the callback. + + @type when: int or float + @param when: absolute timestamp when to call callback + @type callback: callable + @param callback: a function to call + @return: a handle which can be used to cancel the callback + @rtype: asyncio.Handle (or compatible) + """ + delta = when - self.time() + return self.call_later(delta if delta > 0 else 0, callback, *args) + def run_in_executor(self, executor, func, *args): """ Arrange for a func to be called in the specified executor.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 24f861173ebe747a470deb8489887c067cd46b0f Author: Zac Medico gentoo org> AuthorDate: Mon Apr 2 03:46:10 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun Apr 8 22:04:37 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=24f86117 EventLoop: implement add/remove_reader/writer for asyncio compat (bug 649588) Bug: https://bugs.gentoo.org/649588 pym/portage/util/_eventloop/EventLoop.py | 51 1 file changed, 51 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 1bf606354..00568c997 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -93,6 +93,21 @@ class EventLoop(object): self._callback(*self._args) return False + class _repeat_callback(object): + """ + Wraps an callback, and always returns True, for callbacks that + are supposed to run repeatedly. + """ + __slots__ = ("_args", "_callback") + + def __init__(self, callback, args): + self._callback = callback + self._args = args + + def __call__(self, fd, event): + self._callback(*self._args) + return True + def __init__(self, main=True): """ @param main: If True then this is a singleton instance for use @@ -569,6 +584,42 @@ class EventLoop(object): return bool(calls) + def add_reader(self, fd, callback, *args): + """ + Start watching the file descriptor for read availability and then + call the callback with specified arguments. + + Use functools.partial to pass keywords to the callback. + """ + self.io_add_watch(fd, self.IO_IN, self._repeat_callback(callback, args)) + + def remove_reader(self, fd): + """ + Stop watching the file descriptor for read availability. + """ + handler = self._poll_event_handlers.get(fd) + if fd is not None: + return self.source_remove(handler.source_id) + return False + + def add_writer(self, fd, callback, *args): + """ + Start watching the file descriptor for write availability and then + call the callback with specified arguments. + + Use functools.partial to pass keywords to the callback. + """ + self.io_add_watch(fd, self.IO_OUT, self._repeat_callback(callback, args)) + + def remove_writer(self, fd): + """ + Stop watching the file descriptor for write availability. + """ + handler = self._poll_event_handlers.get(fd) + if fd is not None: + return self.source_remove(handler.source_id) + return False + def io_add_watch(self, f, condition, callback, *args): """ Like glib.io_add_watch(), your function should return False to
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 754010f346ec2455ea8c71a6af4796c10fd28d23 Author: Zac Medico gentoo org> AuthorDate: Sun Apr 8 21:36:07 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun Apr 8 21:36:07 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=754010f3 EventLoop: add is_closed method for asyncio compat (bug 591760) Bug: https://bugs.gentoo.org/591760 pym/portage/util/_eventloop/EventLoop.py | 4 1 file changed, 4 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 4ec67241f..1bf606354 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -747,6 +747,10 @@ class EventLoop(object): self._default_executor = executor return executor.submit(func, *args) + def is_closed(self): + """Returns True if the event loop was closed.""" + return self._poll_obj is None + def close(self): """Close the event loop.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: 13621b62e32a7c21aa08247b33f1faa0a146d0d4 Author: Zac Medico gentoo org> AuthorDate: Sun Apr 8 21:15:39 2018 + Commit: Zac Medico gentoo org> CommitDate: Sun Apr 8 21:15:39 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=13621b62 EventLoop: add get/set_debug methods for asyncio compat (bug 591760) Bug: https://bugs.gentoo.org/591760 pym/portage/util/_eventloop/EventLoop.py | 14 ++ 1 file changed, 14 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 1574a6837..4ec67241f 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -102,6 +102,7 @@ class EventLoop(object): @type main: bool """ self._use_signal = main and fcntl is not None + self._debug = bool(os.environ.get('PYTHONASYNCIODEBUG')) self._thread_rlock = threading.RLock() self._thread_condition = threading.Condition(self._thread_rlock) self._poll_event_queue = [] @@ -763,6 +764,19 @@ class EventLoop(object): close() self._poll_obj = None + def get_debug(self): + """ + Get the debug mode (bool) of the event loop. + + The default value is True if the environment variable + PYTHONASYNCIODEBUG is set to a non-empty string, False otherwise. + """ + return self._debug + + def set_debug(self, enabled): + """Set the debug mode of the event loop.""" + self._debug = enabled + _can_poll_device = None
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: db3d216676831d8b6c184fb25f94dc54f8710a11 Author: Zac Medico gentoo org> AuthorDate: Sat Apr 7 20:19:26 2018 + Commit: Zac Medico gentoo org> CommitDate: Sat Apr 7 20:20:15 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=db3d2166 GlibEventLoop: remove (unused and unmaintained) pym/portage/util/_eventloop/GlibEventLoop.py | 23 --- pym/portage/util/_eventloop/global_event_loop.py | 1 - 2 files changed, 24 deletions(-) diff --git a/pym/portage/util/_eventloop/GlibEventLoop.py b/pym/portage/util/_eventloop/GlibEventLoop.py deleted file mode 100644 index f2f5c5e64..0 --- a/pym/portage/util/_eventloop/GlibEventLoop.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2012 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -class GlibEventLoop(object): - - # TODO: Support multiprocessing by using a separate glib.MainContext - # instance for each process. - supports_multiprocessing = False - - def __init__(self): - import gi.repository.GLib as glib - self.IO_ERR = glib.IO_ERR - self.IO_HUP = glib.IO_HUP - self.IO_IN = glib.IO_IN - self.IO_NVAL = glib.IO_NVAL - self.IO_OUT = glib.IO_OUT - self.IO_PRI = glib.IO_PRI - self.iteration = glib.main_context_default().iteration - self.child_watch_add = glib.child_watch_add - self.idle_add = glib.idle_add - self.io_add_watch = glib.io_add_watch - self.timeout_add = glib.timeout_add - self.source_remove = glib.source_remove diff --git a/pym/portage/util/_eventloop/global_event_loop.py b/pym/portage/util/_eventloop/global_event_loop.py index 502dab882..e2c7d71ea 100644 --- a/pym/portage/util/_eventloop/global_event_loop.py +++ b/pym/portage/util/_eventloop/global_event_loop.py @@ -6,7 +6,6 @@ import os from .EventLoop import EventLoop _default_constructor = EventLoop -#from .GlibEventLoop import GlibEventLoop as _default_constructor # If _default_constructor doesn't support multiprocessing, # then _multiprocessing_constructor is used in subprocesses.
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/
commit: dac5089eb7908e9fd643f46c913515082077281e Author: Zac Medico gentoo org> AuthorDate: Fri May 5 09:07:38 2017 + Commit: Zac Medico gentoo org> CommitDate: Fri May 5 18:32:45 2017 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=dac5089e Eventloop: fix deadlock involving idle_add/call_soon (bug 617550) Guarantee that newly added idle_add/call_soon callbacks have an opportunity to execute before the event loop decides to wait on self._thread_condition without a timeout. This fixes a case where the event loop would wait on self._thread_condition indefinitely, even though a callback scheduled by the AsynchronousTask._async_wait method needed to be executed first. X-Gentoo-bug: 617550 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=617550 Acked-by: Brian Dolbec gentoo.org> pym/portage/util/_eventloop/EventLoop.py | 18 -- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 712838e3d..cd154005f 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -108,6 +108,15 @@ class EventLoop(object): self._poll_event_handler_ids = {} # Increment id for each new handler. self._event_handler_id = 0 + # New call_soon callbacks must have an opportunity to + # execute before it's safe to wait on self._thread_condition + # without a timeout, since delaying its execution indefinitely + # could lead to a deadlock. The following attribute stores the + # event handler id of the most recently added call_soon callback. + # If this attribute has changed since the last time that the + # call_soon callbacks have been called, then it's not safe to + # wait on self._thread_condition without a timeout. + self._call_soon_id = 0 # Use OrderedDict in order to emulate the FIFO queue behavior # of the AbstractEventLoop.call_soon method. self._idle_callbacks = OrderedDict() @@ -250,10 +259,15 @@ class EventLoop(object): if not event_handlers: with self._thread_condition: + prev_call_soon_id = self._call_soon_id if self._run_timeouts(): events_handled += 1 timeouts_checked = True - if not event_handlers and not events_handled and may_block: + + call_soon = prev_call_soon_id != self._call_soon_id + + if (not call_soon and not event_handlers + and not events_handled and may_block): # Block so that we don't waste cpu time by looping too # quickly. This makes EventLoop useful for code that needs # to wait for timeout callbacks regardless of whether or @@ -457,7 +471,7 @@ class EventLoop(object): @return: an integer ID """ with self._thread_condition: - source_id = self._new_source_id() + source_id = self._call_soon_id = self._new_source_id() self._idle_callbacks[source_id] = self._idle_callback_class( args=args, callback=callback, source_id=source_id) self._thread_condition.notify()
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/portage/util/_async/, ...
commit: 4b12ed04ec6b99f5a948e0eea5778a4fac502740 Author: Zac Medico gentoo org> AuthorDate: Sun Mar 26 00:45:52 2017 + Commit: Zac Medico gentoo org> CommitDate: Sun Mar 26 20:05:46 2017 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=4b12ed04 Future: implement add_done_callback for asyncio compat (bug 591760) Implement the add_done_callback and remove_done_callback methods, since they are required in order to make further progress toward asyncio compatibility. Also implement the AbstractEventLoop create_future method for the EventLoop class, so that it returns an instance of _EventLoopFuture. EventLoop currently does not implement some of the asyncio.AbstractEventLoop methods that asyncio.Future requires for its add_done_callback implementation, and the create_future method conveniently solves this problem. X-Gentoo-bug: 591760 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=591760 Acked-by: Brian Dolbec gentoo.org> pym/portage/tests/ebuild/test_ipc_daemon.py| 3 +- .../tests/util/eventloop/test_call_soon_fifo.py| 6 +- pym/portage/tests/util/futures/__init__.py | 0 pym/portage/tests/util/futures/__test__.py | 0 .../tests/util/futures/test_done_callback.py | 35 + pym/portage/util/_async/SchedulerInterface.py | 3 +- pym/portage/util/_eventloop/EventLoop.py | 14 pym/portage/util/futures/futures.py| 82 -- 8 files changed, 132 insertions(+), 11 deletions(-) diff --git a/pym/portage/tests/ebuild/test_ipc_daemon.py b/pym/portage/tests/ebuild/test_ipc_daemon.py index 68f139aa4..fc7916541 100644 --- a/pym/portage/tests/ebuild/test_ipc_daemon.py +++ b/pym/portage/tests/ebuild/test_ipc_daemon.py @@ -16,7 +16,6 @@ from portage.util import ensure_dirs from portage.util._async.ForkProcess import ForkProcess from portage.util._async.TaskScheduler import TaskScheduler from portage.util._eventloop.global_event_loop import global_event_loop -from portage.util.futures.futures import Future from _emerge.SpawnProcess import SpawnProcess from _emerge.EbuildBuildDir import EbuildBuildDir from _emerge.EbuildIpcDaemon import EbuildIpcDaemon @@ -150,7 +149,7 @@ class IpcDaemonTestCase(TestCase): self._run_done.set_result(True) def _run(self, event_loop, task_scheduler, timeout): - self._run_done = Future() + self._run_done = event_loop.create_future() timeout_id = event_loop.timeout_add(timeout, self._timeout_callback, task_scheduler) task_scheduler.addExitListener(self._exit_callback) diff --git a/pym/portage/tests/util/eventloop/test_call_soon_fifo.py b/pym/portage/tests/util/eventloop/test_call_soon_fifo.py index 5ecc13f43..f970c67a1 100644 --- a/pym/portage/tests/util/eventloop/test_call_soon_fifo.py +++ b/pym/portage/tests/util/eventloop/test_call_soon_fifo.py @@ -7,22 +7,22 @@ import random from portage import os from portage.tests import TestCase from portage.util._eventloop.global_event_loop import global_event_loop -from portage.util.futures.futures import Future + class CallSoonFifoTestCase(TestCase): def testCallSoonFifo(self): + event_loop = global_event_loop() inputs = [random.random() for index in range(10)] outputs = [] - finished = Future() + finished = event_loop.create_future() def add_output(value): outputs.append(value) if len(outputs) == len(inputs): finished.set_result(True) - event_loop = global_event_loop() for value in inputs: event_loop.call_soon(functools.partial(add_output, value)) diff --git a/pym/portage/tests/util/futures/__init__.py b/pym/portage/tests/util/futures/__init__.py new file mode 100644 index 0..e69de29bb diff --git a/pym/portage/tests/util/futures/__test__.py b/pym/portage/tests/util/futures/__test__.py new file mode 100644 index 0..e69de29bb diff --git a/pym/portage/tests/util/futures/test_done_callback.py b/pym/portage/tests/util/futures/test_done_callback.py new file mode 100644 index 0..76b727b09 --- /dev/null +++ b/pym/portage/tests/util/futures/test_done_callback.py @@ -0,0 +1,35 @@ +# Copyright 2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop + + +class FutureDoneCallbackTestCase(TestCase): + + def testFutureDoneCallback(self): + + event_loop = global_event_loop() + + def done_callback(finished): + done_callback_called.set_result(True) + + done_callback_called = event_loop.create_future() +
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/portage/tests/util/eventloop/, ...
commit: 04b1012594bfad1be719547e2c88a2dcf1051dc1 Author: Zac Medico gentoo org> AuthorDate: Tue Mar 21 06:54:47 2017 + Commit: Zac Medico gentoo org> CommitDate: Fri Mar 24 20:32:11 2017 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=04b10125 EventLoop: implement call_soon for asyncio compat (bug 591760) Since asyncio.AbstractEventLoop has no equivalent to the idle callbacks implemented by the EventLoop.idle_add method, it is necessary to implement the AbstractEventLoop.call_soon and call_soon_threadsafe methods, so that idle_add usage can eventually be eliminated. X-Gentoo-bug: 591760 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=591760 Acked-by: Brian Dolbec gentoo.org> .../tests/util/eventloop/test_call_soon_fifo.py| 30 ++ pym/portage/util/_async/SchedulerInterface.py | 5 +- pym/portage/util/_eventloop/EventLoop.py | 67 +- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/pym/portage/tests/util/eventloop/test_call_soon_fifo.py b/pym/portage/tests/util/eventloop/test_call_soon_fifo.py new file mode 100644 index 0..5ecc13f43 --- /dev/null +++ b/pym/portage/tests/util/eventloop/test_call_soon_fifo.py @@ -0,0 +1,30 @@ +# Copyright 2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import functools +import random + +from portage import os +from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop +from portage.util.futures.futures import Future + +class CallSoonFifoTestCase(TestCase): + + def testCallSoonFifo(self): + + inputs = [random.random() for index in range(10)] + outputs = [] + finished = Future() + + def add_output(value): + outputs.append(value) + if len(outputs) == len(inputs): + finished.set_result(True) + + event_loop = global_event_loop() + for value in inputs: + event_loop.call_soon(functools.partial(add_output, value)) + + event_loop.run_until_complete(finished) + self.assertEqual(inputs, outputs) diff --git a/pym/portage/util/_async/SchedulerInterface.py b/pym/portage/util/_async/SchedulerInterface.py index 2ab668ee4..6028fd90d 100644 --- a/pym/portage/util/_async/SchedulerInterface.py +++ b/pym/portage/util/_async/SchedulerInterface.py @@ -13,8 +13,9 @@ class SchedulerInterface(SlotObject): _event_loop_attrs = ("IO_ERR", "IO_HUP", "IO_IN", "IO_NVAL", "IO_OUT", "IO_PRI", - "child_watch_add", "idle_add", "io_add_watch", - "iteration", "source_remove", "timeout_add") + "call_soon", "call_soon_threadsafe", "child_watch_add", + "idle_add", "io_add_watch", "iteration", "run_until_complete", + "source_remove", "timeout_add") __slots__ = _event_loop_attrs + ("_event_loop", "_is_background") diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 8f13de377..308157bea 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -22,6 +22,7 @@ try: except ImportError: import dummy_threading as threading +from portage import OrderedDict from portage.util import writemsg_level from ..SlotObject import SlotObject from .PollConstants import PollConstants @@ -54,6 +55,38 @@ class EventLoop(object): __slots__ = ("args", "function", "calling", "interval", "source_id", "timestamp") + class _handle(object): + """ + A callback wrapper object, compatible with asyncio.Handle. + """ + __slots__ = ("_callback_id", "_loop") + + def __init__(self, callback_id, loop): + self._callback_id = callback_id + self._loop = loop + + def cancel(self): + """ + Cancel the call. If the callback is already canceled or executed, + this method has no effect. + """ + self._loop.source_remove(self._callback_id) + + class _call_soon_callback(object): + """ + Wraps a call_soon callback, and always returns False, since these + callbacks are only supposed to run once. + """ + __slots__ = ("_args", "_callback") + + def __init__(self, callback, args): + self._callback = callback + self._args = args + + def __call__(self): + self._callback(*self._args) + return False + def __init__(self, main=True):
[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/portage/, pym/_emerge/, pym/portage/_sets/, ...
commit: cbfba17290bbc14514538acbb954efeb61f82d8f Author: Michał Górny mgorny AT gentoo DOT org AuthorDate: Wed Aug 6 17:17:54 2014 + Commit: Michał Górny mgorny AT gentoo DOT org CommitDate: Mon Aug 11 20:28:46 2014 + URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=cbfba172 Enable consistent __future__ behavior for division In Python 2, the division ('/') operator defaults to integer (truncating) division when given integer argument. In Python 3, it performs floating-point division unconditionally instead. To overcome this difference and get a consistent behavior, integers were converted to floats explicitly in a few places. Instead, use a simpler 'from __future__ import division' statement that enables floating-point division globally in Python 2. Use it in all relevant files to get a consistent behavior, and use '//' appropriately whenever integer division is desired. Reviewed-by: Arfrever Frehtes Taifersar Arahesis Arfrever AT Apache.Org Acked-by: Alexander Berntsen bernalex AT gentoo.org Acked-by: Brian Dolbec dolsen AT gentoo.org --- bin/quickpkg | 4 ++-- pym/_emerge/Scheduler.py | 2 +- pym/_emerge/actions.py | 10 +- pym/_emerge/depgraph.py | 4 ++-- pym/_emerge/sync/old_tree_timestamp.py | 12 +++- pym/portage/_emirrordist/FetchTask.py| 6 -- pym/portage/_sets/dbapi.py | 4 +++- pym/portage/cache/sqlite.py | 4 ++-- pym/portage/dbapi/vartree.py | 4 ++-- pym/portage/localization.py | 2 ++ pym/portage/output.py| 6 -- pym/portage/util/_eventloop/EventLoop.py | 8 +--- pym/portage/util/_eventloop/PollSelectAdapter.py | 6 -- 13 files changed, 43 insertions(+), 29 deletions(-) diff --git a/bin/quickpkg b/bin/quickpkg index 90277ad..035131e 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -2,7 +2,7 @@ # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function +from __future__ import division, print_function import errno import math @@ -264,7 +264,7 @@ def quickpkg_main(options, args, eout): size_str = 0 else: power_of_2 = math.log(size, 2) - power_of_2 = 10*int(power_of_2/10) + power_of_2 = 10*(power_of_2//10) unit = units.get(power_of_2) if unit: size = float(size)/(2**power_of_2) diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py index dd268f7..d6db311 100644 --- a/pym/_emerge/Scheduler.py +++ b/pym/_emerge/Scheduler.py @@ -1,7 +1,7 @@ # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function, unicode_literals +from __future__ import division, print_function, unicode_literals from collections import deque import gc diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index b935139..e482744 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -1,7 +1,7 @@ # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function, unicode_literals +from __future__ import division, print_function, unicode_literals import errno import logging @@ -1499,14 +1499,14 @@ def action_info(settings, trees, myopts, myfiles): vm_info = get_vm_info() if ram.total in vm_info: - line = %-9s %10d total % (KiB Mem:, vm_info[ram.total] / 1024) + line = %-9s %10d total % (KiB Mem:, vm_info[ram.total] // 1024) if ram.free in vm_info: - line += ,%10d free % (vm_info[ram.free] / 1024,) + line += ,%10d free % (vm_info[ram.free] // 1024,) append(line) if swap.total in vm_info: - line = %-9s %10d total % (KiB Swap:, vm_info[swap.total] / 1024) + line = %-9s %10d total % (KiB Swap:, vm_info[swap.total] // 1024) if swap.free in vm_info: - line += ,%10d free % (vm_info[swap.free] / 1024,) + line += ,%10d free % (vm_info[swap.free] // 1024,) append(line) lastSync = portage.grabfile(os.path.join( diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index acb1db1..a10297a 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -1,7 +1,7 @@ # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function, unicode_literals +from __future__