1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include "src/compiler/python_generator.h"
20
21 #include <algorithm>
22 #include <cassert>
23 #include <cctype>
24 #include <cstring>
25 #include <fstream>
26 #include <iostream>
27 #include <map>
28 #include <memory>
29 #include <ostream>
30 #include <set>
31 #include <sstream>
32 #include <tuple>
33 #include <vector>
34
35 #include "src/compiler/config.h"
36 #include "src/compiler/generator_helpers.h"
37 #include "src/compiler/protobuf_plugin.h"
38 #include "src/compiler/python_generator_helpers.h"
39 #include "src/compiler/python_private_generator.h"
40
41 using grpc::protobuf::FileDescriptor;
42 using grpc::protobuf::compiler::GeneratorContext;
43 using grpc::protobuf::io::CodedOutputStream;
44 using grpc::protobuf::io::ZeroCopyOutputStream;
45 using std::make_pair;
46 using std::map;
47 using std::pair;
48 using std::replace;
49 using std::set;
50 using std::tuple;
51 using std::vector;
52
53 namespace grpc_python_generator {
54
55 std::string generator_file_name;
56
57 namespace {
58
59 typedef map<std::string, std::string> StringMap;
60 typedef vector<std::string> StringVector;
61 typedef tuple<std::string, std::string> StringPair;
62 typedef set<StringPair> StringPairSet;
63
64 // Provides RAII indentation handling. Use as:
65 // {
66 // IndentScope raii_my_indent_var_name_here(my_py_printer);
67 // // constructor indented my_py_printer
68 // ...
69 // // destructor called at end of scope, un-indenting my_py_printer
70 // }
71 class IndentScope {
72 public:
IndentScope(grpc_generator::Printer * printer)73 explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) {
74 // NOTE(rbellevi): Two-space tabs are hard-coded in the protocol compiler.
75 // Doubling our indents and outdents guarantees compliance with PEP8.
76 printer_->Indent();
77 printer_->Indent();
78 }
79
~IndentScope()80 ~IndentScope() {
81 printer_->Outdent();
82 printer_->Outdent();
83 }
84
85 private:
86 grpc_generator::Printer* printer_;
87 };
88
PrivateGenerator(const GeneratorConfiguration & config,const grpc_generator::File * file)89 PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
90 const grpc_generator::File* file)
91 : config(config), file(file) {}
92
PrintAllComments(StringVector comments,grpc_generator::Printer * out)93 void PrivateGenerator::PrintAllComments(StringVector comments,
94 grpc_generator::Printer* out) {
95 if (comments.empty()) {
96 // Python requires code structures like class and def to have
97 // a body, even if it is just "pass" or a docstring. We need
98 // to ensure not to generate empty bodies. We could do something
99 // smarter and more sophisticated, but at the moment, if there is
100 // no docstring to print, we simply emit "pass" to ensure validity
101 // of the generated code.
102 out->Print(
103 "\"\"\"Missing associated documentation comment in .proto "
104 "file.\"\"\"\n");
105 return;
106 }
107 out->Print("\"\"\"");
108 for (StringVector::iterator it = comments.begin(); it != comments.end();
109 ++it) {
110 size_t start_pos = it->find_first_not_of(' ');
111 if (start_pos != std::string::npos) {
112 out->PrintRaw(it->c_str() + start_pos);
113 }
114 out->Print("\n");
115 }
116 out->Print("\"\"\"\n");
117 }
118
PrintBetaServicer(const grpc_generator::Service * service,grpc_generator::Printer * out)119 bool PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service,
120 grpc_generator::Printer* out) {
121 StringMap service_dict;
122 service_dict["Service"] = service->name();
123 out->Print("\n\n");
124 out->Print(service_dict, "class Beta$Service$Servicer(object):\n");
125 {
126 IndentScope raii_class_indent(out);
127 out->Print(
128 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
129 "\nIt is recommended to use the GA API (classes and functions in this\n"
130 "file not marked beta) for all further purposes. This class was "
131 "generated\n"
132 "only to ease transition from grpcio<0.15.0 to "
133 "grpcio>=0.15.0.\"\"\"\n");
134 StringVector service_comments = service->GetAllComments();
135 PrintAllComments(service_comments, out);
136 for (int i = 0; i < service->method_count(); ++i) {
137 auto method = service->method(i);
138 std::string arg_name =
139 method->ClientStreaming() ? "request_iterator" : "request";
140 StringMap method_dict;
141 method_dict["Method"] = method->name();
142 method_dict["ArgName"] = arg_name;
143 out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
144 {
145 IndentScope raii_method_indent(out);
146 StringVector method_comments = method->GetAllComments();
147 PrintAllComments(method_comments, out);
148 out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
149 }
150 }
151 }
152 return true;
153 }
154
PrintBetaStub(const grpc_generator::Service * service,grpc_generator::Printer * out)155 bool PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service,
156 grpc_generator::Printer* out) {
157 StringMap service_dict;
158 service_dict["Service"] = service->name();
159 out->Print("\n\n");
160 out->Print(service_dict, "class Beta$Service$Stub(object):\n");
161 {
162 IndentScope raii_class_indent(out);
163 out->Print(
164 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
165 "\nIt is recommended to use the GA API (classes and functions in this\n"
166 "file not marked beta) for all further purposes. This class was "
167 "generated\n"
168 "only to ease transition from grpcio<0.15.0 to "
169 "grpcio>=0.15.0.\"\"\"\n");
170 StringVector service_comments = service->GetAllComments();
171 PrintAllComments(service_comments, out);
172 for (int i = 0; i < service->method_count(); ++i) {
173 auto method = service->method(i);
174 std::string arg_name =
175 method->ClientStreaming() ? "request_iterator" : "request";
176 StringMap method_dict;
177 method_dict["Method"] = method->name();
178 method_dict["ArgName"] = arg_name;
179 out->Print(method_dict,
180 "def $Method$(self, $ArgName$, timeout, metadata=None, "
181 "with_call=False, protocol_options=None):\n");
182 {
183 IndentScope raii_method_indent(out);
184 StringVector method_comments = method->GetAllComments();
185 PrintAllComments(method_comments, out);
186 out->Print("raise NotImplementedError()\n");
187 }
188 if (!method->ServerStreaming()) {
189 out->Print(method_dict, "$Method$.future = None\n");
190 }
191 }
192 }
193 return true;
194 }
195
PrintBetaServerFactory(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)196 bool PrivateGenerator::PrintBetaServerFactory(
197 const std::string& package_qualified_service_name,
198 const grpc_generator::Service* service, grpc_generator::Printer* out) {
199 StringMap service_dict;
200 service_dict["Service"] = service->name();
201 out->Print("\n\n");
202 out->Print(service_dict,
203 "def beta_create_$Service$_server(servicer, pool=None, "
204 "pool_size=None, default_timeout=None, maximum_timeout=None):\n");
205 {
206 IndentScope raii_create_server_indent(out);
207 out->Print(
208 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
209 "\nIt is recommended to use the GA API (classes and functions in this\n"
210 "file not marked beta) for all further purposes. This function was\n"
211 "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
212 "\"\"\"\n");
213 StringMap method_implementation_constructors;
214 StringMap input_message_modules_and_classes;
215 StringMap output_message_modules_and_classes;
216 for (int i = 0; i < service->method_count(); ++i) {
217 auto method = service->method(i);
218 const std::string method_implementation_constructor =
219 std::string(method->ClientStreaming() ? "stream_" : "unary_") +
220 std::string(method->ServerStreaming() ? "stream_" : "unary_") +
221 "inline";
222 std::string input_message_module_and_class;
223 if (!method->get_module_and_message_path_input(
224 &input_message_module_and_class, generator_file_name,
225 generate_in_pb2_grpc, config.import_prefix,
226 config.prefixes_to_filter)) {
227 return false;
228 }
229 std::string output_message_module_and_class;
230 if (!method->get_module_and_message_path_output(
231 &output_message_module_and_class, generator_file_name,
232 generate_in_pb2_grpc, config.import_prefix,
233 config.prefixes_to_filter)) {
234 return false;
235 }
236 method_implementation_constructors.insert(
237 make_pair(method->name(), method_implementation_constructor));
238 input_message_modules_and_classes.insert(
239 make_pair(method->name(), input_message_module_and_class));
240 output_message_modules_and_classes.insert(
241 make_pair(method->name(), output_message_module_and_class));
242 }
243 StringMap method_dict;
244 method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
245 out->Print("request_deserializers = {\n");
246 for (StringMap::iterator name_and_input_module_class_pair =
247 input_message_modules_and_classes.begin();
248 name_and_input_module_class_pair !=
249 input_message_modules_and_classes.end();
250 name_and_input_module_class_pair++) {
251 method_dict["MethodName"] = name_and_input_module_class_pair->first;
252 method_dict["InputTypeModuleAndClass"] =
253 name_and_input_module_class_pair->second;
254 IndentScope raii_indent(out);
255 out->Print(method_dict,
256 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
257 "$InputTypeModuleAndClass$.FromString,\n");
258 }
259 out->Print("}\n");
260 out->Print("response_serializers = {\n");
261 for (StringMap::iterator name_and_output_module_class_pair =
262 output_message_modules_and_classes.begin();
263 name_and_output_module_class_pair !=
264 output_message_modules_and_classes.end();
265 name_and_output_module_class_pair++) {
266 method_dict["MethodName"] = name_and_output_module_class_pair->first;
267 method_dict["OutputTypeModuleAndClass"] =
268 name_and_output_module_class_pair->second;
269 IndentScope raii_indent(out);
270 out->Print(method_dict,
271 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
272 "$OutputTypeModuleAndClass$.SerializeToString,\n");
273 }
274 out->Print("}\n");
275 out->Print("method_implementations = {\n");
276 for (StringMap::iterator name_and_implementation_constructor =
277 method_implementation_constructors.begin();
278 name_and_implementation_constructor !=
279 method_implementation_constructors.end();
280 name_and_implementation_constructor++) {
281 method_dict["Method"] = name_and_implementation_constructor->first;
282 method_dict["Constructor"] = name_and_implementation_constructor->second;
283 IndentScope raii_descriptions_indent(out);
284 const std::string method_name =
285 name_and_implementation_constructor->first;
286 out->Print(method_dict,
287 "(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
288 "face_utilities.$Constructor$(servicer.$Method$),\n");
289 }
290 out->Print("}\n");
291 out->Print(
292 "server_options = beta_implementations.server_options("
293 "request_deserializers=request_deserializers, "
294 "response_serializers=response_serializers, "
295 "thread_pool=pool, thread_pool_size=pool_size, "
296 "default_timeout=default_timeout, "
297 "maximum_timeout=maximum_timeout)\n");
298 out->Print(
299 "return beta_implementations.server(method_implementations, "
300 "options=server_options)\n");
301 }
302 return true;
303 }
304
PrintBetaStubFactory(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)305 bool PrivateGenerator::PrintBetaStubFactory(
306 const std::string& package_qualified_service_name,
307 const grpc_generator::Service* service, grpc_generator::Printer* out) {
308 StringMap dict;
309 dict["Service"] = service->name();
310 out->Print("\n\n");
311 out->Print(dict,
312 "def beta_create_$Service$_stub(channel, host=None,"
313 " metadata_transformer=None, pool=None, pool_size=None):\n");
314 {
315 IndentScope raii_create_server_indent(out);
316 out->Print(
317 "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
318 "\nIt is recommended to use the GA API (classes and functions in this\n"
319 "file not marked beta) for all further purposes. This function was\n"
320 "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
321 "\"\"\"\n");
322 StringMap method_cardinalities;
323 StringMap input_message_modules_and_classes;
324 StringMap output_message_modules_and_classes;
325 for (int i = 0; i < service->method_count(); ++i) {
326 auto method = service->method(i);
327 const std::string method_cardinality =
328 std::string(method->ClientStreaming() ? "STREAM" : "UNARY") + "_" +
329 std::string(method->ServerStreaming() ? "STREAM" : "UNARY");
330 std::string input_message_module_and_class;
331 if (!method->get_module_and_message_path_input(
332 &input_message_module_and_class, generator_file_name,
333 generate_in_pb2_grpc, config.import_prefix,
334 config.prefixes_to_filter)) {
335 return false;
336 }
337 std::string output_message_module_and_class;
338 if (!method->get_module_and_message_path_output(
339 &output_message_module_and_class, generator_file_name,
340 generate_in_pb2_grpc, config.import_prefix,
341 config.prefixes_to_filter)) {
342 return false;
343 }
344 method_cardinalities.insert(
345 make_pair(method->name(), method_cardinality));
346 input_message_modules_and_classes.insert(
347 make_pair(method->name(), input_message_module_and_class));
348 output_message_modules_and_classes.insert(
349 make_pair(method->name(), output_message_module_and_class));
350 }
351 StringMap method_dict;
352 method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
353 out->Print("request_serializers = {\n");
354 for (StringMap::iterator name_and_input_module_class_pair =
355 input_message_modules_and_classes.begin();
356 name_and_input_module_class_pair !=
357 input_message_modules_and_classes.end();
358 name_and_input_module_class_pair++) {
359 method_dict["MethodName"] = name_and_input_module_class_pair->first;
360 method_dict["InputTypeModuleAndClass"] =
361 name_and_input_module_class_pair->second;
362 IndentScope raii_indent(out);
363 out->Print(method_dict,
364 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
365 "$InputTypeModuleAndClass$.SerializeToString,\n");
366 }
367 out->Print("}\n");
368 out->Print("response_deserializers = {\n");
369 for (StringMap::iterator name_and_output_module_class_pair =
370 output_message_modules_and_classes.begin();
371 name_and_output_module_class_pair !=
372 output_message_modules_and_classes.end();
373 name_and_output_module_class_pair++) {
374 method_dict["MethodName"] = name_and_output_module_class_pair->first;
375 method_dict["OutputTypeModuleAndClass"] =
376 name_and_output_module_class_pair->second;
377 IndentScope raii_indent(out);
378 out->Print(method_dict,
379 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
380 "$OutputTypeModuleAndClass$.FromString,\n");
381 }
382 out->Print("}\n");
383 out->Print("cardinalities = {\n");
384 for (StringMap::iterator name_and_cardinality =
385 method_cardinalities.begin();
386 name_and_cardinality != method_cardinalities.end();
387 name_and_cardinality++) {
388 method_dict["Method"] = name_and_cardinality->first;
389 method_dict["Cardinality"] = name_and_cardinality->second;
390 IndentScope raii_descriptions_indent(out);
391 out->Print(method_dict,
392 "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n");
393 }
394 out->Print("}\n");
395 out->Print(
396 "stub_options = beta_implementations.stub_options("
397 "host=host, metadata_transformer=metadata_transformer, "
398 "request_serializers=request_serializers, "
399 "response_deserializers=response_deserializers, "
400 "thread_pool=pool, thread_pool_size=pool_size)\n");
401 out->Print(method_dict,
402 "return beta_implementations.dynamic_stub(channel, "
403 "\'$PackageQualifiedServiceName$\', "
404 "cardinalities, options=stub_options)\n");
405 }
406 return true;
407 }
408
PrintStub(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)409 bool PrivateGenerator::PrintStub(
410 const std::string& package_qualified_service_name,
411 const grpc_generator::Service* service, grpc_generator::Printer* out) {
412 StringMap dict;
413 dict["Service"] = service->name();
414 out->Print("\n\n");
415 out->Print(dict, "class $Service$Stub(object):\n");
416 {
417 IndentScope raii_class_indent(out);
418 StringVector service_comments = service->GetAllComments();
419 PrintAllComments(service_comments, out);
420 out->Print("\n");
421 out->Print("def __init__(self, channel):\n");
422 {
423 IndentScope raii_init_indent(out);
424 out->Print("\"\"\"Constructor.\n");
425 out->Print("\n");
426 out->Print("Args:\n");
427 {
428 IndentScope raii_args_indent(out);
429 out->Print("channel: A grpc.Channel.\n");
430 }
431 out->Print("\"\"\"\n");
432 for (int i = 0; i < service->method_count(); ++i) {
433 auto method = service->method(i);
434 std::string multi_callable_constructor =
435 std::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
436 std::string(method->ServerStreaming() ? "stream" : "unary");
437 std::string request_module_and_class;
438 if (!method->get_module_and_message_path_input(
439 &request_module_and_class, generator_file_name,
440 generate_in_pb2_grpc, config.import_prefix,
441 config.prefixes_to_filter)) {
442 return false;
443 }
444 std::string response_module_and_class;
445 if (!method->get_module_and_message_path_output(
446 &response_module_and_class, generator_file_name,
447 generate_in_pb2_grpc, config.import_prefix,
448 config.prefixes_to_filter)) {
449 return false;
450 }
451 StringMap method_dict;
452 method_dict["Method"] = method->name();
453 method_dict["MultiCallableConstructor"] = multi_callable_constructor;
454 out->Print(method_dict,
455 "self.$Method$ = channel.$MultiCallableConstructor$(\n");
456 {
457 method_dict["PackageQualifiedService"] =
458 package_qualified_service_name;
459 method_dict["RequestModuleAndClass"] = request_module_and_class;
460 method_dict["ResponseModuleAndClass"] = response_module_and_class;
461 IndentScope raii_first_attribute_indent(out);
462 IndentScope raii_second_attribute_indent(out);
463 out->Print(method_dict, "'/$PackageQualifiedService$/$Method$',\n");
464 out->Print(method_dict,
465 "request_serializer=$RequestModuleAndClass$."
466 "SerializeToString,\n");
467 out->Print(
468 method_dict,
469 "response_deserializer=$ResponseModuleAndClass$.FromString,\n");
470 out->Print("_registered_method=True)\n");
471 }
472 }
473 }
474 }
475 return true;
476 }
477
PrintServicer(const grpc_generator::Service * service,grpc_generator::Printer * out)478 bool PrivateGenerator::PrintServicer(const grpc_generator::Service* service,
479 grpc_generator::Printer* out) {
480 StringMap service_dict;
481 service_dict["Service"] = service->name();
482 out->Print("\n\n");
483 out->Print(service_dict, "class $Service$Servicer(object):\n");
484 {
485 IndentScope raii_class_indent(out);
486 StringVector service_comments = service->GetAllComments();
487 PrintAllComments(service_comments, out);
488 for (int i = 0; i < service->method_count(); ++i) {
489 auto method = service->method(i);
490 std::string arg_name =
491 method->ClientStreaming() ? "request_iterator" : "request";
492 StringMap method_dict;
493 method_dict["Method"] = method->name();
494 method_dict["ArgName"] = arg_name;
495 out->Print("\n");
496 out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
497 {
498 IndentScope raii_method_indent(out);
499 StringVector method_comments = method->GetAllComments();
500 PrintAllComments(method_comments, out);
501 out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n");
502 out->Print("context.set_details('Method not implemented!')\n");
503 out->Print("raise NotImplementedError('Method not implemented!')\n");
504 }
505 }
506 }
507 return true;
508 }
509
PrintAddServicerToServer(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)510 bool PrivateGenerator::PrintAddServicerToServer(
511 const std::string& package_qualified_service_name,
512 const grpc_generator::Service* service, grpc_generator::Printer* out) {
513 StringMap service_dict;
514 service_dict["Service"] = service->name();
515 out->Print("\n\n");
516 out->Print(service_dict,
517 "def add_$Service$Servicer_to_server(servicer, server):\n");
518 {
519 IndentScope raii_class_indent(out);
520 out->Print("rpc_method_handlers = {\n");
521 {
522 IndentScope raii_dict_first_indent(out);
523 IndentScope raii_dict_second_indent(out);
524 for (int i = 0; i < service->method_count(); ++i) {
525 auto method = service->method(i);
526 std::string method_handler_constructor =
527 std::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
528 std::string(method->ServerStreaming() ? "stream" : "unary") +
529 "_rpc_method_handler";
530 std::string request_module_and_class;
531 if (!method->get_module_and_message_path_input(
532 &request_module_and_class, generator_file_name,
533 generate_in_pb2_grpc, config.import_prefix,
534 config.prefixes_to_filter)) {
535 return false;
536 }
537 std::string response_module_and_class;
538 if (!method->get_module_and_message_path_output(
539 &response_module_and_class, generator_file_name,
540 generate_in_pb2_grpc, config.import_prefix,
541 config.prefixes_to_filter)) {
542 return false;
543 }
544 StringMap method_dict;
545 method_dict["Method"] = method->name();
546 method_dict["MethodHandlerConstructor"] = method_handler_constructor;
547 method_dict["RequestModuleAndClass"] = request_module_and_class;
548 method_dict["ResponseModuleAndClass"] = response_module_and_class;
549 out->Print(method_dict,
550 "'$Method$': grpc.$MethodHandlerConstructor$(\n");
551 {
552 IndentScope raii_call_first_indent(out);
553 IndentScope raii_call_second_indent(out);
554 out->Print(method_dict, "servicer.$Method$,\n");
555 out->Print(
556 method_dict,
557 "request_deserializer=$RequestModuleAndClass$.FromString,\n");
558 out->Print(
559 method_dict,
560 "response_serializer=$ResponseModuleAndClass$.SerializeToString,"
561 "\n");
562 }
563 out->Print("),\n");
564 }
565 }
566 StringMap method_dict;
567 method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
568 out->Print("}\n");
569 out->Print("generic_handler = grpc.method_handlers_generic_handler(\n");
570 {
571 IndentScope raii_call_first_indent(out);
572 IndentScope raii_call_second_indent(out);
573 out->Print(method_dict,
574 "'$PackageQualifiedServiceName$', rpc_method_handlers)\n");
575 }
576 out->Print("server.add_generic_rpc_handlers((generic_handler,))\n");
577 out->Print(method_dict,
578 "server.add_registered_method_handlers('$"
579 "PackageQualifiedServiceName$', rpc_method_handlers)\n");
580 }
581 return true;
582 }
583
584 /* Prints out a service class used as a container for static methods pertaining
585 * to a class. This class has the exact name of service written in the ".proto"
586 * file, with no suffixes. Since this class merely acts as a namespace, it
587 * should never be instantiated.
588 */
PrintServiceClass(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)589 bool PrivateGenerator::PrintServiceClass(
590 const std::string& package_qualified_service_name,
591 const grpc_generator::Service* service, grpc_generator::Printer* out) {
592 StringMap dict;
593 dict["Service"] = service->name();
594 out->Print("\n\n");
595 out->Print(" # This class is part of an EXPERIMENTAL API.\n");
596 out->Print(dict, "class $Service$(object):\n");
597 {
598 IndentScope class_indent(out);
599 StringVector service_comments = service->GetAllComments();
600 PrintAllComments(service_comments, out);
601 for (int i = 0; i < service->method_count(); ++i) {
602 const auto& method = service->method(i);
603 std::string request_module_and_class;
604 if (!method->get_module_and_message_path_input(
605 &request_module_and_class, generator_file_name,
606 generate_in_pb2_grpc, config.import_prefix,
607 config.prefixes_to_filter)) {
608 return false;
609 }
610 std::string response_module_and_class;
611 if (!method->get_module_and_message_path_output(
612 &response_module_and_class, generator_file_name,
613 generate_in_pb2_grpc, config.import_prefix,
614 config.prefixes_to_filter)) {
615 return false;
616 }
617 out->Print("\n");
618 StringMap method_dict;
619 method_dict["Method"] = method->name();
620 out->Print("@staticmethod\n");
621 out->Print(method_dict, "def $Method$(");
622 std::string request_parameter(
623 method->ClientStreaming() ? "request_iterator" : "request");
624 StringMap args_dict;
625 args_dict["RequestParameter"] = request_parameter;
626 {
627 IndentScope args_indent(out);
628 IndentScope args_double_indent(out);
629 out->Print(args_dict, "$RequestParameter$,\n");
630 out->Print("target,\n");
631 out->Print("options=(),\n");
632 out->Print("channel_credentials=None,\n");
633 out->Print("call_credentials=None,\n");
634 out->Print("insecure=False,\n");
635 out->Print("compression=None,\n");
636 out->Print("wait_for_ready=None,\n");
637 out->Print("timeout=None,\n");
638 out->Print("metadata=None):\n");
639 }
640 {
641 IndentScope method_indent(out);
642 std::string arity_method_name =
643 std::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
644 std::string(method->ServerStreaming() ? "stream" : "unary");
645 args_dict["ArityMethodName"] = arity_method_name;
646 args_dict["PackageQualifiedService"] = package_qualified_service_name;
647 args_dict["Method"] = method->name();
648 out->Print(args_dict, "return grpc.experimental.$ArityMethodName$(\n");
649 {
650 IndentScope continuation_indent(out);
651 StringMap serializer_dict;
652 out->Print(args_dict, "$RequestParameter$,\n");
653 out->Print("target,\n");
654 out->Print(args_dict, "'/$PackageQualifiedService$/$Method$',\n");
655 serializer_dict["RequestModuleAndClass"] = request_module_and_class;
656 serializer_dict["ResponseModuleAndClass"] = response_module_and_class;
657 out->Print(serializer_dict,
658 "$RequestModuleAndClass$.SerializeToString,\n");
659 out->Print(serializer_dict, "$ResponseModuleAndClass$.FromString,\n");
660 out->Print("options,\n");
661 out->Print("channel_credentials,\n");
662 out->Print("insecure,\n");
663 out->Print("call_credentials,\n");
664 out->Print("compression,\n");
665 out->Print("wait_for_ready,\n");
666 out->Print("timeout,\n");
667 out->Print("metadata,\n");
668 out->Print("_registered_method=True)\n");
669 }
670 }
671 }
672 }
673 // TODO(rbellevi): Add methods pertinent to the server side as well.
674 return true;
675 }
676
PrintBetaPreamble(grpc_generator::Printer * out)677 bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) {
678 StringMap var;
679 var["Package"] = config.beta_package_root;
680 out->Print(var,
681 "from $Package$ import implementations as beta_implementations\n");
682 out->Print(var, "from $Package$ import interfaces as beta_interfaces\n");
683 out->Print("from grpc.framework.common import cardinality\n");
684 out->Print(
685 "from grpc.framework.interfaces.face import utilities as "
686 "face_utilities\n");
687 return true;
688 }
689
PrintPreamble(grpc_generator::Printer * out)690 bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) {
691 StringMap var;
692 var["Package"] = config.grpc_package_root;
693 out->Print(var, "import $Package$\n");
694 if (config.grpc_tools_version.size() > 0) {
695 out->Print(var, "import warnings\n");
696 }
697 if (generate_in_pb2_grpc) {
698 out->Print("\n");
699 StringPairSet imports_set;
700 for (int i = 0; i < file->service_count(); ++i) {
701 auto service = file->service(i);
702 for (int j = 0; j < service->method_count(); ++j) {
703 auto method = service.get()->method(j);
704
705 std::string input_type_file_name = method->get_input_type_name();
706 std::string input_module_name =
707 ModuleName(input_type_file_name, config.import_prefix,
708 config.prefixes_to_filter);
709 std::string input_module_alias =
710 ModuleAlias(input_type_file_name, config.import_prefix,
711 config.prefixes_to_filter);
712 imports_set.insert(
713 std::make_tuple(input_module_name, input_module_alias));
714
715 std::string output_type_file_name = method->get_output_type_name();
716 std::string output_module_name =
717 ModuleName(output_type_file_name, config.import_prefix,
718 config.prefixes_to_filter);
719 std::string output_module_alias =
720 ModuleAlias(output_type_file_name, config.import_prefix,
721 config.prefixes_to_filter);
722 imports_set.insert(
723 std::make_tuple(output_module_name, output_module_alias));
724 }
725 }
726
727 for (StringPairSet::iterator it = imports_set.begin();
728 it != imports_set.end(); ++it) {
729 auto module_name = std::get<0>(*it);
730 var["ModuleAlias"] = std::get<1>(*it);
731 const size_t last_dot_pos = module_name.rfind('.');
732 if (last_dot_pos == std::string::npos) {
733 var["ImportStatement"] = "import " + module_name;
734 } else {
735 var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) +
736 " import " +
737 module_name.substr(last_dot_pos + 1);
738 }
739 out->Print(var, "$ImportStatement$ as $ModuleAlias$\n");
740 }
741
742 // Checks if generate code is used with a supported grpcio version.
743 if (config.grpc_tools_version.size() > 0) {
744 var["ToolsVersion"] = config.grpc_tools_version;
745 out->Print(var, "\nGRPC_GENERATED_VERSION = '$ToolsVersion$'\n");
746 out->Print("GRPC_VERSION = grpc.__version__\n");
747 out->Print("_version_not_supported = False\n\n");
748 out->Print("try:\n");
749 {
750 IndentScope raii_import_indent(out);
751 out->Print(
752 "from grpc._utilities import first_version_is_lower\n"
753 "_version_not_supported = first_version_is_lower(GRPC_VERSION, "
754 "GRPC_GENERATED_VERSION)\n");
755 }
756 out->Print("except ImportError:\n");
757 {
758 IndentScope raii_import_error_indent(out);
759 out->Print("_version_not_supported = True\n");
760 }
761 out->Print("\nif _version_not_supported:\n");
762 {
763 IndentScope raii_warning_indent(out);
764 out->Print("raise RuntimeError(\n");
765 {
766 IndentScope raii_warning_string_indent(out);
767 std::string filename_without_ext = file->filename_without_ext();
768 std::replace(filename_without_ext.begin(), filename_without_ext.end(),
769 '-', '_');
770 var["Pb2GrpcFileName"] = filename_without_ext;
771 out->Print(
772 var,
773 "f'The grpc package installed is at version {GRPC_VERSION},'\n"
774 "+ f' but the generated code in $Pb2GrpcFileName$_pb2_grpc.py "
775 "depends on'\n"
776 "+ f' grpcio>={GRPC_GENERATED_VERSION}.'\n"
777 "+ f' Please upgrade your grpc module to "
778 "grpcio>={GRPC_GENERATED_VERSION}'\n"
779 "+ f' or downgrade your generated code using "
780 "grpcio-tools<={GRPC_VERSION}.'\n");
781 }
782 out->Print(")\n");
783 }
784 }
785 }
786 return true;
787 }
788
PrintGAServices(grpc_generator::Printer * out)789 bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) {
790 std::string package = file->package();
791 if (!package.empty()) {
792 package = package.append(".");
793 }
794 for (int i = 0; i < file->service_count(); ++i) {
795 auto service = file->service(i);
796 std::string package_qualified_service_name = package + service->name();
797 if (!(PrintStub(package_qualified_service_name, service.get(), out) &&
798 PrintServicer(service.get(), out) &&
799 PrintAddServicerToServer(package_qualified_service_name,
800 service.get(), out) &&
801 PrintServiceClass(package_qualified_service_name, service.get(),
802 out))) {
803 return false;
804 }
805 }
806 return true;
807 }
808
PrintBetaServices(grpc_generator::Printer * out)809 bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) {
810 std::string package = file->package();
811 if (!package.empty()) {
812 package = package.append(".");
813 }
814 for (int i = 0; i < file->service_count(); ++i) {
815 auto service = file->service(i);
816 std::string package_qualified_service_name = package + service->name();
817 if (!(PrintBetaServicer(service.get(), out) &&
818 PrintBetaStub(service.get(), out) &&
819 PrintBetaServerFactory(package_qualified_service_name, service.get(),
820 out) &&
821 PrintBetaStubFactory(package_qualified_service_name, service.get(),
822 out))) {
823 return false;
824 }
825 }
826 return true;
827 }
828
GetGrpcServices()829 pair<bool, std::string> PrivateGenerator::GetGrpcServices() {
830 std::string output;
831 {
832 // Scope the output stream so it closes and finalizes output to the string.
833 auto out = file->CreatePrinter(&output);
834 if (generate_in_pb2_grpc) {
835 out->Print(
836 "# Generated by the gRPC Python protocol compiler plugin. "
837 "DO NOT EDIT!\n\"\"\""
838 "Client and server classes corresponding to protobuf-defined "
839 "services.\"\"\"\n");
840 if (!PrintPreamble(out.get())) {
841 return make_pair(false, "");
842 }
843 if (!PrintGAServices(out.get())) {
844 return make_pair(false, "");
845 }
846 } else {
847 out->Print("try:\n");
848 {
849 IndentScope raii_dict_try_indent(out.get());
850 out->Print(
851 "# THESE ELEMENTS WILL BE DEPRECATED.\n"
852 "# Please use the generated *_pb2_grpc.py files instead.\n");
853 if (!PrintPreamble(out.get())) {
854 return make_pair(false, "");
855 }
856 if (!PrintBetaPreamble(out.get())) {
857 return make_pair(false, "");
858 }
859 if (!PrintGAServices(out.get())) {
860 return make_pair(false, "");
861 }
862 if (!PrintBetaServices(out.get())) {
863 return make_pair(false, "");
864 }
865 }
866 out->Print("except ImportError:\n");
867 {
868 IndentScope raii_dict_except_indent(out.get());
869 out->Print("pass");
870 }
871 }
872 }
873 return make_pair(true, std::move(output));
874 }
875
876 } // namespace
877
GeneratorConfiguration()878 GeneratorConfiguration::GeneratorConfiguration()
879 : grpc_package_root("grpc"),
880 beta_package_root("grpc.beta"),
881 import_prefix(""),
882 grpc_tools_version("") {}
883
GeneratorConfiguration(std::string version)884 GeneratorConfiguration::GeneratorConfiguration(std::string version)
885 : grpc_package_root("grpc"),
886 beta_package_root("grpc.beta"),
887 import_prefix(""),
888 grpc_tools_version(version) {}
889
PythonGrpcGenerator(const GeneratorConfiguration & config)890 PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
891 : config_(config) {}
892
~PythonGrpcGenerator()893 PythonGrpcGenerator::~PythonGrpcGenerator() {}
894
GenerateGrpc(GeneratorContext * context,PrivateGenerator & generator,std::string file_name,bool generate_in_pb2_grpc)895 static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
896 std::string file_name, bool generate_in_pb2_grpc) {
897 bool success;
898 std::unique_ptr<ZeroCopyOutputStream> output;
899 std::unique_ptr<CodedOutputStream> coded_output;
900 std::string grpc_code;
901
902 if (generate_in_pb2_grpc) {
903 output.reset(context->Open(file_name));
904 generator.generate_in_pb2_grpc = true;
905 } else {
906 output.reset(context->OpenForInsert(file_name, "module_scope"));
907 generator.generate_in_pb2_grpc = false;
908 }
909
910 coded_output.reset(new CodedOutputStream(output.get()));
911 tie(success, grpc_code) = generator.GetGrpcServices();
912
913 if (success) {
914 coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
915 return true;
916 } else {
917 return false;
918 }
919 }
920
ParseParameters(const std::string & parameter,std::string * grpc_version,std::vector<std::string> * strip_prefixes,std::string * error)921 static bool ParseParameters(const std::string& parameter,
922 std::string* grpc_version,
923 std::vector<std::string>* strip_prefixes,
924 std::string* error) {
925 std::vector<std::string> comma_delimited_parameters;
926 grpc_python_generator::Split(parameter, ',', &comma_delimited_parameters);
927 if (comma_delimited_parameters.size() == 1 &&
928 comma_delimited_parameters[0].empty()) {
929 *grpc_version = "grpc_2_0";
930 } else if (comma_delimited_parameters.size() == 1) {
931 *grpc_version = comma_delimited_parameters[0];
932 } else if (comma_delimited_parameters.size() == 2) {
933 *grpc_version = comma_delimited_parameters[0];
934 std::copy(comma_delimited_parameters.begin() + 1,
935 comma_delimited_parameters.end(),
936 std::back_inserter(*strip_prefixes));
937 } else {
938 *error = "--grpc_python_out received too many comma-delimited parameters.";
939 return false;
940 }
941 return true;
942 }
943
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const944 bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
945 const std::string& parameter,
946 GeneratorContext* context,
947 std::string* error) const {
948 // Get output file name.
949 std::string pb2_file_name;
950 std::string pb2_grpc_file_name;
951 static const int proto_suffix_length = strlen(".proto");
952 if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
953 file->name().find_last_of(".proto") == file->name().size() - 1) {
954 std::string base(
955 file->name().substr(0, file->name().size() - proto_suffix_length));
956 std::replace(base.begin(), base.end(), '-', '_');
957 pb2_file_name = base + "_pb2.py";
958 pb2_grpc_file_name = base + "_pb2_grpc.py";
959 } else {
960 *error = "Invalid proto file name. Proto file must end with .proto";
961 return false;
962 }
963 generator_file_name = file->name();
964
965 ProtoBufFile pbfile(file);
966 std::string grpc_version;
967 GeneratorConfiguration extended_config(config_);
968 bool success = ParseParameters(parameter, &grpc_version,
969 &(extended_config.prefixes_to_filter), error);
970 PrivateGenerator generator(extended_config, &pbfile);
971 if (!success) return false;
972 if (grpc_version == "grpc_2_0") {
973 return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
974 } else if (grpc_version == "grpc_1_0") {
975 return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
976 GenerateGrpc(context, generator, pb2_file_name, false);
977 } else {
978 *error = "Invalid grpc version '" + grpc_version + "'.";
979 return false;
980 }
981 }
982
983 } // namespace grpc_python_generator
984