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 }
578 return true;
579 }
580
581 /* Prints out a service class used as a container for static methods pertaining
582 * to a class. This class has the exact name of service written in the ".proto"
583 * file, with no suffixes. Since this class merely acts as a namespace, it
584 * should never be instantiated.
585 */
PrintServiceClass(const std::string & package_qualified_service_name,const grpc_generator::Service * service,grpc_generator::Printer * out)586 bool PrivateGenerator::PrintServiceClass(
587 const std::string& package_qualified_service_name,
588 const grpc_generator::Service* service, grpc_generator::Printer* out) {
589 StringMap dict;
590 dict["Service"] = service->name();
591 out->Print("\n\n");
592 out->Print(" # This class is part of an EXPERIMENTAL API.\n");
593 out->Print(dict, "class $Service$(object):\n");
594 {
595 IndentScope class_indent(out);
596 StringVector service_comments = service->GetAllComments();
597 PrintAllComments(service_comments, out);
598 for (int i = 0; i < service->method_count(); ++i) {
599 const auto& method = service->method(i);
600 std::string request_module_and_class;
601 if (!method->get_module_and_message_path_input(
602 &request_module_and_class, generator_file_name,
603 generate_in_pb2_grpc, config.import_prefix,
604 config.prefixes_to_filter)) {
605 return false;
606 }
607 std::string response_module_and_class;
608 if (!method->get_module_and_message_path_output(
609 &response_module_and_class, generator_file_name,
610 generate_in_pb2_grpc, config.import_prefix,
611 config.prefixes_to_filter)) {
612 return false;
613 }
614 out->Print("\n");
615 StringMap method_dict;
616 method_dict["Method"] = method->name();
617 out->Print("@staticmethod\n");
618 out->Print(method_dict, "def $Method$(");
619 std::string request_parameter(
620 method->ClientStreaming() ? "request_iterator" : "request");
621 StringMap args_dict;
622 args_dict["RequestParameter"] = request_parameter;
623 {
624 IndentScope args_indent(out);
625 IndentScope args_double_indent(out);
626 out->Print(args_dict, "$RequestParameter$,\n");
627 out->Print("target,\n");
628 out->Print("options=(),\n");
629 out->Print("channel_credentials=None,\n");
630 out->Print("call_credentials=None,\n");
631 out->Print("insecure=False,\n");
632 out->Print("compression=None,\n");
633 out->Print("wait_for_ready=None,\n");
634 out->Print("timeout=None,\n");
635 out->Print("metadata=None):\n");
636 }
637 {
638 IndentScope method_indent(out);
639 std::string arity_method_name =
640 std::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
641 std::string(method->ServerStreaming() ? "stream" : "unary");
642 args_dict["ArityMethodName"] = arity_method_name;
643 args_dict["PackageQualifiedService"] = package_qualified_service_name;
644 args_dict["Method"] = method->name();
645 out->Print(args_dict, "return grpc.experimental.$ArityMethodName$(\n");
646 {
647 IndentScope continuation_indent(out);
648 StringMap serializer_dict;
649 out->Print(args_dict, "$RequestParameter$,\n");
650 out->Print("target,\n");
651 out->Print(args_dict, "'/$PackageQualifiedService$/$Method$',\n");
652 serializer_dict["RequestModuleAndClass"] = request_module_and_class;
653 serializer_dict["ResponseModuleAndClass"] = response_module_and_class;
654 out->Print(serializer_dict,
655 "$RequestModuleAndClass$.SerializeToString,\n");
656 out->Print(serializer_dict, "$ResponseModuleAndClass$.FromString,\n");
657 out->Print("options,\n");
658 out->Print("channel_credentials,\n");
659 out->Print("insecure,\n");
660 out->Print("call_credentials,\n");
661 out->Print("compression,\n");
662 out->Print("wait_for_ready,\n");
663 out->Print("timeout,\n");
664 out->Print("metadata,\n");
665 out->Print("_registered_method=True)\n");
666 }
667 }
668 }
669 }
670 // TODO(rbellevi): Add methods pertinent to the server side as well.
671 return true;
672 }
673
PrintBetaPreamble(grpc_generator::Printer * out)674 bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) {
675 StringMap var;
676 var["Package"] = config.beta_package_root;
677 out->Print(var,
678 "from $Package$ import implementations as beta_implementations\n");
679 out->Print(var, "from $Package$ import interfaces as beta_interfaces\n");
680 out->Print("from grpc.framework.common import cardinality\n");
681 out->Print(
682 "from grpc.framework.interfaces.face import utilities as "
683 "face_utilities\n");
684 return true;
685 }
686
PrintPreamble(grpc_generator::Printer * out)687 bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) {
688 StringMap var;
689 var["Package"] = config.grpc_package_root;
690 out->Print(var, "import $Package$\n");
691 if (config.grpc_tools_version.size() > 0) {
692 out->Print(var, "import warnings\n");
693 }
694 if (generate_in_pb2_grpc) {
695 out->Print("\n");
696 StringPairSet imports_set;
697 for (int i = 0; i < file->service_count(); ++i) {
698 auto service = file->service(i);
699 for (int j = 0; j < service->method_count(); ++j) {
700 auto method = service.get()->method(j);
701
702 std::string input_type_file_name = method->get_input_type_name();
703 std::string input_module_name =
704 ModuleName(input_type_file_name, config.import_prefix,
705 config.prefixes_to_filter);
706 std::string input_module_alias =
707 ModuleAlias(input_type_file_name, config.import_prefix,
708 config.prefixes_to_filter);
709 imports_set.insert(
710 std::make_tuple(input_module_name, input_module_alias));
711
712 std::string output_type_file_name = method->get_output_type_name();
713 std::string output_module_name =
714 ModuleName(output_type_file_name, config.import_prefix,
715 config.prefixes_to_filter);
716 std::string output_module_alias =
717 ModuleAlias(output_type_file_name, config.import_prefix,
718 config.prefixes_to_filter);
719 imports_set.insert(
720 std::make_tuple(output_module_name, output_module_alias));
721 }
722 }
723
724 for (StringPairSet::iterator it = imports_set.begin();
725 it != imports_set.end(); ++it) {
726 auto module_name = std::get<0>(*it);
727 var["ModuleAlias"] = std::get<1>(*it);
728 const size_t last_dot_pos = module_name.rfind('.');
729 if (last_dot_pos == std::string::npos) {
730 var["ImportStatement"] = "import " + module_name;
731 } else {
732 var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) +
733 " import " +
734 module_name.substr(last_dot_pos + 1);
735 }
736 out->Print(var, "$ImportStatement$ as $ModuleAlias$\n");
737 }
738
739 // Checks if generate code is used with a supported grpcio version.
740 if (config.grpc_tools_version.size() > 0) {
741 var["ToolsVersion"] = config.grpc_tools_version;
742 out->Print(var, "\nGRPC_GENERATED_VERSION = '$ToolsVersion$'\n");
743 out->Print("GRPC_VERSION = grpc.__version__\n");
744 out->Print("EXPECTED_ERROR_RELEASE = '1.65.0'\n");
745 out->Print("SCHEDULED_RELEASE_DATE = 'June 25, 2024'\n");
746 out->Print("_version_not_supported = False\n\n");
747 out->Print("try:\n");
748 {
749 IndentScope raii_import_indent(out);
750 out->Print(
751 "from grpc._utilities import first_version_is_lower\n"
752 "_version_not_supported = first_version_is_lower(GRPC_VERSION, "
753 "GRPC_GENERATED_VERSION)\n");
754 }
755 out->Print("except ImportError:\n");
756 {
757 IndentScope raii_import_error_indent(out);
758 out->Print("_version_not_supported = True\n");
759 }
760 out->Print("\nif _version_not_supported:\n");
761 {
762 IndentScope raii_warning_indent(out);
763 out->Print("warnings.warn(\n");
764 {
765 IndentScope raii_warning_string_indent(out);
766 std::string filename_without_ext = file->filename_without_ext();
767 std::replace(filename_without_ext.begin(), filename_without_ext.end(),
768 '-', '_');
769 var["Pb2GrpcFileName"] = filename_without_ext;
770 out->Print(
771 var,
772 "f'The grpc package installed is at version {GRPC_VERSION},'\n"
773 "+ f' but the generated code in $Pb2GrpcFileName$_pb2_grpc.py "
774 "depends on'\n"
775 "+ f' grpcio>={GRPC_GENERATED_VERSION}.'\n"
776 "+ f' Please upgrade your grpc module to "
777 "grpcio>={GRPC_GENERATED_VERSION}'\n"
778 "+ f' or downgrade your generated code using "
779 "grpcio-tools<={GRPC_VERSION}.'\n"
780 "+ f' This warning will become an error in "
781 "{EXPECTED_ERROR_RELEASE},'\n"
782 "+ f' scheduled for release on {SCHEDULED_RELEASE_DATE}.',\n"
783 "RuntimeWarning\n");
784 }
785 out->Print(")\n");
786 }
787 }
788 }
789 return true;
790 }
791
PrintGAServices(grpc_generator::Printer * out)792 bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) {
793 std::string package = file->package();
794 if (!package.empty()) {
795 package = package.append(".");
796 }
797 for (int i = 0; i < file->service_count(); ++i) {
798 auto service = file->service(i);
799 std::string package_qualified_service_name = package + service->name();
800 if (!(PrintStub(package_qualified_service_name, service.get(), out) &&
801 PrintServicer(service.get(), out) &&
802 PrintAddServicerToServer(package_qualified_service_name,
803 service.get(), out) &&
804 PrintServiceClass(package_qualified_service_name, service.get(),
805 out))) {
806 return false;
807 }
808 }
809 return true;
810 }
811
PrintBetaServices(grpc_generator::Printer * out)812 bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) {
813 std::string package = file->package();
814 if (!package.empty()) {
815 package = package.append(".");
816 }
817 for (int i = 0; i < file->service_count(); ++i) {
818 auto service = file->service(i);
819 std::string package_qualified_service_name = package + service->name();
820 if (!(PrintBetaServicer(service.get(), out) &&
821 PrintBetaStub(service.get(), out) &&
822 PrintBetaServerFactory(package_qualified_service_name, service.get(),
823 out) &&
824 PrintBetaStubFactory(package_qualified_service_name, service.get(),
825 out))) {
826 return false;
827 }
828 }
829 return true;
830 }
831
GetGrpcServices()832 pair<bool, std::string> PrivateGenerator::GetGrpcServices() {
833 std::string output;
834 {
835 // Scope the output stream so it closes and finalizes output to the string.
836 auto out = file->CreatePrinter(&output);
837 if (generate_in_pb2_grpc) {
838 out->Print(
839 "# Generated by the gRPC Python protocol compiler plugin. "
840 "DO NOT EDIT!\n\"\"\""
841 "Client and server classes corresponding to protobuf-defined "
842 "services.\"\"\"\n");
843 if (!PrintPreamble(out.get())) {
844 return make_pair(false, "");
845 }
846 if (!PrintGAServices(out.get())) {
847 return make_pair(false, "");
848 }
849 } else {
850 out->Print("try:\n");
851 {
852 IndentScope raii_dict_try_indent(out.get());
853 out->Print(
854 "# THESE ELEMENTS WILL BE DEPRECATED.\n"
855 "# Please use the generated *_pb2_grpc.py files instead.\n");
856 if (!PrintPreamble(out.get())) {
857 return make_pair(false, "");
858 }
859 if (!PrintBetaPreamble(out.get())) {
860 return make_pair(false, "");
861 }
862 if (!PrintGAServices(out.get())) {
863 return make_pair(false, "");
864 }
865 if (!PrintBetaServices(out.get())) {
866 return make_pair(false, "");
867 }
868 }
869 out->Print("except ImportError:\n");
870 {
871 IndentScope raii_dict_except_indent(out.get());
872 out->Print("pass");
873 }
874 }
875 }
876 return make_pair(true, std::move(output));
877 }
878
879 } // namespace
880
GeneratorConfiguration()881 GeneratorConfiguration::GeneratorConfiguration()
882 : grpc_package_root("grpc"),
883 beta_package_root("grpc.beta"),
884 import_prefix(""),
885 grpc_tools_version("") {}
886
GeneratorConfiguration(std::string version)887 GeneratorConfiguration::GeneratorConfiguration(std::string version)
888 : grpc_package_root("grpc"),
889 beta_package_root("grpc.beta"),
890 import_prefix(""),
891 grpc_tools_version(version) {}
892
PythonGrpcGenerator(const GeneratorConfiguration & config)893 PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
894 : config_(config) {}
895
~PythonGrpcGenerator()896 PythonGrpcGenerator::~PythonGrpcGenerator() {}
897
GenerateGrpc(GeneratorContext * context,PrivateGenerator & generator,std::string file_name,bool generate_in_pb2_grpc)898 static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
899 std::string file_name, bool generate_in_pb2_grpc) {
900 bool success;
901 std::unique_ptr<ZeroCopyOutputStream> output;
902 std::unique_ptr<CodedOutputStream> coded_output;
903 std::string grpc_code;
904
905 if (generate_in_pb2_grpc) {
906 output.reset(context->Open(file_name));
907 generator.generate_in_pb2_grpc = true;
908 } else {
909 output.reset(context->OpenForInsert(file_name, "module_scope"));
910 generator.generate_in_pb2_grpc = false;
911 }
912
913 coded_output.reset(new CodedOutputStream(output.get()));
914 tie(success, grpc_code) = generator.GetGrpcServices();
915
916 if (success) {
917 coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
918 return true;
919 } else {
920 return false;
921 }
922 }
923
ParseParameters(const std::string & parameter,std::string * grpc_version,std::vector<std::string> * strip_prefixes,std::string * error)924 static bool ParseParameters(const std::string& parameter,
925 std::string* grpc_version,
926 std::vector<std::string>* strip_prefixes,
927 std::string* error) {
928 std::vector<std::string> comma_delimited_parameters;
929 grpc_python_generator::Split(parameter, ',', &comma_delimited_parameters);
930 if (comma_delimited_parameters.size() == 1 &&
931 comma_delimited_parameters[0].empty()) {
932 *grpc_version = "grpc_2_0";
933 } else if (comma_delimited_parameters.size() == 1) {
934 *grpc_version = comma_delimited_parameters[0];
935 } else if (comma_delimited_parameters.size() == 2) {
936 *grpc_version = comma_delimited_parameters[0];
937 std::copy(comma_delimited_parameters.begin() + 1,
938 comma_delimited_parameters.end(),
939 std::back_inserter(*strip_prefixes));
940 } else {
941 *error = "--grpc_python_out received too many comma-delimited parameters.";
942 return false;
943 }
944 return true;
945 }
946
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const947 bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
948 const std::string& parameter,
949 GeneratorContext* context,
950 std::string* error) const {
951 // Get output file name.
952 std::string pb2_file_name;
953 std::string pb2_grpc_file_name;
954 static const int proto_suffix_length = strlen(".proto");
955 if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
956 file->name().find_last_of(".proto") == file->name().size() - 1) {
957 std::string base =
958 file->name().substr(0, file->name().size() - proto_suffix_length);
959 std::replace(base.begin(), base.end(), '-', '_');
960 pb2_file_name = base + "_pb2.py";
961 pb2_grpc_file_name = base + "_pb2_grpc.py";
962 } else {
963 *error = "Invalid proto file name. Proto file must end with .proto";
964 return false;
965 }
966 generator_file_name = file->name();
967
968 ProtoBufFile pbfile(file);
969 std::string grpc_version;
970 GeneratorConfiguration extended_config(config_);
971 bool success = ParseParameters(parameter, &grpc_version,
972 &(extended_config.prefixes_to_filter), error);
973 PrivateGenerator generator(extended_config, &pbfile);
974 if (!success) return false;
975 if (grpc_version == "grpc_2_0") {
976 return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
977 } else if (grpc_version == "grpc_1_0") {
978 return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
979 GenerateGrpc(context, generator, pb2_file_name, false);
980 } else {
981 *error = "Invalid grpc version '" + grpc_version + "'.";
982 return false;
983 }
984 }
985
986 } // namespace grpc_python_generator
987