1 | /* |
2 | * |
3 | * Copyright 2015 gRPC authors. |
4 | * |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | * you may not use this file except in compliance with the License. |
7 | * You may obtain a copy of the License at |
8 | * |
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | * |
11 | * Unless required by applicable law or agreed to in writing, software |
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | * See the License for the specific language governing permissions and |
15 | * limitations under the License. |
16 | * |
17 | */ |
18 | |
19 | #include <algorithm> |
20 | #include <cassert> |
21 | #include <cctype> |
22 | #include <cstring> |
23 | #include <fstream> |
24 | #include <iostream> |
25 | #include <map> |
26 | #include <memory> |
27 | #include <ostream> |
28 | #include <set> |
29 | #include <sstream> |
30 | #include <tuple> |
31 | #include <vector> |
32 | |
33 | #include "src/compiler/config.h" |
34 | #include "src/compiler/generator_helpers.h" |
35 | #include "src/compiler/protobuf_plugin.h" |
36 | #include "src/compiler/python_generator.h" |
37 | #include "src/compiler/python_generator_helpers.h" |
38 | #include "src/compiler/python_private_generator.h" |
39 | |
40 | using grpc::protobuf::FileDescriptor; |
41 | using grpc::protobuf::compiler::GeneratorContext; |
42 | using grpc::protobuf::io::CodedOutputStream; |
43 | using grpc::protobuf::io::ZeroCopyOutputStream; |
44 | using std::make_pair; |
45 | using std::map; |
46 | using std::pair; |
47 | using std::replace; |
48 | using std::set; |
49 | using std::tuple; |
50 | using std::vector; |
51 | |
52 | namespace grpc_python_generator { |
53 | |
54 | grpc::string generator_file_name; |
55 | |
56 | namespace { |
57 | |
58 | typedef map<grpc::string, grpc::string> StringMap; |
59 | typedef vector<grpc::string> StringVector; |
60 | typedef tuple<grpc::string, grpc::string> StringPair; |
61 | typedef set<StringPair> StringPairSet; |
62 | |
63 | // Provides RAII indentation handling. Use as: |
64 | // { |
65 | // IndentScope raii_my_indent_var_name_here(my_py_printer); |
66 | // // constructor indented my_py_printer |
67 | // ... |
68 | // // destructor called at end of scope, un-indenting my_py_printer |
69 | // } |
70 | class IndentScope { |
71 | public: |
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 | |
82 | PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config, |
83 | const grpc_generator::File* file) |
84 | : config(config), file(file) {} |
85 | |
86 | void PrivateGenerator::(StringVector , |
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 | |
111 | bool PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service, |
112 | grpc_generator::Printer* out) { |
113 | StringMap service_dict; |
114 | service_dict["Service" ] = service->name(); |
115 | out->Print("\n\n" ); |
116 | out->Print(service_dict, "class Beta$Service$Servicer(object):\n" ); |
117 | { |
118 | IndentScope raii_class_indent(out); |
119 | out->Print( |
120 | "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" |
121 | "\nIt is recommended to use the GA API (classes and functions in this\n" |
122 | "file not marked beta) for all further purposes. This class was " |
123 | "generated\n" |
124 | "only to ease transition from grpcio<0.15.0 to " |
125 | "grpcio>=0.15.0.\"\"\"\n" ); |
126 | StringVector = service->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->GetAllComments(); |
139 | PrintAllComments(method_comments, out); |
140 | out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n" ); |
141 | } |
142 | } |
143 | } |
144 | return true; |
145 | } |
146 | |
147 | bool PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service, |
148 | grpc_generator::Printer* out) { |
149 | StringMap service_dict; |
150 | service_dict["Service" ] = service->name(); |
151 | out->Print("\n\n" ); |
152 | out->Print(service_dict, "class Beta$Service$Stub(object):\n" ); |
153 | { |
154 | IndentScope raii_class_indent(out); |
155 | out->Print( |
156 | "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" |
157 | "\nIt is recommended to use the GA API (classes and functions in this\n" |
158 | "file not marked beta) for all further purposes. This class was " |
159 | "generated\n" |
160 | "only to ease transition from grpcio<0.15.0 to " |
161 | "grpcio>=0.15.0.\"\"\"\n" ); |
162 | StringVector = service->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->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 | |
188 | bool PrivateGenerator::PrintBetaServerFactory( |
189 | const grpc::string& package_qualified_service_name, |
190 | const grpc_generator::Service* service, grpc_generator::Printer* out) { |
191 | StringMap service_dict; |
192 | service_dict["Service" ] = service->name(); |
193 | out->Print("\n\n" ); |
194 | out->Print(service_dict, |
195 | "def beta_create_$Service$_server(servicer, pool=None, " |
196 | "pool_size=None, default_timeout=None, maximum_timeout=None):\n" ); |
197 | { |
198 | IndentScope raii_create_server_indent(out); |
199 | out->Print( |
200 | "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" |
201 | "\nIt is recommended to use the GA API (classes and functions in this\n" |
202 | "file not marked beta) for all further purposes. This function was\n" |
203 | "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" |
204 | "\"\"\"\n" ); |
205 | StringMap method_implementation_constructors; |
206 | StringMap input_message_modules_and_classes; |
207 | StringMap output_message_modules_and_classes; |
208 | for (int i = 0; i < service->method_count(); ++i) { |
209 | auto method = service->method(i); |
210 | const grpc::string method_implementation_constructor = |
211 | grpc::string(method->ClientStreaming() ? "stream_" : "unary_" ) + |
212 | grpc::string(method->ServerStreaming() ? "stream_" : "unary_" ) + |
213 | "inline" ; |
214 | grpc::string input_message_module_and_class; |
215 | if (!method->get_module_and_message_path_input( |
216 | &input_message_module_and_class, generator_file_name, |
217 | generate_in_pb2_grpc, config.import_prefix, |
218 | config.prefixes_to_filter)) { |
219 | return false; |
220 | } |
221 | grpc::string output_message_module_and_class; |
222 | if (!method->get_module_and_message_path_output( |
223 | &output_message_module_and_class, generator_file_name, |
224 | generate_in_pb2_grpc, config.import_prefix, |
225 | config.prefixes_to_filter)) { |
226 | return false; |
227 | } |
228 | method_implementation_constructors.insert( |
229 | make_pair(method->name(), method_implementation_constructor)); |
230 | input_message_modules_and_classes.insert( |
231 | make_pair(method->name(), input_message_module_and_class)); |
232 | output_message_modules_and_classes.insert( |
233 | make_pair(method->name(), output_message_module_and_class)); |
234 | } |
235 | StringMap method_dict; |
236 | method_dict["PackageQualifiedServiceName" ] = package_qualified_service_name; |
237 | out->Print("request_deserializers = {\n" ); |
238 | for (StringMap::iterator name_and_input_module_class_pair = |
239 | input_message_modules_and_classes.begin(); |
240 | name_and_input_module_class_pair != |
241 | input_message_modules_and_classes.end(); |
242 | name_and_input_module_class_pair++) { |
243 | method_dict["MethodName" ] = name_and_input_module_class_pair->first; |
244 | method_dict["InputTypeModuleAndClass" ] = |
245 | name_and_input_module_class_pair->second; |
246 | IndentScope raii_indent(out); |
247 | out->Print(method_dict, |
248 | "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " |
249 | "$InputTypeModuleAndClass$.FromString,\n" ); |
250 | } |
251 | out->Print("}\n" ); |
252 | out->Print("response_serializers = {\n" ); |
253 | for (StringMap::iterator name_and_output_module_class_pair = |
254 | output_message_modules_and_classes.begin(); |
255 | name_and_output_module_class_pair != |
256 | output_message_modules_and_classes.end(); |
257 | name_and_output_module_class_pair++) { |
258 | method_dict["MethodName" ] = name_and_output_module_class_pair->first; |
259 | method_dict["OutputTypeModuleAndClass" ] = |
260 | name_and_output_module_class_pair->second; |
261 | IndentScope raii_indent(out); |
262 | out->Print(method_dict, |
263 | "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " |
264 | "$OutputTypeModuleAndClass$.SerializeToString,\n" ); |
265 | } |
266 | out->Print("}\n" ); |
267 | out->Print("method_implementations = {\n" ); |
268 | for (StringMap::iterator name_and_implementation_constructor = |
269 | method_implementation_constructors.begin(); |
270 | name_and_implementation_constructor != |
271 | method_implementation_constructors.end(); |
272 | name_and_implementation_constructor++) { |
273 | method_dict["Method" ] = name_and_implementation_constructor->first; |
274 | method_dict["Constructor" ] = name_and_implementation_constructor->second; |
275 | IndentScope raii_descriptions_indent(out); |
276 | const grpc::string method_name = |
277 | name_and_implementation_constructor->first; |
278 | out->Print(method_dict, |
279 | "(\'$PackageQualifiedServiceName$\', \'$Method$\'): " |
280 | "face_utilities.$Constructor$(servicer.$Method$),\n" ); |
281 | } |
282 | out->Print("}\n" ); |
283 | out->Print( |
284 | "server_options = beta_implementations.server_options(" |
285 | "request_deserializers=request_deserializers, " |
286 | "response_serializers=response_serializers, " |
287 | "thread_pool=pool, thread_pool_size=pool_size, " |
288 | "default_timeout=default_timeout, " |
289 | "maximum_timeout=maximum_timeout)\n" ); |
290 | out->Print( |
291 | "return beta_implementations.server(method_implementations, " |
292 | "options=server_options)\n" ); |
293 | } |
294 | return true; |
295 | } |
296 | |
297 | bool PrivateGenerator::PrintBetaStubFactory( |
298 | const grpc::string& package_qualified_service_name, |
299 | const grpc_generator::Service* service, grpc_generator::Printer* out) { |
300 | StringMap dict; |
301 | dict["Service" ] = service->name(); |
302 | out->Print("\n\n" ); |
303 | out->Print(dict, |
304 | "def beta_create_$Service$_stub(channel, host=None," |
305 | " metadata_transformer=None, pool=None, pool_size=None):\n" ); |
306 | { |
307 | IndentScope raii_create_server_indent(out); |
308 | out->Print( |
309 | "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" |
310 | "\nIt is recommended to use the GA API (classes and functions in this\n" |
311 | "file not marked beta) for all further purposes. This function was\n" |
312 | "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" |
313 | "\"\"\"\n" ); |
314 | StringMap method_cardinalities; |
315 | StringMap input_message_modules_and_classes; |
316 | StringMap output_message_modules_and_classes; |
317 | for (int i = 0; i < service->method_count(); ++i) { |
318 | auto method = service->method(i); |
319 | const grpc::string method_cardinality = |
320 | grpc::string(method->ClientStreaming() ? "STREAM" : "UNARY" ) + "_" + |
321 | grpc::string(method->ServerStreaming() ? "STREAM" : "UNARY" ); |
322 | grpc::string input_message_module_and_class; |
323 | if (!method->get_module_and_message_path_input( |
324 | &input_message_module_and_class, generator_file_name, |
325 | generate_in_pb2_grpc, config.import_prefix, |
326 | config.prefixes_to_filter)) { |
327 | return false; |
328 | } |
329 | grpc::string output_message_module_and_class; |
330 | if (!method->get_module_and_message_path_output( |
331 | &output_message_module_and_class, generator_file_name, |
332 | generate_in_pb2_grpc, config.import_prefix, |
333 | config.prefixes_to_filter)) { |
334 | return false; |
335 | } |
336 | method_cardinalities.insert( |
337 | make_pair(method->name(), method_cardinality)); |
338 | input_message_modules_and_classes.insert( |
339 | make_pair(method->name(), input_message_module_and_class)); |
340 | output_message_modules_and_classes.insert( |
341 | make_pair(method->name(), output_message_module_and_class)); |
342 | } |
343 | StringMap method_dict; |
344 | method_dict["PackageQualifiedServiceName" ] = package_qualified_service_name; |
345 | out->Print("request_serializers = {\n" ); |
346 | for (StringMap::iterator name_and_input_module_class_pair = |
347 | input_message_modules_and_classes.begin(); |
348 | name_and_input_module_class_pair != |
349 | input_message_modules_and_classes.end(); |
350 | name_and_input_module_class_pair++) { |
351 | method_dict["MethodName" ] = name_and_input_module_class_pair->first; |
352 | method_dict["InputTypeModuleAndClass" ] = |
353 | name_and_input_module_class_pair->second; |
354 | IndentScope raii_indent(out); |
355 | out->Print(method_dict, |
356 | "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " |
357 | "$InputTypeModuleAndClass$.SerializeToString,\n" ); |
358 | } |
359 | out->Print("}\n" ); |
360 | out->Print("response_deserializers = {\n" ); |
361 | for (StringMap::iterator name_and_output_module_class_pair = |
362 | output_message_modules_and_classes.begin(); |
363 | name_and_output_module_class_pair != |
364 | output_message_modules_and_classes.end(); |
365 | name_and_output_module_class_pair++) { |
366 | method_dict["MethodName" ] = name_and_output_module_class_pair->first; |
367 | method_dict["OutputTypeModuleAndClass" ] = |
368 | name_and_output_module_class_pair->second; |
369 | IndentScope raii_indent(out); |
370 | out->Print(method_dict, |
371 | "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " |
372 | "$OutputTypeModuleAndClass$.FromString,\n" ); |
373 | } |
374 | out->Print("}\n" ); |
375 | out->Print("cardinalities = {\n" ); |
376 | for (StringMap::iterator name_and_cardinality = |
377 | method_cardinalities.begin(); |
378 | name_and_cardinality != method_cardinalities.end(); |
379 | name_and_cardinality++) { |
380 | method_dict["Method" ] = name_and_cardinality->first; |
381 | method_dict["Cardinality" ] = name_and_cardinality->second; |
382 | IndentScope raii_descriptions_indent(out); |
383 | out->Print(method_dict, |
384 | "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n" ); |
385 | } |
386 | out->Print("}\n" ); |
387 | out->Print( |
388 | "stub_options = beta_implementations.stub_options(" |
389 | "host=host, metadata_transformer=metadata_transformer, " |
390 | "request_serializers=request_serializers, " |
391 | "response_deserializers=response_deserializers, " |
392 | "thread_pool=pool, thread_pool_size=pool_size)\n" ); |
393 | out->Print(method_dict, |
394 | "return beta_implementations.dynamic_stub(channel, " |
395 | "\'$PackageQualifiedServiceName$\', " |
396 | "cardinalities, options=stub_options)\n" ); |
397 | } |
398 | return true; |
399 | } |
400 | |
401 | bool PrivateGenerator::PrintStub( |
402 | const grpc::string& package_qualified_service_name, |
403 | const grpc_generator::Service* service, grpc_generator::Printer* out) { |
404 | StringMap dict; |
405 | dict["Service" ] = service->name(); |
406 | out->Print("\n\n" ); |
407 | out->Print(dict, "class $Service$Stub(object):\n" ); |
408 | { |
409 | IndentScope raii_class_indent(out); |
410 | StringVector = service->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 | |
470 | bool PrivateGenerator::PrintServicer(const grpc_generator::Service* service, |
471 | grpc_generator::Printer* out) { |
472 | StringMap service_dict; |
473 | service_dict["Service" ] = service->name(); |
474 | out->Print("\n\n" ); |
475 | out->Print(service_dict, "class $Service$Servicer(object):\n" ); |
476 | { |
477 | IndentScope raii_class_indent(out); |
478 | StringVector = service->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->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 | |
502 | bool PrivateGenerator::PrintAddServicerToServer( |
503 | const grpc::string& package_qualified_service_name, |
504 | const grpc_generator::Service* service, grpc_generator::Printer* out) { |
505 | StringMap service_dict; |
506 | service_dict["Service" ] = service->name(); |
507 | out->Print("\n\n" ); |
508 | out->Print(service_dict, |
509 | "def add_$Service$Servicer_to_server(servicer, server):\n" ); |
510 | { |
511 | IndentScope raii_class_indent(out); |
512 | out->Print("rpc_method_handlers = {\n" ); |
513 | { |
514 | IndentScope raii_dict_first_indent(out); |
515 | IndentScope raii_dict_second_indent(out); |
516 | for (int i = 0; i < service->method_count(); ++i) { |
517 | auto method = service->method(i); |
518 | grpc::string method_handler_constructor = |
519 | grpc::string(method->ClientStreaming() ? "stream" : "unary" ) + "_" + |
520 | grpc::string(method->ServerStreaming() ? "stream" : "unary" ) + |
521 | "_rpc_method_handler" ; |
522 | grpc::string request_module_and_class; |
523 | if (!method->get_module_and_message_path_input( |
524 | &request_module_and_class, generator_file_name, |
525 | generate_in_pb2_grpc, config.import_prefix, |
526 | config.prefixes_to_filter)) { |
527 | return false; |
528 | } |
529 | grpc::string response_module_and_class; |
530 | if (!method->get_module_and_message_path_output( |
531 | &response_module_and_class, generator_file_name, |
532 | generate_in_pb2_grpc, config.import_prefix, |
533 | config.prefixes_to_filter)) { |
534 | return false; |
535 | } |
536 | StringMap method_dict; |
537 | method_dict["Method" ] = method->name(); |
538 | method_dict["MethodHandlerConstructor" ] = method_handler_constructor; |
539 | method_dict["RequestModuleAndClass" ] = request_module_and_class; |
540 | method_dict["ResponseModuleAndClass" ] = response_module_and_class; |
541 | out->Print(method_dict, |
542 | "'$Method$': grpc.$MethodHandlerConstructor$(\n" ); |
543 | { |
544 | IndentScope raii_call_first_indent(out); |
545 | IndentScope raii_call_second_indent(out); |
546 | out->Print(method_dict, "servicer.$Method$,\n" ); |
547 | out->Print( |
548 | method_dict, |
549 | "request_deserializer=$RequestModuleAndClass$.FromString,\n" ); |
550 | out->Print( |
551 | method_dict, |
552 | "response_serializer=$ResponseModuleAndClass$.SerializeToString," |
553 | "\n" ); |
554 | } |
555 | out->Print("),\n" ); |
556 | } |
557 | } |
558 | StringMap method_dict; |
559 | method_dict["PackageQualifiedServiceName" ] = package_qualified_service_name; |
560 | out->Print("}\n" ); |
561 | out->Print("generic_handler = grpc.method_handlers_generic_handler(\n" ); |
562 | { |
563 | IndentScope raii_call_first_indent(out); |
564 | IndentScope raii_call_second_indent(out); |
565 | out->Print(method_dict, |
566 | "'$PackageQualifiedServiceName$', rpc_method_handlers)\n" ); |
567 | } |
568 | out->Print("server.add_generic_rpc_handlers((generic_handler,))\n" ); |
569 | } |
570 | return true; |
571 | } |
572 | |
573 | bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) { |
574 | StringMap var; |
575 | var["Package" ] = config.beta_package_root; |
576 | out->Print(var, |
577 | "from $Package$ import implementations as beta_implementations\n" ); |
578 | out->Print(var, "from $Package$ import interfaces as beta_interfaces\n" ); |
579 | out->Print("from grpc.framework.common import cardinality\n" ); |
580 | out->Print( |
581 | "from grpc.framework.interfaces.face import utilities as " |
582 | "face_utilities\n" ); |
583 | return true; |
584 | } |
585 | |
586 | bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) { |
587 | StringMap var; |
588 | var["Package" ] = config.grpc_package_root; |
589 | out->Print(var, "import $Package$\n" ); |
590 | if (generate_in_pb2_grpc) { |
591 | out->Print("\n" ); |
592 | StringPairSet imports_set; |
593 | for (int i = 0; i < file->service_count(); ++i) { |
594 | auto service = file->service(i); |
595 | for (int j = 0; j < service->method_count(); ++j) { |
596 | auto method = service.get()->method(j); |
597 | |
598 | grpc::string input_type_file_name = method->get_input_type_name(); |
599 | grpc::string input_module_name = |
600 | ModuleName(input_type_file_name, config.import_prefix, |
601 | config.prefixes_to_filter); |
602 | grpc::string input_module_alias = |
603 | ModuleAlias(input_type_file_name, config.import_prefix, |
604 | config.prefixes_to_filter); |
605 | imports_set.insert( |
606 | std::make_tuple(input_module_name, input_module_alias)); |
607 | |
608 | grpc::string output_type_file_name = method->get_output_type_name(); |
609 | grpc::string output_module_name = |
610 | ModuleName(output_type_file_name, config.import_prefix, |
611 | config.prefixes_to_filter); |
612 | grpc::string output_module_alias = |
613 | ModuleAlias(output_type_file_name, config.import_prefix, |
614 | config.prefixes_to_filter); |
615 | imports_set.insert( |
616 | std::make_tuple(output_module_name, output_module_alias)); |
617 | } |
618 | } |
619 | |
620 | for (StringPairSet::iterator it = imports_set.begin(); |
621 | it != imports_set.end(); ++it) { |
622 | auto module_name = std::get<0>(*it); |
623 | var["ModuleAlias" ] = std::get<1>(*it); |
624 | const size_t last_dot_pos = module_name.rfind('.'); |
625 | if (last_dot_pos == grpc::string::npos) { |
626 | var["ImportStatement" ] = "import " + module_name; |
627 | } else { |
628 | var["ImportStatement" ] = "from " + module_name.substr(0, last_dot_pos) + |
629 | " import " + |
630 | module_name.substr(last_dot_pos + 1); |
631 | } |
632 | out->Print(var, "$ImportStatement$ as $ModuleAlias$\n" ); |
633 | } |
634 | } |
635 | return true; |
636 | } |
637 | |
638 | bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) { |
639 | grpc::string package = file->package(); |
640 | if (!package.empty()) { |
641 | package = package.append("." ); |
642 | } |
643 | for (int i = 0; i < file->service_count(); ++i) { |
644 | auto service = file->service(i); |
645 | grpc::string package_qualified_service_name = package + service->name(); |
646 | if (!(PrintStub(package_qualified_service_name, service.get(), out) && |
647 | PrintServicer(service.get(), out) && |
648 | PrintAddServicerToServer(package_qualified_service_name, |
649 | service.get(), out))) { |
650 | return false; |
651 | } |
652 | } |
653 | return true; |
654 | } |
655 | |
656 | bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) { |
657 | grpc::string package = file->package(); |
658 | if (!package.empty()) { |
659 | package = package.append("." ); |
660 | } |
661 | for (int i = 0; i < file->service_count(); ++i) { |
662 | auto service = file->service(i); |
663 | grpc::string package_qualified_service_name = package + service->name(); |
664 | if (!(PrintBetaServicer(service.get(), out) && |
665 | PrintBetaStub(service.get(), out) && |
666 | PrintBetaServerFactory(package_qualified_service_name, service.get(), |
667 | out) && |
668 | PrintBetaStubFactory(package_qualified_service_name, service.get(), |
669 | out))) { |
670 | return false; |
671 | } |
672 | } |
673 | return true; |
674 | } |
675 | |
676 | pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() { |
677 | grpc::string output; |
678 | { |
679 | // Scope the output stream so it closes and finalizes output to the string. |
680 | auto out = file->CreatePrinter(&output); |
681 | if (generate_in_pb2_grpc) { |
682 | out->Print( |
683 | "# Generated by the gRPC Python protocol compiler plugin. " |
684 | "DO NOT EDIT!\n" ); |
685 | if (!PrintPreamble(out.get())) { |
686 | return make_pair(false, "" ); |
687 | } |
688 | if (!PrintGAServices(out.get())) { |
689 | return make_pair(false, "" ); |
690 | } |
691 | } else { |
692 | out->Print("try:\n" ); |
693 | { |
694 | IndentScope raii_dict_try_indent(out.get()); |
695 | out->Print( |
696 | "# THESE ELEMENTS WILL BE DEPRECATED.\n" |
697 | "# Please use the generated *_pb2_grpc.py files instead.\n" ); |
698 | if (!PrintPreamble(out.get())) { |
699 | return make_pair(false, "" ); |
700 | } |
701 | if (!PrintBetaPreamble(out.get())) { |
702 | return make_pair(false, "" ); |
703 | } |
704 | if (!PrintGAServices(out.get())) { |
705 | return make_pair(false, "" ); |
706 | } |
707 | if (!PrintBetaServices(out.get())) { |
708 | return make_pair(false, "" ); |
709 | } |
710 | } |
711 | out->Print("except ImportError:\n" ); |
712 | { |
713 | IndentScope raii_dict_except_indent(out.get()); |
714 | out->Print("pass" ); |
715 | } |
716 | } |
717 | } |
718 | return make_pair(true, std::move(output)); |
719 | } |
720 | |
721 | } // namespace |
722 | |
723 | GeneratorConfiguration::GeneratorConfiguration() |
724 | : grpc_package_root("grpc" ), |
725 | beta_package_root("grpc.beta" ), |
726 | import_prefix("" ) {} |
727 | |
728 | PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config) |
729 | : config_(config) {} |
730 | |
731 | PythonGrpcGenerator::~PythonGrpcGenerator() {} |
732 | |
733 | static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator, |
734 | grpc::string file_name, bool generate_in_pb2_grpc) { |
735 | bool success; |
736 | std::unique_ptr<ZeroCopyOutputStream> output; |
737 | std::unique_ptr<CodedOutputStream> coded_output; |
738 | grpc::string grpc_code; |
739 | |
740 | if (generate_in_pb2_grpc) { |
741 | output.reset(context->Open(file_name)); |
742 | generator.generate_in_pb2_grpc = true; |
743 | } else { |
744 | output.reset(context->OpenForInsert(file_name, "module_scope" )); |
745 | generator.generate_in_pb2_grpc = false; |
746 | } |
747 | |
748 | coded_output.reset(new CodedOutputStream(output.get())); |
749 | tie(success, grpc_code) = generator.GetGrpcServices(); |
750 | |
751 | if (success) { |
752 | coded_output->WriteRaw(grpc_code.data(), grpc_code.size()); |
753 | return true; |
754 | } else { |
755 | return false; |
756 | } |
757 | } |
758 | |
759 | static 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 | |
782 | bool 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 | |