blob: b8d4aa509b4623e9020dfa56ca3d1771782e0e55 [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
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080034#include <algorithm>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080035#include <cassert>
36#include <cctype>
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080037#include <cstring>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080038#include <map>
39#include <ostream>
40#include <sstream>
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080041#include <vector>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080042
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +010043#include "src/compiler/generator_helpers.h"
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080044#include "src/compiler/python_generator.h"
45#include <google/protobuf/io/printer.h>
46#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
47#include <google/protobuf/descriptor.pb.h>
48#include <google/protobuf/descriptor.h>
49
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +010050using grpc_generator::StringReplace;
51using grpc_generator::StripProto;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080052using google::protobuf::Descriptor;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080053using google::protobuf::FileDescriptor;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080054using google::protobuf::MethodDescriptor;
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080055using google::protobuf::ServiceDescriptor;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080056using google::protobuf::io::Printer;
57using google::protobuf::io::StringOutputStream;
58using std::initializer_list;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080059using std::make_pair;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080060using std::map;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080061using std::pair;
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080062using std::replace;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080063using std::string;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080064using std::strlen;
65using std::vector;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080066
67namespace grpc_python_generator {
68namespace {
69//////////////////////////////////
70// BEGIN FORMATTING BOILERPLATE //
71//////////////////////////////////
72
73// Converts an initializer list of the form { key0, value0, key1, value1, ... }
74// into a map of key* to value*. Is merely a readability helper for later code.
75map<string, string> ListToDict(const initializer_list<string>& values) {
76 assert(values.size() % 2 == 0);
77 map<string, string> value_map;
78 auto value_iter = values.begin();
79 for (unsigned i = 0; i < values.size()/2; ++i) {
80 string key = *value_iter;
81 ++value_iter;
82 string value = *value_iter;
83 value_map[key] = value;
84 ++value_iter;
85 }
86 return value_map;
87}
88
89// Provides RAII indentation handling. Use as:
90// {
91// IndentScope raii_my_indent_var_name_here(my_py_printer);
92// // constructor indented my_py_printer
93// ...
94// // destructor called at end of scope, un-indenting my_py_printer
95// }
96class IndentScope {
97 public:
98 explicit IndentScope(Printer* printer) : printer_(printer) {
99 printer_->Indent();
100 }
101
102 ~IndentScope() {
103 printer_->Outdent();
104 }
105
106 private:
107 Printer* printer_;
108};
109
110////////////////////////////////
111// END FORMATTING BOILERPLATE //
112////////////////////////////////
113
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800114bool PrintServicer(const ServiceDescriptor* service,
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800115 Printer* out) {
116 string doc = "<fill me in later!>";
117 map<string, string> dict = ListToDict({
118 "Service", service->name(),
119 "Documentation", doc,
120 });
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800121 out->Print(dict, "class EarlyAdopter$Service$Servicer(object):\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800122 {
123 IndentScope raii_class_indent(out);
124 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800125 out->Print("__metaclass__ = abc.ABCMeta\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800126 for (int i = 0; i < service->method_count(); ++i) {
127 auto meth = service->method(i);
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800128 string arg_name = meth->client_streaming() ?
129 "request_iterator" : "request";
130 out->Print("@abc.abstractmethod\n");
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -0800131 out->Print("def $Method$(self, $ArgName$, context):\n",
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800132 "Method", meth->name(), "ArgName", arg_name);
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800133 {
134 IndentScope raii_method_indent(out);
135 out->Print("raise NotImplementedError()\n");
136 }
137 }
138 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800139 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800140}
141
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800142bool PrintServer(const ServiceDescriptor* service, Printer* out) {
143 string doc = "<fill me in later!>";
144 map<string, string> dict = ListToDict({
145 "Service", service->name(),
146 "Documentation", doc,
147 });
148 out->Print(dict, "class EarlyAdopter$Service$Server(object):\n");
149 {
150 IndentScope raii_class_indent(out);
151 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
152 out->Print("__metaclass__ = abc.ABCMeta\n");
153 out->Print("@abc.abstractmethod\n");
154 out->Print("def start(self):\n");
155 {
156 IndentScope raii_method_indent(out);
157 out->Print("raise NotImplementedError()\n");
158 }
159
160 out->Print("@abc.abstractmethod\n");
161 out->Print("def stop(self):\n");
162 {
163 IndentScope raii_method_indent(out);
164 out->Print("raise NotImplementedError()\n");
165 }
166 }
167 return true;
168}
169
170bool PrintStub(const ServiceDescriptor* service,
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800171 Printer* out) {
172 string doc = "<fill me in later!>";
173 map<string, string> dict = ListToDict({
174 "Service", service->name(),
175 "Documentation", doc,
176 });
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800177 out->Print(dict, "class EarlyAdopter$Service$Stub(object):\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800178 {
179 IndentScope raii_class_indent(out);
180 out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800181 out->Print("__metaclass__ = abc.ABCMeta\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800182 for (int i = 0; i < service->method_count(); ++i) {
183 const MethodDescriptor* meth = service->method(i);
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800184 string arg_name = meth->client_streaming() ?
185 "request_iterator" : "request";
186 auto methdict = ListToDict({"Method", meth->name(), "ArgName", arg_name});
187 out->Print("@abc.abstractmethod\n");
188 out->Print(methdict, "def $Method$(self, $ArgName$):\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800189 {
190 IndentScope raii_method_indent(out);
191 out->Print("raise NotImplementedError()\n");
192 }
193 out->Print(methdict, "$Method$.async = None\n");
194 }
195 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800196 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800197}
198
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -0800199// TODO(protobuf team): Export `ModuleName` from protobuf's
200// `src/google/protobuf/compiler/python/python_generator.cc` file.
201string ModuleName(const string& filename) {
202 string basename = StripProto(filename);
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +0100203 basename = StringReplace(basename, "-", "_");
204 basename = StringReplace(basename, "/", ".");
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -0800205 return basename + "_pb2";
206}
207
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800208bool GetModuleAndMessagePath(const Descriptor* type,
209 pair<string, string>* out) {
210 const Descriptor* path_elem_type = type;
211 vector<const Descriptor*> message_path;
212 do {
213 message_path.push_back(path_elem_type);
214 path_elem_type = path_elem_type->containing_type();
215 } while (path_elem_type != nullptr);
216 string file_name = type->file()->name();
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800217 static const int proto_suffix_length = strlen(".proto");
218 if (!(file_name.size() > static_cast<size_t>(proto_suffix_length) &&
219 file_name.find_last_of(".proto") == file_name.size() - 1)) {
220 return false;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800221 }
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -0800222 string module = ModuleName(file_name);
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800223 string message_type;
224 for (auto path_iter = message_path.rbegin();
225 path_iter != message_path.rend(); ++path_iter) {
226 message_type += (*path_iter)->name() + ".";
227 }
Craig Tillercf133f42015-02-26 14:05:56 -0800228 // no pop_back prior to C++11
229 message_type.resize(message_type.size() - 1);
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800230 *out = make_pair(module, message_type);
231 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800232}
233
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800234bool PrintServerFactory(const ServiceDescriptor* service, Printer* out) {
235 out->Print("def early_adopter_create_$Service$_server(servicer, port, "
236 "root_certificates, key_chain_pairs):\n",
237 "Service", service->name());
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800238 {
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800239 IndentScope raii_create_server_indent(out);
240 map<string, pair<string, string>> method_to_module_and_message;
241 out->Print("method_implementations = {\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800242 for (int i = 0; i < service->method_count(); ++i) {
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800243 IndentScope raii_implementations_indent(out);
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800244 const MethodDescriptor* meth = service->method(i);
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800245 string meth_type =
246 string(meth->client_streaming() ? "stream" : "unary") +
247 string(meth->server_streaming() ? "_stream" : "_unary") + "_inline";
248 out->Print("\"$Method$\": utilities.$Type$(servicer.$Method$),\n",
249 "Method", meth->name(),
250 "Type", meth_type);
251 // Maintain information on the input type of the service method for later
252 // use in constructing the service assembly's activated fore link.
253 const Descriptor* input_type = meth->input_type();
254 pair<string, string> module_and_message;
255 if (!GetModuleAndMessagePath(input_type, &module_and_message)) {
256 return false;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800257 }
Nicolas "Pixel" Noblee6d72c22015-02-25 22:16:45 +0100258 method_to_module_and_message.insert(
259 make_pair(meth->name(), module_and_message));
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800260 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800261 out->Print("}\n");
262 // Ensure that we've imported all of the relevant messages.
263 for (auto& meth_vals : method_to_module_and_message) {
264 out->Print("import $Module$\n",
265 "Module", meth_vals.second.first);
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800266 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800267 out->Print("request_deserializers = {\n");
268 for (auto& meth_vals : method_to_module_and_message) {
269 IndentScope raii_serializers_indent(out);
270 string full_input_type_path = meth_vals.second.first + "." +
271 meth_vals.second.second;
272 out->Print("\"$Method$\": $Type$.FromString,\n",
273 "Method", meth_vals.first,
274 "Type", full_input_type_path);
275 }
276 out->Print("}\n");
277 out->Print("response_serializers = {\n");
278 for (auto& meth_vals : method_to_module_and_message) {
279 IndentScope raii_serializers_indent(out);
280 out->Print("\"$Method$\": lambda x: x.SerializeToString(),\n",
281 "Method", meth_vals.first);
282 }
283 out->Print("}\n");
284 out->Print("link = fore.activated_fore_link(port, request_deserializers, "
285 "response_serializers, root_certificates, key_chain_pairs)\n");
286 out->Print("return implementations.assemble_service("
287 "method_implementations, link)\n");
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800288 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800289 return true;
290}
291
292bool PrintStubFactory(const ServiceDescriptor* service, Printer* out) {
293 map<string, string> dict = ListToDict({
294 "Service", service->name(),
295 });
296 out->Print(dict, "def early_adopter_create_$Service$_stub(host, port):\n");
297 {
298 IndentScope raii_create_server_indent(out);
299 map<string, pair<string, string>> method_to_module_and_message;
300 out->Print("method_implementations = {\n");
301 for (int i = 0; i < service->method_count(); ++i) {
302 IndentScope raii_implementations_indent(out);
303 const MethodDescriptor* meth = service->method(i);
304 string meth_type =
305 string(meth->client_streaming() ? "stream" : "unary") +
306 string(meth->server_streaming() ? "_stream" : "_unary") + "_inline";
307 // TODO(atash): once the expected input to assemble_dynamic_inline_stub is
308 // cleaned up, change this to the expected argument's dictionary values.
309 out->Print("\"$Method$\": utilities.$Type$(None),\n",
310 "Method", meth->name(),
311 "Type", meth_type);
312 // Maintain information on the input type of the service method for later
313 // use in constructing the service assembly's activated fore link.
314 const Descriptor* output_type = meth->output_type();
315 pair<string, string> module_and_message;
316 if (!GetModuleAndMessagePath(output_type, &module_and_message)) {
317 return false;
318 }
Nicolas "Pixel" Noblee6d72c22015-02-25 22:16:45 +0100319 method_to_module_and_message.insert(
320 make_pair(meth->name(), module_and_message));
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800321 }
322 out->Print("}\n");
323 // Ensure that we've imported all of the relevant messages.
324 for (auto& meth_vals : method_to_module_and_message) {
325 out->Print("import $Module$\n",
326 "Module", meth_vals.second.first);
327 }
328 out->Print("response_deserializers = {\n");
329 for (auto& meth_vals : method_to_module_and_message) {
330 IndentScope raii_serializers_indent(out);
331 string full_output_type_path = meth_vals.second.first + "." +
332 meth_vals.second.second;
333 out->Print("\"$Method$\": $Type$.FromString,\n",
334 "Method", meth_vals.first,
335 "Type", full_output_type_path);
336 }
337 out->Print("}\n");
338 out->Print("request_serializers = {\n");
339 for (auto& meth_vals : method_to_module_and_message) {
340 IndentScope raii_serializers_indent(out);
341 out->Print("\"$Method$\": lambda x: x.SerializeToString(),\n",
342 "Method", meth_vals.first);
343 }
344 out->Print("}\n");
345 out->Print("link = rear.activated_rear_link("
346 "host, port, request_serializers, response_deserializers)\n");
347 out->Print("return implementations.assemble_dynamic_inline_stub("
348 "method_implementations, link)\n");
349 }
350 return true;
351}
352
353bool PrintPreamble(const FileDescriptor* file, Printer* out) {
354 out->Print("import abc\n");
355 out->Print("from grpc._adapter import fore\n");
356 out->Print("from grpc._adapter import rear\n");
357 out->Print("from grpc.framework.assembly import implementations\n");
358 out->Print("from grpc.framework.assembly import utilities\n");
359 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800360}
361
362} // namespace
363
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800364pair<bool, string> GetServices(const FileDescriptor* file) {
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800365 string output;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800366 {
367 // Scope the output stream so it closes and finalizes output to the string.
368 StringOutputStream output_stream(&output);
369 Printer out(&output_stream, '$');
370 if (!PrintPreamble(file, &out)) {
371 return make_pair(false, "");
372 }
373 for (int i = 0; i < file->service_count(); ++i) {
374 auto service = file->service(i);
375 if (!(PrintServicer(service, &out) &&
376 PrintServer(service, &out) &&
377 PrintStub(service, &out) &&
378 PrintServerFactory(service, &out) &&
379 PrintStubFactory(service, &out))) {
380 return make_pair(false, "");
381 }
382 }
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800383 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800384 return make_pair(true, std::move(output));
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800385}
386
387} // namespace grpc_python_generator