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 <algorithm>
20 #include <cassert>
21 #include <cctype>
22 #include <cstring>
23 #include <fstream>
24 #include <iostream>
25 #include <map>
26 #include <memory>
27 #include <ostream>
28 #include <set>
29 #include <sstream>
30 #include <tuple>
31 #include <vector>
32 
33 #include "src/compiler/config.h"
34 #include "src/compiler/generator_helpers.h"
35 #include "src/compiler/protobuf_plugin.h"
36 #include "src/compiler/python_generator.h"
37 #include "src/compiler/python_generator_helpers.h"
38 #include "src/compiler/python_private_generator.h"
39 
40 using grpc::protobuf::FileDescriptor;
41 using grpc::protobuf::compiler::GeneratorContext;
42 using grpc::protobuf::io::CodedOutputStream;
43 using grpc::protobuf::io::ZeroCopyOutputStream;
44 using std::make_pair;
45 using std::map;
46 using std::pair;
47 using std::replace;
48 using std::set;
49 using std::tuple;
50 using std::vector;
51 
52 namespace grpc_python_generator {
53 
54 grpc::string generator_file_name;
55 
56 namespace {
57 
58 typedef map<grpc::string, grpc::string> StringMap;
59 typedef vector<grpc::string> StringVector;
60 typedef tuple<grpc::string, grpc::string> StringPair;
61 typedef set<StringPair> StringPairSet;
62 
63 // Provides RAII indentation handling. Use as:
64 // {
65 //   IndentScope raii_my_indent_var_name_here(my_py_printer);
66 //   // constructor indented my_py_printer
67 //   ...
68 //   // destructor called at end of scope, un-indenting my_py_printer
69 // }
70 class IndentScope {
71  public:
IndentScope(grpc_generator::Printer * printer)72   explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) {
73     printer_->Indent();
74   }
75 
~IndentScope()76   ~IndentScope() { printer_->Outdent(); }
77 
78  private:
79   grpc_generator::Printer* printer_;
80 };
81 
PrivateGenerator(const GeneratorConfiguration & config,const grpc_generator::File * file)82 PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
83                                    const grpc_generator::File* file)
84     : config(config), file(file) {}
85 
PrintAllComments(StringVector comments,grpc_generator::Printer * out)86 void PrivateGenerator::PrintAllComments(StringVector comments,
87                                         grpc_generator::Printer* out) {
88   if (comments.empty()) {
89     // Python requires code structures like class and def to have
90     // a body, even if it is just "pass" or a docstring.  We need
91     // to ensure not to generate empty bodies. We could do something
92     // smarter and more sophisticated, but at the moment, if there is
93     // no docstring to print, we simply emit "pass" to ensure validity
94     // of the generated code.
95     out->Print("# missing associated documentation comment in .proto file\n");
96     out->Print("pass\n");
97     return;
98   }
99   out->Print("\"\"\"");
100   for (StringVector::iterator it = comments.begin(); it != comments.end();
101        ++it) {
102     size_t start_pos = it->find_first_not_of(' ');
103     if (start_pos != grpc::string::npos) {
104       out->PrintRaw(it->c_str() + start_pos);
105     }
106     out->Print("\n");
107   }
108   out->Print("\"\"\"\n");
109 }
110 
PrintBetaServicer(const grpc_generator::Service * service,grpc_generator::Printer * out)111 bool PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service,
112                                          grpc_generator::Printer* out) {
113   StringMap service_dict;
114   service_dict["Service"] = service->name();
115   out->Print("\n\n");
116   out->Print(service_dict, "class Beta$Service$Servicer(object):\n");
117   {
118     IndentScope raii_class_indent(out);
119     out->Print(
120         "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
121         "\nIt is recommended to use the GA API (classes and functions in this\n"
122         "file not marked beta) for all further purposes. This class was "
123         "generated\n"
124         "only to ease transition from grpcio<0.15.0 to "
125         "grpcio>=0.15.0.\"\"\"\n");
126     StringVector service_comments = service->GetAllComments();
127     PrintAllComments(service_comments, out);
128     for (int i = 0; i < service->method_count(); ++i) {
129       auto method = service->method(i);
130       grpc::string arg_name =
131           method->ClientStreaming() ? "request_iterator" : "request";
132       StringMap method_dict;
133       method_dict["Method"] = method->name();
134       method_dict["ArgName"] = arg_name;
135       out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
136       {
137         IndentScope raii_method_indent(out);
138         StringVector method_comments = method->GetAllComments();
139         PrintAllComments(method_comments, out);
140         out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
141       }
142     }
143   }
144   return true;
145 }
146 
PrintBetaStub(const grpc_generator::Service * service,grpc_generator::Printer * out)147 bool PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service,
148                                      grpc_generator::Printer* out) {
149   StringMap service_dict;
150   service_dict["Service"] = service->name();
151   out->Print("\n\n");
152   out->Print(service_dict, "class Beta$Service$Stub(object):\n");
153   {
154     IndentScope raii_class_indent(out);
155     out->Print(
156         "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
157         "\nIt is recommended to use the GA API (classes and functions in this\n"
158         "file not marked beta) for all further purposes. This class was "
159         "generated\n"
160         "only to ease transition from grpcio<0.15.0 to "
161         "grpcio>=0.15.0.\"\"\"\n");
162     StringVector service_comments = service->GetAllComments();
163     PrintAllComments(service_comments, out);
164     for (int i = 0; i < service->method_count(); ++i) {
165       auto method = service->method(i);
166       grpc::string arg_name =
167           method->ClientStreaming() ? "request_iterator" : "request";
168       StringMap method_dict;
169       method_dict["Method"] = method->name();
170       method_dict["ArgName"] = arg_name;
171       out->Print(method_dict,
172                  "def $Method$(self, $ArgName$, timeout, metadata=None, "
173                  "with_call=False, protocol_options=None):\n");
174       {
175         IndentScope raii_method_indent(out);
176         StringVector method_comments = method->GetAllComments();
177         PrintAllComments(method_comments, out);
178         out->Print("raise NotImplementedError()\n");
179       }
180       if (!method->ServerStreaming()) {
181         out->Print(method_dict, "$Method$.future = None\n");
182       }
183     }
184   }
185   return true;
186 }
187 
PrintBetaServerFactory(const grpc::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)188 bool PrivateGenerator::PrintBetaServerFactory(
189     const grpc::string& package_qualified_service_name,
190     const grpc_generator::Service* service, grpc_generator::Printer* out) {
191   StringMap service_dict;
192   service_dict["Service"] = service->name();
193   out->Print("\n\n");
194   out->Print(service_dict,
195              "def beta_create_$Service$_server(servicer, pool=None, "
196              "pool_size=None, default_timeout=None, maximum_timeout=None):\n");
197   {
198     IndentScope raii_create_server_indent(out);
199     out->Print(
200         "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
201         "\nIt is recommended to use the GA API (classes and functions in this\n"
202         "file not marked beta) for all further purposes. This function was\n"
203         "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
204         "\"\"\"\n");
205     StringMap method_implementation_constructors;
206     StringMap input_message_modules_and_classes;
207     StringMap output_message_modules_and_classes;
208     for (int i = 0; i < service->method_count(); ++i) {
209       auto method = service->method(i);
210       const grpc::string method_implementation_constructor =
211           grpc::string(method->ClientStreaming() ? "stream_" : "unary_") +
212           grpc::string(method->ServerStreaming() ? "stream_" : "unary_") +
213           "inline";
214       grpc::string input_message_module_and_class;
215       if (!method->get_module_and_message_path_input(
216               &input_message_module_and_class, generator_file_name,
217               generate_in_pb2_grpc, config.import_prefix,
218               config.prefixes_to_filter)) {
219         return false;
220       }
221       grpc::string output_message_module_and_class;
222       if (!method->get_module_and_message_path_output(
223               &output_message_module_and_class, generator_file_name,
224               generate_in_pb2_grpc, config.import_prefix,
225               config.prefixes_to_filter)) {
226         return false;
227       }
228       method_implementation_constructors.insert(
229           make_pair(method->name(), method_implementation_constructor));
230       input_message_modules_and_classes.insert(
231           make_pair(method->name(), input_message_module_and_class));
232       output_message_modules_and_classes.insert(
233           make_pair(method->name(), output_message_module_and_class));
234     }
235     StringMap method_dict;
236     method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
237     out->Print("request_deserializers = {\n");
238     for (StringMap::iterator name_and_input_module_class_pair =
239              input_message_modules_and_classes.begin();
240          name_and_input_module_class_pair !=
241          input_message_modules_and_classes.end();
242          name_and_input_module_class_pair++) {
243       method_dict["MethodName"] = name_and_input_module_class_pair->first;
244       method_dict["InputTypeModuleAndClass"] =
245           name_and_input_module_class_pair->second;
246       IndentScope raii_indent(out);
247       out->Print(method_dict,
248                  "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
249                  "$InputTypeModuleAndClass$.FromString,\n");
250     }
251     out->Print("}\n");
252     out->Print("response_serializers = {\n");
253     for (StringMap::iterator name_and_output_module_class_pair =
254              output_message_modules_and_classes.begin();
255          name_and_output_module_class_pair !=
256          output_message_modules_and_classes.end();
257          name_and_output_module_class_pair++) {
258       method_dict["MethodName"] = name_and_output_module_class_pair->first;
259       method_dict["OutputTypeModuleAndClass"] =
260           name_and_output_module_class_pair->second;
261       IndentScope raii_indent(out);
262       out->Print(method_dict,
263                  "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
264                  "$OutputTypeModuleAndClass$.SerializeToString,\n");
265     }
266     out->Print("}\n");
267     out->Print("method_implementations = {\n");
268     for (StringMap::iterator name_and_implementation_constructor =
269              method_implementation_constructors.begin();
270          name_and_implementation_constructor !=
271          method_implementation_constructors.end();
272          name_and_implementation_constructor++) {
273       method_dict["Method"] = name_and_implementation_constructor->first;
274       method_dict["Constructor"] = name_and_implementation_constructor->second;
275       IndentScope raii_descriptions_indent(out);
276       const grpc::string method_name =
277           name_and_implementation_constructor->first;
278       out->Print(method_dict,
279                  "(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
280                  "face_utilities.$Constructor$(servicer.$Method$),\n");
281     }
282     out->Print("}\n");
283     out->Print(
284         "server_options = beta_implementations.server_options("
285         "request_deserializers=request_deserializers, "
286         "response_serializers=response_serializers, "
287         "thread_pool=pool, thread_pool_size=pool_size, "
288         "default_timeout=default_timeout, "
289         "maximum_timeout=maximum_timeout)\n");
290     out->Print(
291         "return beta_implementations.server(method_implementations, "
292         "options=server_options)\n");
293   }
294   return true;
295 }
296 
PrintBetaStubFactory(const grpc::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)297 bool PrivateGenerator::PrintBetaStubFactory(
298     const grpc::string& package_qualified_service_name,
299     const grpc_generator::Service* service, grpc_generator::Printer* out) {
300   StringMap dict;
301   dict["Service"] = service->name();
302   out->Print("\n\n");
303   out->Print(dict,
304              "def beta_create_$Service$_stub(channel, host=None,"
305              " metadata_transformer=None, pool=None, pool_size=None):\n");
306   {
307     IndentScope raii_create_server_indent(out);
308     out->Print(
309         "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
310         "\nIt is recommended to use the GA API (classes and functions in this\n"
311         "file not marked beta) for all further purposes. This function was\n"
312         "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
313         "\"\"\"\n");
314     StringMap method_cardinalities;
315     StringMap input_message_modules_and_classes;
316     StringMap output_message_modules_and_classes;
317     for (int i = 0; i < service->method_count(); ++i) {
318       auto method = service->method(i);
319       const grpc::string method_cardinality =
320           grpc::string(method->ClientStreaming() ? "STREAM" : "UNARY") + "_" +
321           grpc::string(method->ServerStreaming() ? "STREAM" : "UNARY");
322       grpc::string input_message_module_and_class;
323       if (!method->get_module_and_message_path_input(
324               &input_message_module_and_class, generator_file_name,
325               generate_in_pb2_grpc, config.import_prefix,
326               config.prefixes_to_filter)) {
327         return false;
328       }
329       grpc::string output_message_module_and_class;
330       if (!method->get_module_and_message_path_output(
331               &output_message_module_and_class, generator_file_name,
332               generate_in_pb2_grpc, config.import_prefix,
333               config.prefixes_to_filter)) {
334         return false;
335       }
336       method_cardinalities.insert(
337           make_pair(method->name(), method_cardinality));
338       input_message_modules_and_classes.insert(
339           make_pair(method->name(), input_message_module_and_class));
340       output_message_modules_and_classes.insert(
341           make_pair(method->name(), output_message_module_and_class));
342     }
343     StringMap method_dict;
344     method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
345     out->Print("request_serializers = {\n");
346     for (StringMap::iterator name_and_input_module_class_pair =
347              input_message_modules_and_classes.begin();
348          name_and_input_module_class_pair !=
349          input_message_modules_and_classes.end();
350          name_and_input_module_class_pair++) {
351       method_dict["MethodName"] = name_and_input_module_class_pair->first;
352       method_dict["InputTypeModuleAndClass"] =
353           name_and_input_module_class_pair->second;
354       IndentScope raii_indent(out);
355       out->Print(method_dict,
356                  "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
357                  "$InputTypeModuleAndClass$.SerializeToString,\n");
358     }
359     out->Print("}\n");
360     out->Print("response_deserializers = {\n");
361     for (StringMap::iterator name_and_output_module_class_pair =
362              output_message_modules_and_classes.begin();
363          name_and_output_module_class_pair !=
364          output_message_modules_and_classes.end();
365          name_and_output_module_class_pair++) {
366       method_dict["MethodName"] = name_and_output_module_class_pair->first;
367       method_dict["OutputTypeModuleAndClass"] =
368           name_and_output_module_class_pair->second;
369       IndentScope raii_indent(out);
370       out->Print(method_dict,
371                  "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
372                  "$OutputTypeModuleAndClass$.FromString,\n");
373     }
374     out->Print("}\n");
375     out->Print("cardinalities = {\n");
376     for (StringMap::iterator name_and_cardinality =
377              method_cardinalities.begin();
378          name_and_cardinality != method_cardinalities.end();
379          name_and_cardinality++) {
380       method_dict["Method"] = name_and_cardinality->first;
381       method_dict["Cardinality"] = name_and_cardinality->second;
382       IndentScope raii_descriptions_indent(out);
383       out->Print(method_dict,
384                  "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n");
385     }
386     out->Print("}\n");
387     out->Print(
388         "stub_options = beta_implementations.stub_options("
389         "host=host, metadata_transformer=metadata_transformer, "
390         "request_serializers=request_serializers, "
391         "response_deserializers=response_deserializers, "
392         "thread_pool=pool, thread_pool_size=pool_size)\n");
393     out->Print(method_dict,
394                "return beta_implementations.dynamic_stub(channel, "
395                "\'$PackageQualifiedServiceName$\', "
396                "cardinalities, options=stub_options)\n");
397   }
398   return true;
399 }
400 
PrintStub(const grpc::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)401 bool PrivateGenerator::PrintStub(
402     const grpc::string& package_qualified_service_name,
403     const grpc_generator::Service* service, grpc_generator::Printer* out) {
404   StringMap dict;
405   dict["Service"] = service->name();
406   out->Print("\n\n");
407   out->Print(dict, "class $Service$Stub(object):\n");
408   {
409     IndentScope raii_class_indent(out);
410     StringVector service_comments = service->GetAllComments();
411     PrintAllComments(service_comments, out);
412     out->Print("\n");
413     out->Print("def __init__(self, channel):\n");
414     {
415       IndentScope raii_init_indent(out);
416       out->Print("\"\"\"Constructor.\n");
417       out->Print("\n");
418       out->Print("Args:\n");
419       {
420         IndentScope raii_args_indent(out);
421         out->Print("channel: A grpc.Channel.\n");
422       }
423       out->Print("\"\"\"\n");
424       for (int i = 0; i < service->method_count(); ++i) {
425         auto method = service->method(i);
426         grpc::string multi_callable_constructor =
427             grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
428             grpc::string(method->ServerStreaming() ? "stream" : "unary");
429         grpc::string request_module_and_class;
430         if (!method->get_module_and_message_path_input(
431                 &request_module_and_class, generator_file_name,
432                 generate_in_pb2_grpc, config.import_prefix,
433                 config.prefixes_to_filter)) {
434           return false;
435         }
436         grpc::string response_module_and_class;
437         if (!method->get_module_and_message_path_output(
438                 &response_module_and_class, generator_file_name,
439                 generate_in_pb2_grpc, config.import_prefix,
440                 config.prefixes_to_filter)) {
441           return false;
442         }
443         StringMap method_dict;
444         method_dict["Method"] = method->name();
445         method_dict["MultiCallableConstructor"] = multi_callable_constructor;
446         out->Print(method_dict,
447                    "self.$Method$ = channel.$MultiCallableConstructor$(\n");
448         {
449           method_dict["PackageQualifiedService"] =
450               package_qualified_service_name;
451           method_dict["RequestModuleAndClass"] = request_module_and_class;
452           method_dict["ResponseModuleAndClass"] = response_module_and_class;
453           IndentScope raii_first_attribute_indent(out);
454           IndentScope raii_second_attribute_indent(out);
455           out->Print(method_dict, "'/$PackageQualifiedService$/$Method$',\n");
456           out->Print(method_dict,
457                      "request_serializer=$RequestModuleAndClass$."
458                      "SerializeToString,\n");
459           out->Print(
460               method_dict,
461               "response_deserializer=$ResponseModuleAndClass$.FromString,\n");
462           out->Print(")\n");
463         }
464       }
465     }
466   }
467   return true;
468 }
469 
PrintServicer(const grpc_generator::Service * service,grpc_generator::Printer * out)470 bool PrivateGenerator::PrintServicer(const grpc_generator::Service* service,
471                                      grpc_generator::Printer* out) {
472   StringMap service_dict;
473   service_dict["Service"] = service->name();
474   out->Print("\n\n");
475   out->Print(service_dict, "class $Service$Servicer(object):\n");
476   {
477     IndentScope raii_class_indent(out);
478     StringVector service_comments = service->GetAllComments();
479     PrintAllComments(service_comments, out);
480     for (int i = 0; i < service->method_count(); ++i) {
481       auto method = service->method(i);
482       grpc::string arg_name =
483           method->ClientStreaming() ? "request_iterator" : "request";
484       StringMap method_dict;
485       method_dict["Method"] = method->name();
486       method_dict["ArgName"] = arg_name;
487       out->Print("\n");
488       out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
489       {
490         IndentScope raii_method_indent(out);
491         StringVector method_comments = method->GetAllComments();
492         PrintAllComments(method_comments, out);
493         out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n");
494         out->Print("context.set_details('Method not implemented!')\n");
495         out->Print("raise NotImplementedError('Method not implemented!')\n");
496       }
497     }
498   }
499   return true;
500 }
501 
PrintAddServicerToServer(const grpc::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)502 bool PrivateGenerator::PrintAddServicerToServer(
503     const grpc::string& package_qualified_service_name,
504     const grpc_generator::Service* service, grpc_generator::Printer* out) {
505   StringMap service_dict;
506   service_dict["Service"] = service->name();
507   out->Print("\n\n");
508   out->Print(service_dict,
509              "def add_$Service$Servicer_to_server(servicer, server):\n");
510   {
511     IndentScope raii_class_indent(out);
512     out->Print("rpc_method_handlers = {\n");
513     {
514       IndentScope raii_dict_first_indent(out);
515       IndentScope raii_dict_second_indent(out);
516       for (int i = 0; i < service->method_count(); ++i) {
517         auto method = service->method(i);
518         grpc::string method_handler_constructor =
519             grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
520             grpc::string(method->ServerStreaming() ? "stream" : "unary") +
521             "_rpc_method_handler";
522         grpc::string request_module_and_class;
523         if (!method->get_module_and_message_path_input(
524                 &request_module_and_class, generator_file_name,
525                 generate_in_pb2_grpc, config.import_prefix,
526                 config.prefixes_to_filter)) {
527           return false;
528         }
529         grpc::string response_module_and_class;
530         if (!method->get_module_and_message_path_output(
531                 &response_module_and_class, generator_file_name,
532                 generate_in_pb2_grpc, config.import_prefix,
533                 config.prefixes_to_filter)) {
534           return false;
535         }
536         StringMap method_dict;
537         method_dict["Method"] = method->name();
538         method_dict["MethodHandlerConstructor"] = method_handler_constructor;
539         method_dict["RequestModuleAndClass"] = request_module_and_class;
540         method_dict["ResponseModuleAndClass"] = response_module_and_class;
541         out->Print(method_dict,
542                    "'$Method$': grpc.$MethodHandlerConstructor$(\n");
543         {
544           IndentScope raii_call_first_indent(out);
545           IndentScope raii_call_second_indent(out);
546           out->Print(method_dict, "servicer.$Method$,\n");
547           out->Print(
548               method_dict,
549               "request_deserializer=$RequestModuleAndClass$.FromString,\n");
550           out->Print(
551               method_dict,
552               "response_serializer=$ResponseModuleAndClass$.SerializeToString,"
553               "\n");
554         }
555         out->Print("),\n");
556       }
557     }
558     StringMap method_dict;
559     method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
560     out->Print("}\n");
561     out->Print("generic_handler = grpc.method_handlers_generic_handler(\n");
562     {
563       IndentScope raii_call_first_indent(out);
564       IndentScope raii_call_second_indent(out);
565       out->Print(method_dict,
566                  "'$PackageQualifiedServiceName$', rpc_method_handlers)\n");
567     }
568     out->Print("server.add_generic_rpc_handlers((generic_handler,))\n");
569   }
570   return true;
571 }
572 
PrintBetaPreamble(grpc_generator::Printer * out)573 bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) {
574   StringMap var;
575   var["Package"] = config.beta_package_root;
576   out->Print(var,
577              "from $Package$ import implementations as beta_implementations\n");
578   out->Print(var, "from $Package$ import interfaces as beta_interfaces\n");
579   out->Print("from grpc.framework.common import cardinality\n");
580   out->Print(
581       "from grpc.framework.interfaces.face import utilities as "
582       "face_utilities\n");
583   return true;
584 }
585 
PrintPreamble(grpc_generator::Printer * out)586 bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) {
587   StringMap var;
588   var["Package"] = config.grpc_package_root;
589   out->Print(var, "import $Package$\n");
590   if (generate_in_pb2_grpc) {
591     out->Print("\n");
592     StringPairSet imports_set;
593     for (int i = 0; i < file->service_count(); ++i) {
594       auto service = file->service(i);
595       for (int j = 0; j < service->method_count(); ++j) {
596         auto method = service.get()->method(j);
597 
598         grpc::string input_type_file_name = method->get_input_type_name();
599         grpc::string input_module_name =
600             ModuleName(input_type_file_name, config.import_prefix,
601                        config.prefixes_to_filter);
602         grpc::string input_module_alias =
603             ModuleAlias(input_type_file_name, config.import_prefix,
604                         config.prefixes_to_filter);
605         imports_set.insert(
606             std::make_tuple(input_module_name, input_module_alias));
607 
608         grpc::string output_type_file_name = method->get_output_type_name();
609         grpc::string output_module_name =
610             ModuleName(output_type_file_name, config.import_prefix,
611                        config.prefixes_to_filter);
612         grpc::string output_module_alias =
613             ModuleAlias(output_type_file_name, config.import_prefix,
614                         config.prefixes_to_filter);
615         imports_set.insert(
616             std::make_tuple(output_module_name, output_module_alias));
617       }
618     }
619 
620     for (StringPairSet::iterator it = imports_set.begin();
621          it != imports_set.end(); ++it) {
622       auto module_name = std::get<0>(*it);
623       var["ModuleAlias"] = std::get<1>(*it);
624       const size_t last_dot_pos = module_name.rfind('.');
625       if (last_dot_pos == grpc::string::npos) {
626         var["ImportStatement"] = "import " + module_name;
627       } else {
628         var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) +
629                                  " import " +
630                                  module_name.substr(last_dot_pos + 1);
631       }
632       out->Print(var, "$ImportStatement$ as $ModuleAlias$\n");
633     }
634   }
635   return true;
636 }
637 
PrintGAServices(grpc_generator::Printer * out)638 bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) {
639   grpc::string package = file->package();
640   if (!package.empty()) {
641     package = package.append(".");
642   }
643   for (int i = 0; i < file->service_count(); ++i) {
644     auto service = file->service(i);
645     grpc::string package_qualified_service_name = package + service->name();
646     if (!(PrintStub(package_qualified_service_name, service.get(), out) &&
647           PrintServicer(service.get(), out) &&
648           PrintAddServicerToServer(package_qualified_service_name,
649                                    service.get(), out))) {
650       return false;
651     }
652   }
653   return true;
654 }
655 
PrintBetaServices(grpc_generator::Printer * out)656 bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) {
657   grpc::string package = file->package();
658   if (!package.empty()) {
659     package = package.append(".");
660   }
661   for (int i = 0; i < file->service_count(); ++i) {
662     auto service = file->service(i);
663     grpc::string package_qualified_service_name = package + service->name();
664     if (!(PrintBetaServicer(service.get(), out) &&
665           PrintBetaStub(service.get(), out) &&
666           PrintBetaServerFactory(package_qualified_service_name, service.get(),
667                                  out) &&
668           PrintBetaStubFactory(package_qualified_service_name, service.get(),
669                                out))) {
670       return false;
671     }
672   }
673   return true;
674 }
675 
GetGrpcServices()676 pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() {
677   grpc::string output;
678   {
679     // Scope the output stream so it closes and finalizes output to the string.
680     auto out = file->CreatePrinter(&output);
681     if (generate_in_pb2_grpc) {
682       out->Print(
683           "# Generated by the gRPC Python protocol compiler plugin. "
684           "DO NOT EDIT!\n");
685       if (!PrintPreamble(out.get())) {
686         return make_pair(false, "");
687       }
688       if (!PrintGAServices(out.get())) {
689         return make_pair(false, "");
690       }
691     } else {
692       out->Print("try:\n");
693       {
694         IndentScope raii_dict_try_indent(out.get());
695         out->Print(
696             "# THESE ELEMENTS WILL BE DEPRECATED.\n"
697             "# Please use the generated *_pb2_grpc.py files instead.\n");
698         if (!PrintPreamble(out.get())) {
699           return make_pair(false, "");
700         }
701         if (!PrintBetaPreamble(out.get())) {
702           return make_pair(false, "");
703         }
704         if (!PrintGAServices(out.get())) {
705           return make_pair(false, "");
706         }
707         if (!PrintBetaServices(out.get())) {
708           return make_pair(false, "");
709         }
710       }
711       out->Print("except ImportError:\n");
712       {
713         IndentScope raii_dict_except_indent(out.get());
714         out->Print("pass");
715       }
716     }
717   }
718   return make_pair(true, std::move(output));
719 }
720 
721 }  // namespace
722 
GeneratorConfiguration()723 GeneratorConfiguration::GeneratorConfiguration()
724     : grpc_package_root("grpc"),
725       beta_package_root("grpc.beta"),
726       import_prefix("") {}
727 
PythonGrpcGenerator(const GeneratorConfiguration & config)728 PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
729     : config_(config) {}
730 
~PythonGrpcGenerator()731 PythonGrpcGenerator::~PythonGrpcGenerator() {}
732 
GenerateGrpc(GeneratorContext * context,PrivateGenerator & generator,grpc::string file_name,bool generate_in_pb2_grpc)733 static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
734                          grpc::string file_name, bool generate_in_pb2_grpc) {
735   bool success;
736   std::unique_ptr<ZeroCopyOutputStream> output;
737   std::unique_ptr<CodedOutputStream> coded_output;
738   grpc::string grpc_code;
739 
740   if (generate_in_pb2_grpc) {
741     output.reset(context->Open(file_name));
742     generator.generate_in_pb2_grpc = true;
743   } else {
744     output.reset(context->OpenForInsert(file_name, "module_scope"));
745     generator.generate_in_pb2_grpc = false;
746   }
747 
748   coded_output.reset(new CodedOutputStream(output.get()));
749   tie(success, grpc_code) = generator.GetGrpcServices();
750 
751   if (success) {
752     coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
753     return true;
754   } else {
755     return false;
756   }
757 }
758 
Generate(const FileDescriptor * file,const grpc::string & parameter,GeneratorContext * context,grpc::string * error) const759 bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
760                                    const grpc::string& parameter,
761                                    GeneratorContext* context,
762                                    grpc::string* error) const {
763   // Get output file name.
764   grpc::string pb2_file_name;
765   grpc::string pb2_grpc_file_name;
766   static const int proto_suffix_length = strlen(".proto");
767   if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
768       file->name().find_last_of(".proto") == file->name().size() - 1) {
769     grpc::string base =
770         file->name().substr(0, file->name().size() - proto_suffix_length);
771     std::replace(base.begin(), base.end(), '-', '_');
772     pb2_file_name = base + "_pb2.py";
773     pb2_grpc_file_name = base + "_pb2_grpc.py";
774   } else {
775     *error = "Invalid proto file name. Proto file must end with .proto";
776     return false;
777   }
778   generator_file_name = file->name();
779 
780   ProtoBufFile pbfile(file);
781   PrivateGenerator generator(config_, &pbfile);
782   if (parameter == "" || parameter == "grpc_2_0") {
783     return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
784   } else if (parameter == "grpc_1_0") {
785     return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
786            GenerateGrpc(context, generator, pb2_file_name, false);
787   } else {
788     *error = "Invalid parameter '" + parameter + "'.";
789     return false;
790   }
791 }
792 
793 }  // namespace grpc_python_generator
794