Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/futures/, lib/portage/util/futures/
Date: Mon, 24 Sep 2018 06:12:18
Message-Id: 1537760492.de0b60ff277311e780102131dce3111b4db1c196.zmedico@gentoo
1 commit: de0b60ff277311e780102131dce3111b4db1c196
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Sat Jul 28 13:53:11 2018 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Mon Sep 24 03:41:32 2018 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=de0b60ff
7
8 Add _sync_decorator module
9
10 Add functions that decorate coroutine methods and functions for
11 synchronous usage, allowing coroutines to smoothly blend with
12 synchronous code. This eliminates clutter that might otherwise
13 discourage the proliferation of coroutine usage for I/O bound tasks.
14
15 In the next commit, _sync_decorator will be used for smooth
16 integration of new classes that have coroutine methods.
17
18 Bug: https://bugs.gentoo.org/662070
19 Reviewed-by: Brian Dolbec <dolsen <AT> gentoo.org>
20 Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>
21
22 .../tests/util/futures/test_compat_coroutine.py | 14 ++++++
23 lib/portage/util/futures/_sync_decorator.py | 54 ++++++++++++++++++++++
24 2 files changed, 68 insertions(+)
25
26 diff --git a/lib/portage/tests/util/futures/test_compat_coroutine.py b/lib/portage/tests/util/futures/test_compat_coroutine.py
27 index b6f75b1a2..f96aa9be5 100644
28 --- a/lib/portage/tests/util/futures/test_compat_coroutine.py
29 +++ b/lib/portage/tests/util/futures/test_compat_coroutine.py
30 @@ -6,6 +6,7 @@ from portage.util.futures.compat_coroutine import (
31 coroutine,
32 coroutine_return,
33 )
34 +from portage.util.futures._sync_decorator import _sync_decorator, _sync_methods
35 from portage.tests import TestCase
36
37
38 @@ -161,3 +162,16 @@ class CompatCoroutineTestCase(TestCase):
39 loop.run_until_complete(asyncio.wait([writer, reader]))
40
41 self.assertEqual(reader.result(), values)
42 +
43 + # Test decoration of coroutine methods and functions for
44 + # synchronous usage, allowing coroutines to smoothly
45 + # blend with synchronous code.
46 + sync_cubby = _sync_methods(cubby, loop=loop)
47 + sync_reader = _sync_decorator(reader_coroutine, loop=loop)
48 + writer = asyncio.ensure_future(writer_coroutine(cubby, values, None), loop=loop)
49 + self.assertEqual(sync_reader(cubby, None), values)
50 + self.assertTrue(writer.done())
51 +
52 + for i in range(3):
53 + sync_cubby.write(i)
54 + self.assertEqual(sync_cubby.read(), i)
55
56 diff --git a/lib/portage/util/futures/_sync_decorator.py b/lib/portage/util/futures/_sync_decorator.py
57 new file mode 100644
58 index 000000000..02a0963a7
59 --- /dev/null
60 +++ b/lib/portage/util/futures/_sync_decorator.py
61 @@ -0,0 +1,54 @@
62 +# Copyright 2018 Gentoo Foundation
63 +# Distributed under the terms of the GNU General Public License v2
64 +
65 +import functools
66 +
67 +import portage
68 +portage.proxy.lazyimport.lazyimport(globals(),
69 + 'portage.util.futures:asyncio',
70 +)
71 +
72 +
73 +def _sync_decorator(func, loop=None):
74 + """
75 + Decorate an asynchronous function (either a corouting function or a
76 + function that returns a Future) with a wrapper that runs the function
77 + synchronously.
78 + """
79 + loop = asyncio._wrap_loop(loop)
80 + @functools.wraps(func)
81 + def wrapper(*args, **kwargs):
82 + return loop.run_until_complete(func(*args, **kwargs))
83 + return wrapper
84 +
85 +
86 +def _sync_methods(obj, loop=None):
87 + """
88 + For use with synchronous code that needs to interact with an object
89 + that has coroutine methods, this function generates a proxy which
90 + conveniently converts coroutine methods into synchronous methods.
91 + This allows coroutines to smoothly blend with synchronous
92 + code, eliminating clutter that might otherwise discourage the
93 + proliferation of coroutine usage for I/O bound tasks.
94 + """
95 + loop = asyncio._wrap_loop(loop)
96 + return _ObjectAttrWrapper(obj,
97 + lambda attr: _sync_decorator(attr, loop=loop)
98 + if asyncio.iscoroutinefunction(attr) else attr)
99 +
100 +
101 +class _ObjectAttrWrapper(portage.proxy.objectproxy.ObjectProxy):
102 +
103 + __slots__ = ('_obj', '_attr_wrapper')
104 +
105 + def __init__(self, obj, attr_wrapper):
106 + object.__setattr__(self, '_obj', obj)
107 + object.__setattr__(self, '_attr_wrapper', attr_wrapper)
108 +
109 + def __getattribute__(self, attr):
110 + obj = object.__getattribute__(self, '_obj')
111 + attr_wrapper = object.__getattribute__(self, '_attr_wrapper')
112 + return attr_wrapper(getattr(obj, attr))
113 +
114 + def _get_target(self):
115 + return object.__getattribute__(self, '_obj')