[gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/

2018-07-17 Thread Zac Medico
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/

2018-07-17 Thread Zac Medico
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/

2018-07-17 Thread Zac Medico
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/

2018-06-26 Thread Zac Medico
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/

2018-05-26 Thread Zac Medico
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/

2018-05-24 Thread Zac Medico
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/

2018-05-13 Thread Zac Medico
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/

2018-05-06 Thread Zac Medico
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/

2018-04-19 Thread Zac Medico
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/

2018-04-19 Thread Zac Medico
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/

2018-04-18 Thread Zac Medico
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/

2018-04-17 Thread Zac Medico
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/

2018-04-17 Thread Zac Medico
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/

2018-04-17 Thread Zac Medico
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/

2018-04-16 Thread Zac Medico
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/

2018-04-16 Thread Zac Medico
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/

2018-04-16 Thread Zac Medico
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/

2018-04-16 Thread Zac Medico
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/

2018-04-16 Thread Zac Medico
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/

2018-04-15 Thread Zac Medico
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/

2018-04-15 Thread Zac Medico
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/

2018-04-08 Thread Zac Medico
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/

2018-04-08 Thread Zac Medico
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/

2018-04-08 Thread Zac Medico
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/

2018-04-08 Thread Zac Medico
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/

2018-04-07 Thread Zac Medico
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/

2017-05-05 Thread Zac Medico
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/, ...

2017-03-26 Thread Zac Medico
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/, ...

2017-03-24 Thread Zac Medico
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/, ...

2014-08-11 Thread Michał Górny
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__