From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id C1FBC138334 for ; Mon, 24 Sep 2018 06:12:18 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 10AA8E093A; Mon, 24 Sep 2018 06:12:16 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id D4CA9E093A for ; Mon, 24 Sep 2018 06:12:15 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 2A76B335CD4 for ; Mon, 24 Sep 2018 06:12:14 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 6DF533E2 for ; Mon, 24 Sep 2018 06:12:11 +0000 (UTC) From: "Zac Medico" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Zac Medico" Message-ID: <1537760492.de0b60ff277311e780102131dce3111b4db1c196.zmedico@gentoo> Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/futures/, lib/portage/util/futures/ X-VCS-Repository: proj/portage X-VCS-Files: lib/portage/tests/util/futures/test_compat_coroutine.py lib/portage/util/futures/_sync_decorator.py X-VCS-Directories: lib/portage/util/futures/ lib/portage/tests/util/futures/ X-VCS-Committer: zmedico X-VCS-Committer-Name: Zac Medico X-VCS-Revision: de0b60ff277311e780102131dce3111b4db1c196 X-VCS-Branch: master Date: Mon, 24 Sep 2018 06:12:11 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 4c1c716b-d9e9-42e0-9663-8ae5e0f0ab5f X-Archives-Hash: 9971c777bcae6e47245f05664ecf75e9 commit: de0b60ff277311e780102131dce3111b4db1c196 Author: Zac Medico gentoo org> AuthorDate: Sat Jul 28 13:53:11 2018 +0000 Commit: Zac Medico gentoo org> CommitDate: Mon Sep 24 03:41:32 2018 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=de0b60ff Add _sync_decorator module Add functions that decorate coroutine methods and functions for synchronous usage, allowing coroutines to smoothly blend with synchronous code. This eliminates clutter that might otherwise discourage the proliferation of coroutine usage for I/O bound tasks. In the next commit, _sync_decorator will be used for smooth integration of new classes that have coroutine methods. Bug: https://bugs.gentoo.org/662070 Reviewed-by: Brian Dolbec gentoo.org> Signed-off-by: Zac Medico gentoo.org> .../tests/util/futures/test_compat_coroutine.py | 14 ++++++ lib/portage/util/futures/_sync_decorator.py | 54 ++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/lib/portage/tests/util/futures/test_compat_coroutine.py b/lib/portage/tests/util/futures/test_compat_coroutine.py index b6f75b1a2..f96aa9be5 100644 --- a/lib/portage/tests/util/futures/test_compat_coroutine.py +++ b/lib/portage/tests/util/futures/test_compat_coroutine.py @@ -6,6 +6,7 @@ from portage.util.futures.compat_coroutine import ( coroutine, coroutine_return, ) +from portage.util.futures._sync_decorator import _sync_decorator, _sync_methods from portage.tests import TestCase @@ -161,3 +162,16 @@ class CompatCoroutineTestCase(TestCase): loop.run_until_complete(asyncio.wait([writer, reader])) self.assertEqual(reader.result(), values) + + # Test decoration of coroutine methods and functions for + # synchronous usage, allowing coroutines to smoothly + # blend with synchronous code. + sync_cubby = _sync_methods(cubby, loop=loop) + sync_reader = _sync_decorator(reader_coroutine, loop=loop) + writer = asyncio.ensure_future(writer_coroutine(cubby, values, None), loop=loop) + self.assertEqual(sync_reader(cubby, None), values) + self.assertTrue(writer.done()) + + for i in range(3): + sync_cubby.write(i) + self.assertEqual(sync_cubby.read(), i) diff --git a/lib/portage/util/futures/_sync_decorator.py b/lib/portage/util/futures/_sync_decorator.py new file mode 100644 index 000000000..02a0963a7 --- /dev/null +++ b/lib/portage/util/futures/_sync_decorator.py @@ -0,0 +1,54 @@ +# Copyright 2018 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import functools + +import portage +portage.proxy.lazyimport.lazyimport(globals(), + 'portage.util.futures:asyncio', +) + + +def _sync_decorator(func, loop=None): + """ + Decorate an asynchronous function (either a corouting function or a + function that returns a Future) with a wrapper that runs the function + synchronously. + """ + loop = asyncio._wrap_loop(loop) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return loop.run_until_complete(func(*args, **kwargs)) + return wrapper + + +def _sync_methods(obj, loop=None): + """ + For use with synchronous code that needs to interact with an object + that has coroutine methods, this function generates a proxy which + conveniently converts coroutine methods into synchronous methods. + This allows coroutines to smoothly blend with synchronous + code, eliminating clutter that might otherwise discourage the + proliferation of coroutine usage for I/O bound tasks. + """ + loop = asyncio._wrap_loop(loop) + return _ObjectAttrWrapper(obj, + lambda attr: _sync_decorator(attr, loop=loop) + if asyncio.iscoroutinefunction(attr) else attr) + + +class _ObjectAttrWrapper(portage.proxy.objectproxy.ObjectProxy): + + __slots__ = ('_obj', '_attr_wrapper') + + def __init__(self, obj, attr_wrapper): + object.__setattr__(self, '_obj', obj) + object.__setattr__(self, '_attr_wrapper', attr_wrapper) + + def __getattribute__(self, attr): + obj = object.__getattribute__(self, '_obj') + attr_wrapper = object.__getattribute__(self, '_attr_wrapper') + return attr_wrapper(getattr(obj, attr)) + + def _get_target(self): + return object.__getattribute__(self, '_obj')