blob: 270291ac33e2632bd74bdd4ddf8692454b9d2fca [file] [log] [blame]
Masood Malekghassemif8e297a2015-02-19 15:39:32 -08001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Masood Malekghassemif8e297a2015-02-19 15:39:32 -08004 * 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 Malekghassemid9539592016-10-20 14:47:14 -070038#include <fstream>
39#include <iostream>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080040#include <map>
Masood Malekghassemi3bb52152015-03-17 21:52:52 -070041#include <memory>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080042#include <ostream>
43#include <sstream>
Masood Malekghassemi3bb52152015-03-17 21:52:52 -070044#include <tuple>
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080045#include <vector>
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080046
Masood Malekghassemi65c803b2015-03-20 06:48:47 -070047#include "src/compiler/config.h"
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +010048#include "src/compiler/generator_helpers.h"
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080049#include "src/compiler/python_generator.h"
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080050
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +010051using grpc_generator::StringReplace;
52using grpc_generator::StripProto;
Masood Malekghassemi65c803b2015-03-20 06:48:47 -070053using grpc::protobuf::Descriptor;
54using grpc::protobuf::FileDescriptor;
55using grpc::protobuf::MethodDescriptor;
56using grpc::protobuf::ServiceDescriptor;
57using grpc::protobuf::compiler::GeneratorContext;
58using grpc::protobuf::io::CodedOutputStream;
59using grpc::protobuf::io::Printer;
60using grpc::protobuf::io::StringOutputStream;
61using grpc::protobuf::io::ZeroCopyOutputStream;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080062using std::initializer_list;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080063using std::make_pair;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080064using std::map;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080065using std::pair;
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080066using std::replace;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -080067using std::vector;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080068
69namespace grpc_python_generator {
Masood Malekghassemi3bb52152015-03-17 21:52:52 -070070
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080071namespace {
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080072
73// Provides RAII indentation handling. Use as:
74// {
75// IndentScope raii_my_indent_var_name_here(my_py_printer);
76// // constructor indented my_py_printer
77// ...
78// // destructor called at end of scope, un-indenting my_py_printer
79// }
80class IndentScope {
81 public:
82 explicit IndentScope(Printer* printer) : printer_(printer) {
83 printer_->Indent();
84 }
85
Masood Malekghassemiac592452016-07-01 11:58:04 -070086 ~IndentScope() { printer_->Outdent(); }
Masood Malekghassemif8e297a2015-02-19 15:39:32 -080087
88 private:
89 Printer* printer_;
90};
91
Ken Paysonbe187b02016-05-06 15:32:58 -070092// TODO(https://github.com/google/protobuf/issues/888):
93// Export `ModuleName` from protobuf's
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080094// `src/google/protobuf/compiler/python/python_generator.cc` file.
Masood Malekghassemi65c803b2015-03-20 06:48:47 -070095grpc::string ModuleName(const grpc::string& filename) {
96 grpc::string basename = StripProto(filename);
Nicolas "Pixel" Noble93fa0982015-02-27 21:50:58 +010097 basename = StringReplace(basename, "-", "_");
98 basename = StringReplace(basename, "/", ".");
Masood Malekghassemi40e8cbd2015-02-26 08:39:50 -080099 return basename + "_pb2";
100}
101
Ken Paysonbe187b02016-05-06 15:32:58 -0700102// TODO(https://github.com/google/protobuf/issues/888):
103// Export `ModuleAlias` from protobuf's
104// `src/google/protobuf/compiler/python/python_generator.cc` file.
105grpc::string ModuleAlias(const grpc::string& filename) {
106 grpc::string module_name = ModuleName(filename);
107 // We can't have dots in the module name, so we replace each with _dot_.
108 // But that could lead to a collision between a.b and a_dot_b, so we also
109 // duplicate each underscore.
110 module_name = StringReplace(module_name, "_", "__");
111 module_name = StringReplace(module_name, ".", "_dot_");
112 return module_name;
113}
114
Masood Malekghassemid9539592016-10-20 14:47:14 -0700115// Tucks all generator state in an anonymous namespace away from
116// PythonGrpcGenerator and the header file, mostly to encourage future changes
117// to not require updates to the grpcio-tools C++ code part. Assumes that it is
118// only ever used from a single thread.
119struct PrivateGenerator {
120 const GeneratorConfiguration& config;
121 const FileDescriptor *file;
122
123 bool generate_in_pb2_grpc;
124
125 Printer *out;
126
127 PrivateGenerator(const GeneratorConfiguration& config,
128 const FileDescriptor *file);
129
130 std::pair<bool, grpc::string> GetGrpcServices();
131
132 private:
133 bool PrintPreamble();
134 bool PrintBetaPreamble();
135 bool PrintGAServices();
136 bool PrintBetaServices();
137
138 bool PrintAddServicerToServer(
139 const grpc::string& package_qualified_service_name,
140 const ServiceDescriptor* service);
141 bool PrintServicer(const ServiceDescriptor* service);
142 bool PrintStub(const grpc::string& package_qualified_service_name,
143 const ServiceDescriptor* service);
144
145 bool PrintBetaServicer(const ServiceDescriptor* service);
146 bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name,
147 const ServiceDescriptor* service);
148 bool PrintBetaStub(const ServiceDescriptor* service);
149 bool PrintBetaStubFactory(
150 const grpc::string& package_qualified_service_name,
151 const ServiceDescriptor* service);
152
153 // Get all comments (leading, leading_detached, trailing) and print them as a
154 // docstring. Any leading space of a line will be removed, but the line
155 // wrapping will not be changed.
156 template <typename DescriptorType>
157 void PrintAllComments(const DescriptorType* descriptor);
158
159 bool GetModuleAndMessagePath(const Descriptor* type,
160 grpc::string* out);
161};
162
163
164PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
165 const FileDescriptor *file)
166 : config(config), file(file) {}
167
168bool PrivateGenerator::GetModuleAndMessagePath(const Descriptor* type,
169 grpc::string* out) {
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800170 const Descriptor* path_elem_type = type;
171 vector<const Descriptor*> message_path;
172 do {
173 message_path.push_back(path_elem_type);
174 path_elem_type = path_elem_type->containing_type();
Masood Malekghassemiac592452016-07-01 11:58:04 -0700175 } while (path_elem_type); // implicit nullptr comparison; don't be explicit
Masood Malekghassemi65c803b2015-03-20 06:48:47 -0700176 grpc::string file_name = type->file()->name();
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800177 static const int proto_suffix_length = strlen(".proto");
178 if (!(file_name.size() > static_cast<size_t>(proto_suffix_length) &&
179 file_name.find_last_of(".proto") == file_name.size() - 1)) {
180 return false;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800181 }
Masood Malekghassemid9539592016-10-20 14:47:14 -0700182 grpc::string generator_file_name = file->name();
183 grpc::string module;
184 if (generator_file_name != file_name || generate_in_pb2_grpc) {
185 module = ModuleAlias(file_name) + ".";
186 } else {
187 module = "";
188 }
Masood Malekghassemi65c803b2015-03-20 06:48:47 -0700189 grpc::string message_type;
Masood Malekghassemiac592452016-07-01 11:58:04 -0700190 for (auto path_iter = message_path.rbegin(); path_iter != message_path.rend();
191 ++path_iter) {
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800192 message_type += (*path_iter)->name() + ".";
193 }
Craig Tillercf133f42015-02-26 14:05:56 -0800194 // no pop_back prior to C++11
195 message_type.resize(message_type.size() - 1);
Ken Paysonbe187b02016-05-06 15:32:58 -0700196 *out = module + message_type;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800197 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800198}
199
yang-gb0de7162016-05-03 15:48:19 -0700200template <typename DescriptorType>
Masood Malekghassemid9539592016-10-20 14:47:14 -0700201void PrivateGenerator::PrintAllComments(const DescriptorType* descriptor) {
202 vector<grpc::string> comments;
203 grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
yang-gb0de7162016-05-03 15:48:19 -0700204 &comments);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700205 grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_LEADING,
yang-gb0de7162016-05-03 15:48:19 -0700206 &comments);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700207 grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_TRAILING,
yang-gb0de7162016-05-03 15:48:19 -0700208 &comments);
209 if (comments.empty()) {
210 return;
211 }
Masood Malekghassemid9539592016-10-20 14:47:14 -0700212 out->Print("\"\"\"");
yang-gb0de7162016-05-03 15:48:19 -0700213 for (auto it = comments.begin(); it != comments.end(); ++it) {
214 size_t start_pos = it->find_first_not_of(' ');
215 if (start_pos != grpc::string::npos) {
Masood Malekghassemid9539592016-10-20 14:47:14 -0700216 out->Print(it->c_str() + start_pos);
yang-gb0de7162016-05-03 15:48:19 -0700217 }
Masood Malekghassemid9539592016-10-20 14:47:14 -0700218 out->Print("\n");
yang-gb0de7162016-05-03 15:48:19 -0700219 }
Masood Malekghassemid9539592016-10-20 14:47:14 -0700220 out->Print("\"\"\"\n");
yang-gb0de7162016-05-03 15:48:19 -0700221}
222
Masood Malekghassemid9539592016-10-20 14:47:14 -0700223bool PrivateGenerator::PrintBetaServicer(const ServiceDescriptor* service) {
Ken Paysonbe187b02016-05-06 15:32:58 -0700224 out->Print("\n\n");
yang-gb0de7162016-05-03 15:48:19 -0700225 out->Print("class Beta$Service$Servicer(object):\n", "Service",
226 service->name());
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000227 {
228 IndentScope raii_class_indent(out);
Nicolas "Pixel" Noblebeeeddd2016-09-29 20:27:23 +0200229 out->Print(
230 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
231 "\nIt is recommended to use the GA API (classes and functions in this\n"
232 "file not marked beta) for all further purposes. This class was "
233 "generated\n"
234 "only to ease transition from grpcio<0.15.0 to "
235 "grpcio>=0.15.0.\"\"\"\n");
Masood Malekghassemid9539592016-10-20 14:47:14 -0700236 PrintAllComments(service);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000237 for (int i = 0; i < service->method_count(); ++i) {
238 auto meth = service->method(i);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700239 grpc::string arg_name =
240 meth->client_streaming() ? "request_iterator" : "request";
241 out->Print("def $Method$(self, $ArgName$, context):\n", "Method",
242 meth->name(), "ArgName", arg_name);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000243 {
244 IndentScope raii_method_indent(out);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700245 PrintAllComments(meth);
Masood Malekghassemi832ae812016-04-27 18:38:54 -0700246 out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000247 }
248 }
249 }
250 return true;
251}
252
Masood Malekghassemid9539592016-10-20 14:47:14 -0700253bool PrivateGenerator::PrintBetaStub(const ServiceDescriptor* service) {
Ken Paysonbe187b02016-05-06 15:32:58 -0700254 out->Print("\n\n");
yang-gb0de7162016-05-03 15:48:19 -0700255 out->Print("class Beta$Service$Stub(object):\n", "Service", service->name());
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000256 {
257 IndentScope raii_class_indent(out);
Nicolas "Pixel" Noblebeeeddd2016-09-29 20:27:23 +0200258 out->Print(
259 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
260 "\nIt is recommended to use the GA API (classes and functions in this\n"
261 "file not marked beta) for all further purposes. This class was "
262 "generated\n"
263 "only to ease transition from grpcio<0.15.0 to "
264 "grpcio>=0.15.0.\"\"\"\n");
Masood Malekghassemid9539592016-10-20 14:47:14 -0700265 PrintAllComments(service);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000266 for (int i = 0; i < service->method_count(); ++i) {
267 const MethodDescriptor* meth = service->method(i);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700268 grpc::string arg_name =
269 meth->client_streaming() ? "request_iterator" : "request";
Masood Malekghassemid9539592016-10-20 14:47:14 -0700270 auto methdict;
271 methdict["Method"] = meth->name();
272 methdict["ArgName"] = arg_name;
Masood Malekghassemiac592452016-07-01 11:58:04 -0700273 out->Print(methdict,
274 "def $Method$(self, $ArgName$, timeout, metadata=None, "
275 "with_call=False, protocol_options=None):\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000276 {
277 IndentScope raii_method_indent(out);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700278 PrintAllComments(meth);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000279 out->Print("raise NotImplementedError()\n");
280 }
281 if (!meth->server_streaming()) {
282 out->Print(methdict, "$Method$.future = None\n");
283 }
284 }
285 }
286 return true;
287}
288
Masood Malekghassemid9539592016-10-20 14:47:14 -0700289bool PrivateGenerator::PrintBetaServerFactory(
290 const grpc::string& package_qualified_service_name,
291 const ServiceDescriptor* service) {
Ken Paysonbe187b02016-05-06 15:32:58 -0700292 out->Print("\n\n");
Masood Malekghassemiac592452016-07-01 11:58:04 -0700293 out->Print(
294 "def beta_create_$Service$_server(servicer, pool=None, "
295 "pool_size=None, default_timeout=None, maximum_timeout=None):\n",
296 "Service", service->name());
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000297 {
298 IndentScope raii_create_server_indent(out);
Nicolas "Pixel" Noblebeeeddd2016-09-29 20:27:23 +0200299 out->Print(
300 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
301 "\nIt is recommended to use the GA API (classes and functions in this\n"
302 "file not marked beta) for all further purposes. This function was\n"
303 "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
304 "\"\"\"\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000305 map<grpc::string, grpc::string> method_implementation_constructors;
Ken Paysonbe187b02016-05-06 15:32:58 -0700306 map<grpc::string, grpc::string> input_message_modules_and_classes;
307 map<grpc::string, grpc::string> output_message_modules_and_classes;
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000308 for (int i = 0; i < service->method_count(); ++i) {
309 const MethodDescriptor* method = service->method(i);
310 const grpc::string method_implementation_constructor =
311 grpc::string(method->client_streaming() ? "stream_" : "unary_") +
312 grpc::string(method->server_streaming() ? "stream_" : "unary_") +
313 "inline";
Ken Paysonbe187b02016-05-06 15:32:58 -0700314 grpc::string input_message_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700315 if (!GetModuleAndMessagePath(method->input_type(),
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000316 &input_message_module_and_class)) {
317 return false;
318 }
Ken Paysonbe187b02016-05-06 15:32:58 -0700319 grpc::string output_message_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700320 if (!GetModuleAndMessagePath(method->output_type(),
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000321 &output_message_module_and_class)) {
322 return false;
323 }
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000324 method_implementation_constructors.insert(
325 make_pair(method->name(), method_implementation_constructor));
326 input_message_modules_and_classes.insert(
327 make_pair(method->name(), input_message_module_and_class));
328 output_message_modules_and_classes.insert(
329 make_pair(method->name(), output_message_module_and_class));
330 }
331 out->Print("request_deserializers = {\n");
332 for (auto name_and_input_module_class_pair =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700333 input_message_modules_and_classes.begin();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000334 name_and_input_module_class_pair !=
Masood Malekghassemiac592452016-07-01 11:58:04 -0700335 input_message_modules_and_classes.end();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000336 name_and_input_module_class_pair++) {
337 IndentScope raii_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700338 out->Print(
339 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
340 "$InputTypeModuleAndClass$.FromString,\n",
341 "PackageQualifiedServiceName", package_qualified_service_name,
342 "MethodName", name_and_input_module_class_pair->first,
343 "InputTypeModuleAndClass", name_and_input_module_class_pair->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000344 }
345 out->Print("}\n");
346 out->Print("response_serializers = {\n");
347 for (auto name_and_output_module_class_pair =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700348 output_message_modules_and_classes.begin();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000349 name_and_output_module_class_pair !=
Masood Malekghassemiac592452016-07-01 11:58:04 -0700350 output_message_modules_and_classes.end();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000351 name_and_output_module_class_pair++) {
352 IndentScope raii_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700353 out->Print(
354 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
355 "$OutputTypeModuleAndClass$.SerializeToString,\n",
356 "PackageQualifiedServiceName", package_qualified_service_name,
357 "MethodName", name_and_output_module_class_pair->first,
358 "OutputTypeModuleAndClass",
359 name_and_output_module_class_pair->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000360 }
361 out->Print("}\n");
362 out->Print("method_implementations = {\n");
363 for (auto name_and_implementation_constructor =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700364 method_implementation_constructors.begin();
365 name_and_implementation_constructor !=
366 method_implementation_constructors.end();
367 name_and_implementation_constructor++) {
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000368 IndentScope raii_descriptions_indent(out);
369 const grpc::string method_name =
370 name_and_implementation_constructor->first;
Masood Malekghassemiac592452016-07-01 11:58:04 -0700371 out->Print(
372 "(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
373 "face_utilities.$Constructor$(servicer.$Method$),\n",
374 "PackageQualifiedServiceName", package_qualified_service_name,
375 "Method", name_and_implementation_constructor->first, "Constructor",
376 name_and_implementation_constructor->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000377 }
378 out->Print("}\n");
Masood Malekghassemiac592452016-07-01 11:58:04 -0700379 out->Print(
380 "server_options = beta_implementations.server_options("
381 "request_deserializers=request_deserializers, "
382 "response_serializers=response_serializers, "
383 "thread_pool=pool, thread_pool_size=pool_size, "
384 "default_timeout=default_timeout, "
385 "maximum_timeout=maximum_timeout)\n");
386 out->Print(
387 "return beta_implementations.server(method_implementations, "
388 "options=server_options)\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000389 }
390 return true;
391}
392
Masood Malekghassemid9539592016-10-20 14:47:14 -0700393bool PrivateGenerator::PrintBetaStubFactory(
394 const grpc::string& package_qualified_service_name,
395 const ServiceDescriptor* service) {
396 map<grpc::string, grpc::string> dict;
397 dict["Service"] = service->name();
Ken Paysonbe187b02016-05-06 15:32:58 -0700398 out->Print("\n\n");
Masood Malekghassemiac592452016-07-01 11:58:04 -0700399 out->Print(dict,
400 "def beta_create_$Service$_stub(channel, host=None,"
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000401 " metadata_transformer=None, pool=None, pool_size=None):\n");
402 {
403 IndentScope raii_create_server_indent(out);
Nicolas "Pixel" Noblebeeeddd2016-09-29 20:27:23 +0200404 out->Print(
405 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
406 "\nIt is recommended to use the GA API (classes and functions in this\n"
407 "file not marked beta) for all further purposes. This function was\n"
408 "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
409 "\"\"\"\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000410 map<grpc::string, grpc::string> method_cardinalities;
Ken Paysonbe187b02016-05-06 15:32:58 -0700411 map<grpc::string, grpc::string> input_message_modules_and_classes;
412 map<grpc::string, grpc::string> output_message_modules_and_classes;
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000413 for (int i = 0; i < service->method_count(); ++i) {
414 const MethodDescriptor* method = service->method(i);
415 const grpc::string method_cardinality =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700416 grpc::string(method->client_streaming() ? "STREAM" : "UNARY") + "_" +
Ken Paysonbe187b02016-05-06 15:32:58 -0700417 grpc::string(method->server_streaming() ? "STREAM" : "UNARY");
418 grpc::string input_message_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700419 if (!GetModuleAndMessagePath(method->input_type(),
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000420 &input_message_module_and_class)) {
421 return false;
422 }
Ken Paysonbe187b02016-05-06 15:32:58 -0700423 grpc::string output_message_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700424 if (!GetModuleAndMessagePath(method->output_type(),
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000425 &output_message_module_and_class)) {
426 return false;
427 }
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000428 method_cardinalities.insert(
429 make_pair(method->name(), method_cardinality));
430 input_message_modules_and_classes.insert(
431 make_pair(method->name(), input_message_module_and_class));
432 output_message_modules_and_classes.insert(
433 make_pair(method->name(), output_message_module_and_class));
434 }
435 out->Print("request_serializers = {\n");
436 for (auto name_and_input_module_class_pair =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700437 input_message_modules_and_classes.begin();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000438 name_and_input_module_class_pair !=
Masood Malekghassemiac592452016-07-01 11:58:04 -0700439 input_message_modules_and_classes.end();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000440 name_and_input_module_class_pair++) {
441 IndentScope raii_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700442 out->Print(
443 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
444 "$InputTypeModuleAndClass$.SerializeToString,\n",
445 "PackageQualifiedServiceName", package_qualified_service_name,
446 "MethodName", name_and_input_module_class_pair->first,
447 "InputTypeModuleAndClass", name_and_input_module_class_pair->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000448 }
449 out->Print("}\n");
450 out->Print("response_deserializers = {\n");
451 for (auto name_and_output_module_class_pair =
Masood Malekghassemiac592452016-07-01 11:58:04 -0700452 output_message_modules_and_classes.begin();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000453 name_and_output_module_class_pair !=
Masood Malekghassemiac592452016-07-01 11:58:04 -0700454 output_message_modules_and_classes.end();
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000455 name_and_output_module_class_pair++) {
456 IndentScope raii_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700457 out->Print(
458 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
459 "$OutputTypeModuleAndClass$.FromString,\n",
460 "PackageQualifiedServiceName", package_qualified_service_name,
461 "MethodName", name_and_output_module_class_pair->first,
462 "OutputTypeModuleAndClass",
463 name_and_output_module_class_pair->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000464 }
465 out->Print("}\n");
466 out->Print("cardinalities = {\n");
467 for (auto name_and_cardinality = method_cardinalities.begin();
468 name_and_cardinality != method_cardinalities.end();
469 name_and_cardinality++) {
470 IndentScope raii_descriptions_indent(out);
471 out->Print("\'$Method$\': cardinality.Cardinality.$Cardinality$,\n",
Masood Malekghassemiac592452016-07-01 11:58:04 -0700472 "Method", name_and_cardinality->first, "Cardinality",
473 name_and_cardinality->second);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000474 }
475 out->Print("}\n");
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000476 out->Print(
Masood Malekghassemiac592452016-07-01 11:58:04 -0700477 "stub_options = beta_implementations.stub_options("
478 "host=host, metadata_transformer=metadata_transformer, "
479 "request_serializers=request_serializers, "
480 "response_deserializers=response_deserializers, "
481 "thread_pool=pool, thread_pool_size=pool_size)\n");
482 out->Print(
483 "return beta_implementations.dynamic_stub(channel, "
484 "\'$PackageQualifiedServiceName$\', "
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000485 "cardinalities, options=stub_options)\n",
486 "PackageQualifiedServiceName", package_qualified_service_name);
487 }
488 return true;
489}
490
Masood Malekghassemid9539592016-10-20 14:47:14 -0700491bool PrivateGenerator::PrintStub(const grpc::string& package_qualified_service_name,
492 const ServiceDescriptor* service) {
Nathaniel Manista45479402016-06-13 20:14:18 +0000493 out->Print("\n\n");
494 out->Print("class $Service$Stub(object):\n", "Service", service->name());
495 {
496 IndentScope raii_class_indent(out);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700497 PrintAllComments(service);
Nathaniel Manista45479402016-06-13 20:14:18 +0000498 out->Print("\n");
499 out->Print("def __init__(self, channel):\n");
500 {
501 IndentScope raii_init_indent(out);
502 out->Print("\"\"\"Constructor.\n");
503 out->Print("\n");
504 out->Print("Args:\n");
505 {
506 IndentScope raii_args_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700507 out->Print("channel: A grpc.Channel.\n");
Nathaniel Manista45479402016-06-13 20:14:18 +0000508 }
509 out->Print("\"\"\"\n");
510 for (int i = 0; i < service->method_count(); ++i) {
511 auto method = service->method(i);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700512 auto multi_callable_constructor =
513 grpc::string(method->client_streaming() ? "stream" : "unary") +
514 "_" + grpc::string(method->server_streaming() ? "stream" : "unary");
515 grpc::string request_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700516 if (!GetModuleAndMessagePath(method->input_type(),
Masood Malekghassemiac592452016-07-01 11:58:04 -0700517 &request_module_and_class)) {
Nathaniel Manista45479402016-06-13 20:14:18 +0000518 return false;
Masood Malekghassemiac592452016-07-01 11:58:04 -0700519 }
520 grpc::string response_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700521 if (!GetModuleAndMessagePath(method->output_type(),
Masood Malekghassemiac592452016-07-01 11:58:04 -0700522 &response_module_and_class)) {
523 return false;
524 }
525 out->Print("self.$Method$ = channel.$MultiCallableConstructor$(\n",
526 "Method", method->name(), "MultiCallableConstructor",
527 multi_callable_constructor);
528 {
Nathaniel Manista45479402016-06-13 20:14:18 +0000529 IndentScope raii_first_attribute_indent(out);
530 IndentScope raii_second_attribute_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700531 out->Print("'/$PackageQualifiedService$/$Method$',\n",
532 "PackageQualifiedService", package_qualified_service_name,
533 "Method", method->name());
534 out->Print(
535 "request_serializer=$RequestModuleAndClass$.SerializeToString,\n",
536 "RequestModuleAndClass", request_module_and_class);
537 out->Print(
Nathaniel Manista45479402016-06-13 20:14:18 +0000538 "response_deserializer=$ResponseModuleAndClass$.FromString,\n",
Masood Malekghassemiac592452016-07-01 11:58:04 -0700539 "ResponseModuleAndClass", response_module_and_class);
540 out->Print(")\n");
541 }
Nathaniel Manista45479402016-06-13 20:14:18 +0000542 }
543 }
544 }
545 return true;
546}
547
Masood Malekghassemid9539592016-10-20 14:47:14 -0700548bool PrivateGenerator::PrintServicer(const ServiceDescriptor* service) {
Nathaniel Manista45479402016-06-13 20:14:18 +0000549 out->Print("\n\n");
550 out->Print("class $Service$Servicer(object):\n", "Service", service->name());
551 {
552 IndentScope raii_class_indent(out);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700553 PrintAllComments(service);
Nathaniel Manista45479402016-06-13 20:14:18 +0000554 for (int i = 0; i < service->method_count(); ++i) {
555 auto method = service->method(i);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700556 grpc::string arg_name =
557 method->client_streaming() ? "request_iterator" : "request";
Nathaniel Manista45479402016-06-13 20:14:18 +0000558 out->Print("\n");
Masood Malekghassemiac592452016-07-01 11:58:04 -0700559 out->Print("def $Method$(self, $ArgName$, context):\n", "Method",
560 method->name(), "ArgName", arg_name);
Nathaniel Manista45479402016-06-13 20:14:18 +0000561 {
562 IndentScope raii_method_indent(out);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700563 PrintAllComments(method);
Nathaniel Manista45479402016-06-13 20:14:18 +0000564 out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n");
565 out->Print("context.set_details('Method not implemented!')\n");
566 out->Print("raise NotImplementedError('Method not implemented!')\n");
567 }
568 }
569 }
570 return true;
571}
572
Masood Malekghassemid9539592016-10-20 14:47:14 -0700573bool PrivateGenerator::PrintAddServicerToServer(
Masood Malekghassemiac592452016-07-01 11:58:04 -0700574 const grpc::string& package_qualified_service_name,
Masood Malekghassemid9539592016-10-20 14:47:14 -0700575 const ServiceDescriptor* service) {
Nathaniel Manista45479402016-06-13 20:14:18 +0000576 out->Print("\n\n");
577 out->Print("def add_$Service$Servicer_to_server(servicer, server):\n",
Masood Malekghassemiac592452016-07-01 11:58:04 -0700578 "Service", service->name());
Nathaniel Manista45479402016-06-13 20:14:18 +0000579 {
580 IndentScope raii_class_indent(out);
581 out->Print("rpc_method_handlers = {\n");
582 {
583 IndentScope raii_dict_first_indent(out);
584 IndentScope raii_dict_second_indent(out);
585 for (int i = 0; i < service->method_count(); ++i) {
586 auto method = service->method(i);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700587 auto method_handler_constructor =
Nathaniel Manista45479402016-06-13 20:14:18 +0000588 grpc::string(method->client_streaming() ? "stream" : "unary") +
Masood Malekghassemiac592452016-07-01 11:58:04 -0700589 "_" +
Nathaniel Manista45479402016-06-13 20:14:18 +0000590 grpc::string(method->server_streaming() ? "stream" : "unary") +
591 "_rpc_method_handler";
Masood Malekghassemiac592452016-07-01 11:58:04 -0700592 grpc::string request_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700593 if (!GetModuleAndMessagePath(method->input_type(),
Masood Malekghassemiac592452016-07-01 11:58:04 -0700594 &request_module_and_class)) {
Nathaniel Manista45479402016-06-13 20:14:18 +0000595 return false;
Masood Malekghassemiac592452016-07-01 11:58:04 -0700596 }
597 grpc::string response_module_and_class;
Masood Malekghassemid9539592016-10-20 14:47:14 -0700598 if (!GetModuleAndMessagePath(method->output_type(),
Masood Malekghassemiac592452016-07-01 11:58:04 -0700599 &response_module_and_class)) {
600 return false;
601 }
602 out->Print("'$Method$': grpc.$MethodHandlerConstructor$(\n", "Method",
603 method->name(), "MethodHandlerConstructor",
604 method_handler_constructor);
605 {
Nathaniel Manista45479402016-06-13 20:14:18 +0000606 IndentScope raii_call_first_indent(out);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700607 IndentScope raii_call_second_indent(out);
608 out->Print("servicer.$Method$,\n", "Method", method->name());
609 out->Print(
610 "request_deserializer=$RequestModuleAndClass$.FromString,\n",
611 "RequestModuleAndClass", request_module_and_class);
612 out->Print(
613 "response_serializer=$ResponseModuleAndClass$.SerializeToString,"
614 "\n",
615 "ResponseModuleAndClass", response_module_and_class);
616 }
617 out->Print("),\n");
Nathaniel Manista45479402016-06-13 20:14:18 +0000618 }
619 }
620 out->Print("}\n");
621 out->Print("generic_handler = grpc.method_handlers_generic_handler(\n");
622 {
623 IndentScope raii_call_first_indent(out);
624 IndentScope raii_call_second_indent(out);
625 out->Print("'$PackageQualifiedServiceName$', rpc_method_handlers)\n",
Masood Malekghassemiac592452016-07-01 11:58:04 -0700626 "PackageQualifiedServiceName", package_qualified_service_name);
Nathaniel Manista45479402016-06-13 20:14:18 +0000627 }
628 out->Print("server.add_generic_rpc_handlers((generic_handler,))\n");
629 }
630 return true;
631}
632
Masood Malekghassemid9539592016-10-20 14:47:14 -0700633bool PrivateGenerator::PrintBetaPreamble() {
Nathaniel Manistaf65d3c12015-09-05 03:55:19 +0000634 out->Print("from $Package$ import implementations as beta_implementations\n",
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000635 "Package", config.beta_package_root);
Masood Malekghassemiac592452016-07-01 11:58:04 -0700636 out->Print("from $Package$ import interfaces as beta_interfaces\n", "Package",
637 config.beta_package_root);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700638 return true;
639}
640
641bool PrivateGenerator::PrintPreamble() {
642 out->Print("import $Package$\n", "Package", config.grpc_package_root);
Nathaniel Manistacd9ec0e2015-08-31 07:49:45 +0000643 out->Print("from grpc.framework.common import cardinality\n");
Masood Malekghassemiac592452016-07-01 11:58:04 -0700644 out->Print(
645 "from grpc.framework.interfaces.face import utilities as "
646 "face_utilities\n");
Masood Malekghassemid9539592016-10-20 14:47:14 -0700647 if (generate_in_pb2_grpc) {
648 out->Print("\n");
649 for (int i = 0; i < file->service_count(); ++i) {
650 const ServiceDescriptor *service = file->service(i);
651 for (int j = 0; j < service->method_count(); ++j) {
652 auto method = service->method(j);
653 const Descriptor *types[2] = {method->input_type(), method->output_type()};
654 for (int k = 0; k < 2; ++k) {
655 const Descriptor *type = types[k];
656 grpc::string type_file_name = type->file()->name();
657 grpc::string module_name = ModuleName(type_file_name);
658 grpc::string module_alias = ModuleAlias(type_file_name);
659 out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName", module_name, "ModuleAlias", module_alias);
660 }
661 }
662 }
663 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800664 return true;
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800665}
666
Masood Malekghassemid9539592016-10-20 14:47:14 -0700667bool PrivateGenerator::PrintGAServices() {
668 grpc::string package = file->package();
669 if (!package.empty()) {
670 package = package.append(".");
671 }
672 for (int i = 0; i < file->service_count(); ++i) {
673 const ServiceDescriptor *service = file->service(i);
674 grpc::string package_qualified_service_name = package + service->name();
675 if (!(PrintStub(package_qualified_service_name, service) &&
676 PrintServicer(service) &&
677 PrintAddServicerToServer(package_qualified_service_name, service))) {
678 return false;
679 }
680 }
681 return true;
682}
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800683
Masood Malekghassemid9539592016-10-20 14:47:14 -0700684bool PrivateGenerator::PrintBetaServices() {
685 grpc::string package = file->package();
686 if (!package.empty()) {
687 package = package.append(".");
688 }
689 for (int i = 0; i < file->service_count(); ++i) {
690 const ServiceDescriptor *service = file->service(i);
691 grpc::string package_qualified_service_name = package + service->name();
692 if (!(PrintBetaServicer(service) && PrintBetaStub(service) &&
693 PrintBetaServerFactory(package_qualified_service_name, service) &&
694 PrintBetaStubFactory(package_qualified_service_name, service))) {
695 return false;
696 }
697 }
698 return true;
699}
700
701pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() {
Masood Malekghassemi65c803b2015-03-20 06:48:47 -0700702 grpc::string output;
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800703 {
704 // Scope the output stream so it closes and finalizes output to the string.
705 StringOutputStream output_stream(&output);
Masood Malekghassemid9539592016-10-20 14:47:14 -0700706 Printer out_printer(&output_stream, '$');
707 out = &out_printer;
708
709 if (generate_in_pb2_grpc) {
710 if (!PrintPreamble()) {
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800711 return make_pair(false, "");
712 }
Masood Malekghassemid9539592016-10-20 14:47:14 -0700713 if (!PrintGAServices()) {
714 return make_pair(false, "");
715 }
716 } else {
717 out->Print("try:\n");
718 {
719 IndentScope raii_dict_try_indent(out);
720 out->Print("# THESE ELEMENTS WILL BE DEPRECATED.\n"
721 "# Please use the generated *_pb2_grpc.py files instead.\n");
722 if (!PrintPreamble()) {
723 return make_pair(false, "");
724 }
725 if (!PrintBetaPreamble()) {
726 return make_pair(false, "");
727 }
728 if (!PrintGAServices()) {
729 return make_pair(false, "");
730 }
731 if (!PrintBetaServices()) {
732 return make_pair(false, "");
733 }
734 }
735 out->Print("except ImportError:\n");
736 {
737 IndentScope raii_dict_except_indent(out);
738 out->Print("pass");
739 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800740 }
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800741 }
Masood Malekghassemi59d9ff42015-02-23 15:28:07 -0800742 return make_pair(true, std::move(output));
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800743}
744
Masood Malekghassemid9539592016-10-20 14:47:14 -0700745} // namespace
746
747GeneratorConfiguration::GeneratorConfiguration()
748 : grpc_package_root("grpc"), beta_package_root("grpc.beta") {}
749
750PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
751 : config_(config) {}
752
753PythonGrpcGenerator::~PythonGrpcGenerator() {}
754
755bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
756 const grpc::string& parameter,
757 GeneratorContext* context,
758 grpc::string* error) const {
759 // Get output file name.
760 grpc::string pb2_file_name;
761 grpc::string pb2_grpc_file_name;
762 static const int proto_suffix_length = strlen(".proto");
763 if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
764 file->name().find_last_of(".proto") == file->name().size() - 1) {
765 grpc::string base = file->name().substr(
766 0, file->name().size() - proto_suffix_length);
767 pb2_file_name = base + "_pb2.py";
768 pb2_grpc_file_name = base + "_pb2_grpc.py";
769 } else {
770 *error = "Invalid proto file name. Proto file must end with .proto";
771 return false;
772 }
773
774 PrivateGenerator generator(config_, file);
775
776 std::unique_ptr<ZeroCopyOutputStream> pb2_output(
777 context->OpenForAppend(pb2_file_name));
778 std::unique_ptr<ZeroCopyOutputStream> grpc_output(
779 context->Open(pb2_grpc_file_name));
780 CodedOutputStream pb2_coded_out(pb2_output.get());
781 CodedOutputStream grpc_coded_out(grpc_output.get());
782 bool success = false;
783 grpc::string pb2_code;
784 grpc::string grpc_code;
785 generator.generate_in_pb2_grpc = false;
786 tie(success, pb2_code) = generator.GetGrpcServices();
787 if (success) {
788 generator.generate_in_pb2_grpc = true;
789 tie(success, grpc_code) = generator.GetGrpcServices();
790 if (success) {
791 pb2_coded_out.WriteRaw(pb2_code.data(), pb2_code.size());
792 grpc_coded_out.WriteRaw(grpc_code.data(), grpc_code.size());
793 return true;
794 }
795 }
796 return false;
797}
798
Masood Malekghassemif8e297a2015-02-19 15:39:32 -0800799} // namespace grpc_python_generator