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') |