1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/python/pyi_generator.h"
9
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "absl/container/flat_hash_set.h"
15 #include "absl/log/absl_check.h"
16 #include "absl/log/absl_log.h"
17 #include "absl/strings/ascii.h"
18 #include "absl/strings/match.h"
19 #include "absl/strings/str_split.h"
20 #include "absl/strings/string_view.h"
21 #include "google/protobuf/compiler/code_generator.h"
22 #include "google/protobuf/compiler/python/helpers.h"
23 #include "google/protobuf/descriptor.h"
24 #include "google/protobuf/descriptor.pb.h"
25 #include "google/protobuf/io/printer.h"
26 #include "google/protobuf/io/zero_copy_stream.h"
27
28 namespace google {
29 namespace protobuf {
30 namespace compiler {
31 namespace python {
32
PyiGenerator()33 PyiGenerator::PyiGenerator() : file_(nullptr) {}
34
~PyiGenerator()35 PyiGenerator::~PyiGenerator() {}
36
37 template <typename DescriptorT>
ModuleLevelName(const DescriptorT & descriptor) const38 std::string PyiGenerator::ModuleLevelName(const DescriptorT& descriptor) const {
39 std::string name = NamePrefixedWithNestedTypes(descriptor, ".");
40 if (descriptor.file() != file_) {
41 std::string module_alias;
42 const absl::string_view filename = descriptor.file()->name();
43 if (import_map_.find(filename) == import_map_.end()) {
44 std::string module_name = ModuleName(descriptor.file()->name());
45 std::vector<absl::string_view> tokens = absl::StrSplit(module_name, '.');
46 module_alias = absl::StrCat("_", tokens.back());
47 } else {
48 module_alias = import_map_.at(filename);
49 }
50 name = absl::StrCat(module_alias, ".", name);
51 }
52 return name;
53 }
54
PublicPackage() const55 std::string PyiGenerator::PublicPackage() const { return "google.protobuf"; }
56
InternalPackage() const57 std::string PyiGenerator::InternalPackage() const {
58 return "google.protobuf.internal";
59 }
60
61 struct ImportModules {
62 bool has_repeated = false; // _containers
63 bool has_iterable = false; // typing.Iterable
64 bool has_messages = false; // _message
65 bool has_enums = false; // _enum_type_wrapper
66 bool has_extendable = false; // _python_message
67 bool has_mapping = false; // typing.Mapping
68 bool has_optional = false; // typing.Optional
69 bool has_union = false; // typing.Union
70 bool has_well_known_type = false;
71 };
72
73 // Checks whether a descriptor name matches a well-known type.
IsWellKnownType(const absl::string_view name)74 bool IsWellKnownType(const absl::string_view name) {
75 // LINT.IfChange(wktbases)
76 return (name == "google.protobuf.Any" ||
77 name == "google.protobuf.Duration" ||
78 name == "google.protobuf.FieldMask" ||
79 name == "google.protobuf.ListValue" ||
80 name == "google.protobuf.Struct" ||
81 name == "google.protobuf.Timestamp");
82 // LINT.ThenChange(//depot/google3/net/proto2/python/internal/well_known_types.py:wktbases)
83 }
84
85 // Checks what modules should be imported for this message
86 // descriptor.
CheckImportModules(const Descriptor * descriptor,ImportModules * import_modules)87 void CheckImportModules(const Descriptor* descriptor,
88 ImportModules* import_modules) {
89 if (descriptor->extension_range_count() > 0) {
90 import_modules->has_extendable = true;
91 }
92 if (descriptor->enum_type_count() > 0) {
93 import_modules->has_enums = true;
94 }
95 if (IsWellKnownType(descriptor->full_name())) {
96 import_modules->has_well_known_type = true;
97 }
98 for (int i = 0; i < descriptor->field_count(); ++i) {
99 const FieldDescriptor* field = descriptor->field(i);
100 if (IsPythonKeyword(field->name())) {
101 continue;
102 }
103 import_modules->has_optional = true;
104 if (field->is_repeated()) {
105 import_modules->has_repeated = true;
106 }
107 if (field->is_map()) {
108 import_modules->has_mapping = true;
109 const FieldDescriptor* value_des = field->message_type()->field(1);
110 if (value_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
111 value_des->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
112 import_modules->has_union = true;
113 }
114 } else {
115 if (field->is_repeated()) {
116 import_modules->has_iterable = true;
117 }
118 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
119 import_modules->has_union = true;
120 import_modules->has_mapping = true;
121 }
122 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
123 import_modules->has_union = true;
124 }
125 }
126 }
127 for (int i = 0; i < descriptor->nested_type_count(); ++i) {
128 CheckImportModules(descriptor->nested_type(i), import_modules);
129 }
130 }
131
PrintImportForDescriptor(const FileDescriptor & desc,absl::flat_hash_set<std::string> * seen_aliases,bool * has_importlib) const132 void PyiGenerator::PrintImportForDescriptor(
133 const FileDescriptor& desc, absl::flat_hash_set<std::string>* seen_aliases,
134 bool* has_importlib) const {
135 const absl::string_view filename = desc.name();
136 std::string module_name_owned = StrippedModuleName(filename);
137 absl::string_view module_name(module_name_owned);
138 size_t last_dot_pos = module_name.rfind('.');
139 std::string alias = absl::StrCat("_", module_name.substr(last_dot_pos + 1));
140 // Generate a unique alias by adding _1 suffixes until we get an unused alias.
141 while (seen_aliases->find(alias) != seen_aliases->end()) {
142 absl::StrAppend(&alias, "_1");
143 }
144 if (ContainsPythonKeyword(module_name)) {
145 if (*has_importlib == false) {
146 printer_->Print("import importlib\n");
147 *has_importlib = true;
148 }
149 printer_->Print("$alias$ = importlib.import_module('$name$')\n", "alias",
150 alias, "name", module_name);
151 } else {
152 std::string import_statement;
153 if (last_dot_pos == std::string::npos) {
154 import_statement = absl::StrCat("import ", module_name);
155 } else {
156 import_statement =
157 absl::StrCat("from ", module_name.substr(0, last_dot_pos), " import ",
158 module_name.substr(last_dot_pos + 1));
159 }
160 printer_->Print("$statement$ as $alias$\n", "statement", import_statement,
161 "alias", alias);
162 import_map_[filename] = alias;
163 seen_aliases->insert(alias);
164 }
165 }
166
PrintImports() const167 void PyiGenerator::PrintImports() const {
168 // Prints imported dependent _pb2 files.
169 absl::flat_hash_set<std::string> seen_aliases;
170 bool has_importlib = false;
171 for (int i = 0; i < file_->dependency_count(); ++i) {
172 const FileDescriptor* dep = file_->dependency(i);
173 if (strip_nonfunctional_codegen_ && IsKnownFeatureProto(dep->name())) {
174 continue;
175 }
176 PrintImportForDescriptor(*dep, &seen_aliases, &has_importlib);
177 for (int j = 0; j < dep->public_dependency_count(); ++j) {
178 PrintImportForDescriptor(*dep->public_dependency(j), &seen_aliases,
179 &has_importlib);
180 }
181 }
182
183 // Checks what modules should be imported.
184 ImportModules import_modules;
185 if (file_->message_type_count() > 0) {
186 import_modules.has_messages = true;
187 }
188 if (file_->enum_type_count() > 0) {
189 import_modules.has_enums = true;
190 }
191 if (!opensource_runtime_ && file_->service_count() > 0) {
192 import_modules.has_optional = true;
193 import_modules.has_union = true;
194 }
195 for (int i = 0; i < file_->message_type_count(); i++) {
196 CheckImportModules(file_->message_type(i), &import_modules);
197 }
198
199 // Prints modules (e.g. _containers, _messages, typing) that are
200 // required in the proto file.
201 if (import_modules.has_repeated) {
202 printer_->Print(
203 "from $internal_package$ import containers as _containers\n",
204 "internal_package", InternalPackage());
205 }
206 if (import_modules.has_enums) {
207 printer_->Print(
208 "from $internal_package$ import enum_type_wrapper as "
209 "_enum_type_wrapper\n",
210 "internal_package", InternalPackage());
211 }
212 if (import_modules.has_extendable) {
213 printer_->Print(
214 "from $internal_package$ import python_message as _python_message\n",
215 "internal_package", InternalPackage());
216 }
217 if (import_modules.has_well_known_type) {
218 printer_->Print(
219 "from $internal_package$ import well_known_types as "
220 "_well_known_types\n",
221 "internal_package", InternalPackage());
222 }
223 printer_->Print("from $public_package$ import descriptor as _descriptor\n",
224 "public_package", PublicPackage());
225 if (import_modules.has_messages) {
226 printer_->Print("from $public_package$ import message as _message\n",
227 "public_package", PublicPackage());
228 }
229 if (opensource_runtime_) {
230 if (HasGenericServices(file_)) {
231 printer_->Print("from $public_package$ import service as _service\n",
232 "public_package", PublicPackage());
233 }
234 } else {
235 if (file_->service_count() > 0) {
236 printer_->Print(
237 "from google3.net.rpc.python import proto_python_api_2_stub as "
238 "_proto_python_api_2_stub\n"
239 "from google3.net.rpc.python import pywraprpc as _pywraprpc\n"
240 "from google3.net.rpc.python import rpcserver as _rpcserver\n");
241 }
242 }
243 printer_->Print("from typing import ");
244 if (!opensource_runtime_ && file_->service_count() > 0) {
245 printer_->Print("Any as _Any, ");
246 }
247 printer_->Print("ClassVar as _ClassVar");
248 if (import_modules.has_iterable) {
249 printer_->Print(", Iterable as _Iterable");
250 }
251 if (import_modules.has_mapping) {
252 printer_->Print(", Mapping as _Mapping");
253 }
254 if (import_modules.has_optional) {
255 printer_->Print(", Optional as _Optional");
256 }
257 if (import_modules.has_union) {
258 printer_->Print(", Union as _Union");
259 }
260 printer_->Print("\n");
261
262 // Public imports
263 for (int i = 0; i < file_->public_dependency_count(); ++i) {
264 const FileDescriptor* public_dep = file_->public_dependency(i);
265 std::string module_name = StrippedModuleName(public_dep->name());
266 // Top level messages in public imports
267 for (int i = 0; i < public_dep->message_type_count(); ++i) {
268 printer_->Print(
269 "from $module$ import $message_class$ as $message_class$\n", "module",
270 module_name, "message_class", public_dep->message_type(i)->name());
271 }
272 // Top level enums for public imports
273 for (int i = 0; i < public_dep->enum_type_count(); ++i) {
274 printer_->Print("from $module$ import $enum_class$ as $enum_class$\n",
275 "module", module_name, "enum_class",
276 public_dep->enum_type(i)->name());
277 }
278 }
279 printer_->Print("\n");
280 }
281
282 // Annotate wrapper for debugging purposes
283 // Print a message after Annotate to see what is annotated.
284 template <typename DescriptorT>
Annotate(const std::string & label,const DescriptorT * descriptor) const285 void PyiGenerator::Annotate(const std::string& label,
286 const DescriptorT* descriptor) const {
287 printer_->Annotate(label.c_str(), descriptor);
288 }
289
PrintEnum(const EnumDescriptor & enum_descriptor) const290 void PyiGenerator::PrintEnum(const EnumDescriptor& enum_descriptor) const {
291 const absl::string_view enum_name = enum_descriptor.name();
292 printer_->Print(
293 "class $enum_name$(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):\n"
294 " __slots__ = ()\n",
295 "enum_name", enum_name);
296 Annotate("enum_name", &enum_descriptor);
297 printer_->Indent();
298 PrintEnumValues(enum_descriptor, /* is_classvar = */ true);
299 printer_->Outdent();
300 }
301
PrintEnumValues(const EnumDescriptor & enum_descriptor,bool is_classvar) const302 void PyiGenerator::PrintEnumValues(const EnumDescriptor& enum_descriptor,
303 bool is_classvar) const {
304 // enum values
305 std::string module_enum_name = ModuleLevelName(enum_descriptor);
306 for (int j = 0; j < enum_descriptor.value_count(); ++j) {
307 const EnumValueDescriptor* value_descriptor = enum_descriptor.value(j);
308 if (is_classvar) {
309 printer_->Print("$name$: _ClassVar[$module_enum_name$]\n", "name",
310 value_descriptor->name(), "module_enum_name",
311 module_enum_name);
312 } else {
313 printer_->Print("$name$: $module_enum_name$\n", "name",
314 value_descriptor->name(), "module_enum_name",
315 module_enum_name);
316 }
317 Annotate("name", value_descriptor);
318 }
319 }
320
PrintTopLevelEnums() const321 void PyiGenerator::PrintTopLevelEnums() const {
322 for (int i = 0; i < file_->enum_type_count(); ++i) {
323 printer_->Print("\n");
324 PrintEnum(*file_->enum_type(i));
325 }
326 }
327
328 template <typename DescriptorT>
PrintExtensions(const DescriptorT & descriptor) const329 void PyiGenerator::PrintExtensions(const DescriptorT& descriptor) const {
330 for (int i = 0; i < descriptor.extension_count(); ++i) {
331 const FieldDescriptor* extension_field = descriptor.extension(i);
332 std::string constant_name =
333 absl::StrCat(extension_field->name(), "_FIELD_NUMBER");
334 absl::AsciiStrToUpper(&constant_name);
335 printer_->Print("$constant_name$: _ClassVar[int]\n",
336 "constant_name", constant_name);
337 printer_->Print("$name$: _descriptor.FieldDescriptor\n",
338 "name", extension_field->name());
339 Annotate("name", extension_field);
340 }
341 }
342
343 // Returns the string format of a field's cpp_type
GetFieldType(const FieldDescriptor & field_des,const Descriptor & containing_des) const344 std::string PyiGenerator::GetFieldType(
345 const FieldDescriptor& field_des, const Descriptor& containing_des) const {
346 switch (field_des.cpp_type()) {
347 case FieldDescriptor::CPPTYPE_INT32:
348 case FieldDescriptor::CPPTYPE_UINT32:
349 case FieldDescriptor::CPPTYPE_INT64:
350 case FieldDescriptor::CPPTYPE_UINT64:
351 return "int";
352 case FieldDescriptor::CPPTYPE_DOUBLE:
353 case FieldDescriptor::CPPTYPE_FLOAT:
354 return "float";
355 case FieldDescriptor::CPPTYPE_BOOL:
356 return "bool";
357 case FieldDescriptor::CPPTYPE_ENUM:
358 return ModuleLevelName(*field_des.enum_type());
359 case FieldDescriptor::CPPTYPE_STRING:
360 if (field_des.type() == FieldDescriptor::TYPE_STRING) {
361 return "str";
362 } else {
363 return "bytes";
364 }
365 case FieldDescriptor::CPPTYPE_MESSAGE: {
366 // If the field is inside a nested message and the nested message has the
367 // same name as a top-level message, then we need to prefix the field type
368 // with the module name for disambiguation.
369 std::string name = ModuleLevelName(*field_des.message_type());
370 if ((containing_des.containing_type() != nullptr &&
371 name == containing_des.name())) {
372 std::string module = ModuleName(field_des.file()->name());
373 name = absl::StrCat(module, ".", name);
374 }
375 return name;
376 }
377 default:
378 ABSL_LOG(FATAL) << "Unsupported field type.";
379 }
380 return "";
381 }
382
PrintMessage(const Descriptor & message_descriptor,bool is_nested) const383 void PyiGenerator::PrintMessage(
384 const Descriptor& message_descriptor, bool is_nested) const {
385 if (!is_nested) {
386 printer_->Print("\n");
387 }
388 const absl::string_view class_name = message_descriptor.name();
389 std::string extra_base;
390 // A well-known type needs to inherit from its corresponding base class in
391 // net/proto2/python/internal/well_known_types.
392 if (IsWellKnownType(message_descriptor.full_name())) {
393 extra_base =
394 absl::StrCat(", _well_known_types.", message_descriptor.name());
395 } else {
396 extra_base = "";
397 }
398 printer_->Print("class $class_name$(_message.Message$extra_base$):\n",
399 "class_name", class_name, "extra_base", extra_base);
400 Annotate("class_name", &message_descriptor);
401 printer_->Indent();
402
403 // Prints slots
404 printer_->Print("__slots__ = (");
405 int items_printed = 0;
406 for (int i = 0; i < message_descriptor.field_count(); ++i) {
407 const FieldDescriptor* field_des = message_descriptor.field(i);
408 if (IsPythonKeyword(field_des->name())) {
409 continue;
410 }
411 if (items_printed > 0) {
412 printer_->Print(", ");
413 }
414 ++items_printed;
415 printer_->Print("\"$field_name$\"", "field_name", field_des->name());
416 }
417 printer_->Print(items_printed == 1 ? ",)\n" : ")\n");
418
419 // Prints Extensions for extendable messages
420 if (message_descriptor.extension_range_count() > 0) {
421 printer_->Print("Extensions: _python_message._ExtensionDict\n");
422 }
423
424 // Prints nested enums
425 for (int i = 0; i < message_descriptor.enum_type_count(); ++i) {
426 PrintEnum(*message_descriptor.enum_type(i));
427 PrintEnumValues(*message_descriptor.enum_type(i));
428 }
429
430 // Prints nested messages
431 for (int i = 0; i < message_descriptor.nested_type_count(); ++i) {
432 PrintMessage(*message_descriptor.nested_type(i), true);
433 }
434
435 PrintExtensions(message_descriptor);
436
437 // Prints field number
438 for (int i = 0; i < message_descriptor.field_count(); ++i) {
439 const FieldDescriptor& field_des = *message_descriptor.field(i);
440 printer_->Print(
441 "$field_number_name$: _ClassVar[int]\n", "field_number_name",
442 absl::StrCat(absl::AsciiStrToUpper(field_des.name()), "_FIELD_NUMBER"));
443 }
444 // Prints field name and type
445 for (int i = 0; i < message_descriptor.field_count(); ++i) {
446 const FieldDescriptor& field_des = *message_descriptor.field(i);
447 if (IsPythonKeyword(field_des.name())) {
448 continue;
449 }
450 std::string field_type = "";
451 if (field_des.is_map()) {
452 const FieldDescriptor* key_des = field_des.message_type()->field(0);
453 const FieldDescriptor* value_des = field_des.message_type()->field(1);
454 field_type =
455 absl::StrCat(value_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
456 ? "_containers.MessageMap["
457 : "_containers.ScalarMap[",
458 GetFieldType(*key_des, message_descriptor), ", ",
459 GetFieldType(*value_des, message_descriptor));
460 } else {
461 if (field_des.is_repeated()) {
462 field_type = (field_des.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
463 ? "_containers.RepeatedCompositeFieldContainer["
464 : "_containers.RepeatedScalarFieldContainer[");
465 }
466 field_type += GetFieldType(field_des, message_descriptor);
467 }
468
469 if (field_des.is_repeated()) {
470 absl::StrAppend(&field_type, "]");
471 }
472 printer_->Print("$name$: $type$\n",
473 "name", field_des.name(), "type", field_type);
474 Annotate("name", &field_des);
475 }
476
477 // Prints __init__
478 printer_->Print("def __init__(self");
479 bool has_key_words = false;
480 bool is_first = true;
481 for (int i = 0; i < message_descriptor.field_count(); ++i) {
482 const FieldDescriptor* field_des = message_descriptor.field(i);
483 if (IsPythonKeyword(field_des->name())) {
484 has_key_words = true;
485 continue;
486 }
487 std::string field_name = std::string(field_des->name());
488 if (is_first && field_name == "self") {
489 // See b/144146793 for an example of real code that generates a (self,
490 // self) method signature. Since repeating a parameter name is illegal in
491 // Python, we rename the duplicate self.
492 field_name = "self_";
493 }
494 is_first = false;
495 printer_->Print(", $field_name$: ", "field_name", field_name);
496 Annotate("field_name", field_des);
497 if (field_des->is_repeated() ||
498 field_des->cpp_type() != FieldDescriptor::CPPTYPE_BOOL) {
499 printer_->Print("_Optional[");
500 }
501 if (field_des->is_map()) {
502 const Descriptor* map_entry = field_des->message_type();
503 printer_->Print(
504 "_Mapping[$key_type$, $value_type$]", "key_type",
505 GetFieldType(*map_entry->field(0), message_descriptor),
506 "value_type",
507 GetFieldType(*map_entry->field(1), message_descriptor));
508 } else {
509 if (field_des->is_repeated()) {
510 printer_->Print("_Iterable[");
511 }
512 if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
513 printer_->Print(
514 "_Union[$type_name$, _Mapping]", "type_name",
515 GetFieldType(*field_des, message_descriptor));
516 } else {
517 if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
518 printer_->Print("_Union[$type_name$, str]", "type_name",
519 ModuleLevelName(*field_des->enum_type()));
520 } else {
521 printer_->Print(
522 "$type_name$", "type_name",
523 GetFieldType(*field_des, message_descriptor));
524 }
525 }
526 if (field_des->is_repeated()) {
527 printer_->Print("]");
528 }
529 }
530 if (field_des->is_repeated() ||
531 field_des->cpp_type() != FieldDescriptor::CPPTYPE_BOOL) {
532 printer_->Print("]");
533 }
534 printer_->Print(" = ...");
535 }
536 if (has_key_words) {
537 printer_->Print(", **kwargs");
538 }
539 printer_->Print(") -> None: ...\n");
540 printer_->Outdent();
541 }
542
PrintMessages() const543 void PyiGenerator::PrintMessages() const {
544 // Deterministically order the descriptors.
545 for (int i = 0; i < file_->message_type_count(); ++i) {
546 PrintMessage(*file_->message_type(i), false);
547 }
548 }
549
PrintServices() const550 void PyiGenerator::PrintServices() const {
551 // Prints $Service$ and $Service$_Stub classes
552 for (int i = 0; i < file_->service_count(); ++i) {
553 printer_->Print("\n");
554 printer_->Print(
555 "class $service_name$(_service.service): ...\n\n"
556 "class $service_name$_Stub($service_name$): ...\n",
557 "service_name", file_->service(i)->name());
558 }
559 }
560
561
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const562 bool PyiGenerator::Generate(const FileDescriptor* file,
563 const std::string& parameter,
564 GeneratorContext* context,
565 std::string* error) const {
566 absl::MutexLock lock(&mutex_);
567 import_map_.clear();
568 // Calculate file name.
569 file_ = file;
570 // In google3, devtools/python/bazel/pytype/pytype_impl.bzl uses --pyi_out to
571 // directly set the output file name.
572 std::vector<std::pair<std::string, std::string> > options;
573 ParseGeneratorParameter(parameter, &options);
574
575 std::string filename;
576 bool annotate_code = false;
577 strip_nonfunctional_codegen_ = false;
578 for (const std::pair<std::string, std::string>& option : options) {
579 if (option.first == "annotate_code") {
580 annotate_code = true;
581 } else if (absl::EndsWith(option.first, ".pyi")) {
582 filename = option.first;
583 } else if (option.first == "experimental_strip_nonfunctional_codegen") {
584 strip_nonfunctional_codegen_ = true;
585 } else {
586 *error = absl::StrCat("Unknown generator option: ", option.first);
587 return false;
588 }
589 }
590
591 if (filename.empty()) {
592 filename = GetFileName(file, ".pyi");
593 }
594
595 std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
596 ABSL_CHECK(output.get());
597 GeneratedCodeInfo annotations;
598 io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
599 &annotations);
600 io::Printer::Options printer_opt(
601 '$', annotate_code ? &annotation_collector : nullptr);
602 printer_opt.spaces_per_indent = 4;
603 io::Printer printer(output.get(), printer_opt);
604 printer_ = &printer;
605
606 PrintImports();
607 printer_->Print("DESCRIPTOR: _descriptor.FileDescriptor\n");
608
609 // Prints extensions and enums from imports.
610 for (int i = 0; i < file_->public_dependency_count(); ++i) {
611 const FileDescriptor* public_dep = file_->public_dependency(i);
612 PrintExtensions(*public_dep);
613 for (int i = 0; i < public_dep->enum_type_count(); ++i) {
614 const EnumDescriptor* enum_descriptor = public_dep->enum_type(i);
615 PrintEnumValues(*enum_descriptor);
616 }
617 }
618
619 PrintTopLevelEnums();
620 // Prints top level enum values
621 for (int i = 0; i < file_->enum_type_count(); ++i) {
622 PrintEnumValues(*file_->enum_type(i));
623 }
624 // Prints top level Extensions
625 PrintExtensions(*file_);
626 PrintMessages();
627
628 if (opensource_runtime_ && HasGenericServices(file)) {
629 PrintServices();
630 }
631 return true;
632 }
633
634 } // namespace python
635 } // namespace compiler
636 } // namespace protobuf
637 } // namespace google
638