Gentoo Archives: gentoo-portage-dev

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