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
40using grpc::protobuf::FileDescriptor;
41using grpc::protobuf::compiler::GeneratorContext;
42using grpc::protobuf::io::CodedOutputStream;
43using grpc::protobuf::io::ZeroCopyOutputStream;
44using std::make_pair;
45using std::map;
46using std::pair;
47using std::replace;
48using std::set;
49using std::tuple;
50using std::vector;
51
52namespace grpc_python_generator {
53
54grpc::string generator_file_name;
55
56namespace {
57
58typedef map<grpc::string, grpc::string> StringMap;
59typedef vector<grpc::string> StringVector;
60typedef tuple<grpc::string, grpc::string> StringPair;
61typedef 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// }
70class IndentScope {
71 public:
72 explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) {
73 printer_->Indent();
74 }
75
76 ~IndentScope() { printer_->Outdent(); }
77
78 private:
79 grpc_generator::Printer* printer_;
80};
81
82PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
83 const grpc_generator::File* file)
84 : config(config), file(file) {}
85
86void 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
111bool 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
147bool 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
188bool 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
297bool 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
401bool 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
470bool 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
502bool 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
573bool 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
586bool 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
638bool 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
656bool 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
676pair<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
723GeneratorConfiguration::GeneratorConfiguration()
724 : grpc_package_root("grpc"),
725 beta_package_root("grpc.beta"),
726 import_prefix("") {}
727
728PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
729 : config_(config) {}
730
731PythonGrpcGenerator::~PythonGrpcGenerator() {}
732
733static 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
759static bool ParseParameters(const grpc::string& parameter,
760 grpc::string* grpc_version,
761 std::vector<grpc::string>* strip_prefixes,
762 grpc::string* error) {
763 std::vector<grpc::string> comma_delimited_parameters;
764 grpc_python_generator::Split(parameter, ',', &comma_delimited_parameters);
765 if (comma_delimited_parameters.size() == 1 &&
766 comma_delimited_parameters[0].empty()) {
767 *grpc_version = "grpc_2_0";
768 } else if (comma_delimited_parameters.size() == 1) {
769 *grpc_version = comma_delimited_parameters[0];
770 } else if (comma_delimited_parameters.size() == 2) {
771 *grpc_version = comma_delimited_parameters[0];
772 std::copy(comma_delimited_parameters.begin() + 1,
773 comma_delimited_parameters.end(),
774 std::back_inserter(*strip_prefixes));
775 } else {
776 *error = "--grpc_python_out received too many comma-delimited parameters.";
777 return false;
778 }
779 return true;
780}
781
782bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
783 const grpc::string& parameter,
784 GeneratorContext* context,
785 grpc::string* error) const {
786 // Get output file name.
787 grpc::string pb2_file_name;
788 grpc::string pb2_grpc_file_name;
789 static const int proto_suffix_length = strlen(".proto");
790 if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
791 file->name().find_last_of(".proto") == file->name().size() - 1) {
792 grpc::string base =
793 file->name().substr(0, file->name().size() - proto_suffix_length);
794 std::replace(base.begin(), base.end(), '-', '_');
795 pb2_file_name = base + "_pb2.py";
796 pb2_grpc_file_name = base + "_pb2_grpc.py";
797 } else {
798 *error = "Invalid proto file name. Proto file must end with .proto";
799 return false;
800 }
801 generator_file_name = file->name();
802
803 ProtoBufFile pbfile(file);
804 grpc::string grpc_version;
805 GeneratorConfiguration extended_config(config_);
806 bool success = ParseParameters(parameter, &grpc_version,
807 &(extended_config.prefixes_to_filter), error);
808 PrivateGenerator generator(extended_config, &pbfile);
809 if (!success) return false;
810 if (grpc_version == "grpc_2_0") {
811 return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
812 } else if (grpc_version == "grpc_1_0") {
813 return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
814 GenerateGrpc(context, generator, pb2_file_name, false);
815 } else {
816 *error = "Invalid grpc version '" + grpc_version + "'.";
817 return false;
818 }
819}
820
821} // namespace grpc_python_generator
822