• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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