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 |