blob: 48d90624d622bfe8b8f9e109aa4ce99c7b8e2025 [file] [log] [blame]
Masood Malekghassemif8e297a2015-02-19 15:39:32 -08001/*
2 *
3 * Copyright 2015, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <cassert>
35#include <cctype>
36#include <map>
37#include <ostream>
38#include <sstream>
39
40#include "src/compiler/python_generator.h"
41#include <google/protobuf/io/printer.h>
42#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
43#include <google/protobuf/descriptor.pb.h>
44#include <google/protobuf/descriptor.h>
45
46using google::protobuf::FileDescriptor;
47using google::protobuf::ServiceDescriptor;
48using google::protobuf::MethodDescriptor;
49using google::protobuf::io::Printer;
50using google::protobuf::io::StringOutputStream;
51using std::initializer_list;
52using std::map;
53using std::string;
54
55namespace grpc_python_generator {
56namespace {
57//////////////////////////////////
58// BEGIN FORMATTING BOILERPLATE //
59//////////////////////////////////
60
61// Converts an initializer list of the form { key0, value0, key1, value1, ... }
62// into a map of key* to value*. Is merely a readability helper for later code.
63map<string, string> ListToDict(const initializer_list<string>& values) {
64 assert(values.size() % 2 == 0);
65 map<string, string> value_map;
66 auto value_iter = values.begin();
67 for (unsigned i = 0; i < values.size()/2; ++i) {
68 string key = *value_iter;
69 ++value_iter;
70 string value = *value_iter;
71 value_map[key] = value;
72 ++value_iter;
73 }
74 return value_map;
75}
76
77// Provides RAII indentation handling. Use as:
78// {
79// IndentScope raii_my_indent_var_name_here(my_py_printer);
80// // constructor indented my_py_printer
81// ...
82// // destructor called at end of scope, un-indenting my_py_printer
83// }
84class IndentScope {
85 public:
86 explicit IndentScope(Printer* printer) : printer_(printer) {
87 printer_->Indent();
88 }
89
90 ~IndentScope() {
91 printer_->Outdent();
92 }
93
94 private:
95 Printer* printer_;
96};
97
98////////////////////////////////
99// END FORMATTING BOILERPLATE //
100////////////////////////////////
101
102void PrintService(const ServiceDescriptor* service,
103 Printer* out) {
104 string doc = "<fill me in later!>";
105 map<string, string> dict = ListToDict({
106 "Service", service->name(),
107 "Documentation", doc,
108 });
109 out->Print(dict, "class $Service$Service(object):\n");
110 {
111 IndentScope raii_class_indent(out);
112 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
113 out->Print("def __init__(self):\n");
114 {
115 IndentScope raii_method_indent(out);
116 out->Print("pass\n");
117 }
118 }
119}
120
121void PrintServicer(const ServiceDescriptor* service,
122 Printer* out) {
123 string doc = "<fill me in later!>";
124 map<string, string> dict = ListToDict({
125 "Service", service->name(),
126 "Documentation", doc,
127 });
128 out->Print(dict, "class $Service$Servicer(object):\n");
129 {
130 IndentScope raii_class_indent(out);
131 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
132 for (int i = 0; i < service->method_count(); ++i) {
133 auto meth = service->method(i);
134 out->Print("def $Method$(self, arg):\n", "Method", meth->name());
135 {
136 IndentScope raii_method_indent(out);
137 out->Print("raise NotImplementedError()\n");
138 }
139 }
140 }
141}
142
143void PrintStub(const ServiceDescriptor* service,
144 Printer* out) {
145 string doc = "<fill me in later!>";
146 map<string, string> dict = ListToDict({
147 "Service", service->name(),
148 "Documentation", doc,
149 });
150 out->Print(dict, "class $Service$Stub(object):\n");
151 {
152 IndentScope raii_class_indent(out);
153 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
154 for (int i = 0; i < service->method_count(); ++i) {
155 const MethodDescriptor* meth = service->method(i);
156 auto methdict = ListToDict({"Method", meth->name()});
157 out->Print(methdict, "def $Method$(self, arg):\n");
158 {
159 IndentScope raii_method_indent(out);
160 out->Print("raise NotImplementedError()\n");
161 }
162 out->Print(methdict, "$Method$.async = None\n");
163 }
164 }
165}
166
167void PrintStubImpl(const ServiceDescriptor* service,
168 Printer* out) {
169 map<string, string> dict = ListToDict({
170 "Service", service->name(),
171 });
172 out->Print(dict, "class _$Service$Stub($Service$Stub):\n");
173 {
174 IndentScope raii_class_indent(out);
175 out->Print("def __init__(self, face_stub, default_timeout):\n");
176 {
177 IndentScope raii_method_indent(out);
178 out->Print("self._face_stub = face_stub\n"
179 "self._default_timeout = default_timeout\n"
180 "stub_self = self\n");
181
182 for (int i = 0; i < service->method_count(); ++i) {
183 const MethodDescriptor* meth = service->method(i);
184 bool server_streaming = meth->server_streaming();
185 bool client_streaming = meth->client_streaming();
186 std::string blocking_call, future_call;
187 if (server_streaming) {
188 if (client_streaming) {
189 blocking_call = "stub_self._face_stub.inline_stream_in_stream_out";
190 future_call = blocking_call;
191 } else {
192 blocking_call = "stub_self._face_stub.inline_value_in_stream_out";
193 future_call = blocking_call;
194 }
195 } else {
196 if (client_streaming) {
197 blocking_call = "stub_self._face_stub.blocking_stream_in_value_out";
198 future_call = "stub_self._face_stub.future_stream_in_value_out";
199 } else {
200 blocking_call = "stub_self._face_stub.blocking_value_in_value_out";
201 future_call = "stub_self._face_stub.future_value_in_value_out";
202 }
203 }
204 // TODO(atash): use the solution described at
205 // http://stackoverflow.com/a/2982 to bind 'async' attribute
206 // functions to def'd functions instead of using callable attributes.
207 auto methdict = ListToDict({
208 "Method", meth->name(),
209 "BlockingCall", blocking_call,
210 "FutureCall", future_call
211 });
212 out->Print(methdict, "class $Method$(object):\n");
213 {
214 IndentScope raii_callable_indent(out);
215 out->Print("def __call__(self, arg):\n");
216 {
217 IndentScope raii_callable_call_indent(out);
218 out->Print(methdict,
219 "return $BlockingCall$(\"$Method$\", arg, "
220 "stub_self._default_timeout)\n");
221 }
222 out->Print("def async(self, arg):\n");
223 {
224 IndentScope raii_callable_async_indent(out);
225 out->Print(methdict,
226 "return $FutureCall$(\"$Method$\", arg, "
227 "stub_self._default_timeout)\n");
228 }
229 }
230 out->Print(methdict, "self.$Method$ = $Method$()\n");
231 }
232 }
233 }
234}
235
236void PrintStubGenerators(const ServiceDescriptor* service, Printer* out) {
237 map<string, string> dict = ListToDict({
238 "Service", service->name(),
239 });
240 // Write out a generator of linked pairs of Server/Stub
241 out->Print(dict, "def mock_$Service$(servicer, default_timeout):\n");
242 {
243 IndentScope raii_mock_indent(out);
244 out->Print("value_in_value_out = {}\n"
245 "value_in_stream_out = {}\n"
246 "stream_in_value_out = {}\n"
247 "stream_in_stream_out = {}\n");
248 for (int i = 0; i < service->method_count(); ++i) {
249 const MethodDescriptor* meth = service->method(i);
250 std::string super_interface, meth_dict;
251 bool server_streaming = meth->server_streaming();
252 bool client_streaming = meth->client_streaming();
253 if (server_streaming) {
254 if (client_streaming) {
255 super_interface = "InlineStreamInStreamOutMethod";
256 meth_dict = "stream_in_stream_out";
257 } else {
258 super_interface = "InlineValueInStreamOutMethod";
259 meth_dict = "value_in_stream_out";
260 }
261 } else {
262 if (client_streaming) {
263 super_interface = "InlineStreamInValueOutMethod";
264 meth_dict = "stream_in_value_out";
265 } else {
266 super_interface = "InlineValueInValueOutMethod";
267 meth_dict = "value_in_value_out";
268 }
269 }
270 map<string, string> methdict = ListToDict({
271 "Method", meth->name(),
272 "SuperInterface", super_interface,
273 "MethodDict", meth_dict
274 });
275 out->Print(
276 methdict, "class $Method$(_face_interfaces.$SuperInterface$):\n");
277 {
278 IndentScope raii_inline_class_indent(out);
279 out->Print("def service(self, request, context):\n");
280 {
281 IndentScope raii_inline_class_fn_indent(out);
282 out->Print(methdict, "return servicer.$Method$(request)\n");
283 }
284 }
285 out->Print(methdict, "$MethodDict$['$Method$'] = $Method$()\n");
286 }
287 out->Print(
288 "face_linked_pair = _face_testing.server_and_stub(default_timeout,"
289 "inline_value_in_value_out_methods=value_in_value_out,"
290 "inline_value_in_stream_out_methods=value_in_stream_out,"
291 "inline_stream_in_value_out_methods=stream_in_value_out,"
292 "inline_stream_in_stream_out_methods=stream_in_stream_out)\n");
293 out->Print("class LinkedPair(object):\n");
294 {
295 IndentScope raii_linked_pair(out);
296 out->Print("def __init__(self, server, stub):\n");
297 {
298 IndentScope raii_linked_pair_init(out);
299 out->Print("self.server = server\n"
300 "self.stub = stub\n");
301 }
302 }
303 out->Print(
304 dict,
305 "stub = _$Service$Stub(face_linked_pair.stub, default_timeout)\n");
306 out->Print("return LinkedPair(None, stub)\n");
307 }
308}
309
310} // namespace
311
312string GetServices(const FileDescriptor* file) {
313 string output;
314 StringOutputStream output_stream(&output);
315 Printer out(&output_stream, '$');
316 out.Print("import abc\n");
317 out.Print("import google3\n");
318 out.Print("from grpc.framework.face import demonstration as _face_testing\n");
319 out.Print("from grpc.framework.face import interfaces as _face_interfaces\n");
320
321 for (int i = 0; i < file->service_count(); ++i) {
322 auto service = file->service(i);
323 PrintService(service, &out);
324 PrintServicer(service, &out);
325 PrintStub(service, &out);
326 PrintStubImpl(service, &out);
327 PrintStubGenerators(service, &out);
328 }
329 return output;
330}
331
332} // namespace grpc_python_generator