19
19
20
20
import collections
21
21
import dataclasses
22
+ import itertools
22
23
import keyword
23
24
import os
24
25
import sys
25
26
from typing import Callable , Container , Dict , FrozenSet , Mapping , Optional , Sequence , Set , Tuple
27
+ from types import MappingProxyType
26
28
27
29
from google .api_core import exceptions # type: ignore
30
+ from google .api import resource_pb2
28
31
from google .longrunning import operations_pb2 # type: ignore
29
32
from google .protobuf import descriptor_pb2
30
33
@@ -58,11 +61,14 @@ def __getattr__(self, name: str):
58
61
59
62
@classmethod
60
63
def build (
61
- cls , file_descriptor : descriptor_pb2 .FileDescriptorProto ,
62
- file_to_generate : bool , naming : api_naming .Naming ,
63
- opts : Options = Options (),
64
- prior_protos : Mapping [str , 'Proto' ] = None ,
65
- load_services : bool = True
64
+ cls ,
65
+ file_descriptor : descriptor_pb2 .FileDescriptorProto ,
66
+ file_to_generate : bool ,
67
+ naming : api_naming .Naming ,
68
+ opts : Options = Options (),
69
+ prior_protos : Mapping [str , 'Proto' ] = None ,
70
+ load_services : bool = True ,
71
+ all_resources : Optional [Mapping [str , wrappers .CommonResource ]] = None ,
66
72
) -> 'Proto' :
67
73
"""Build and return a Proto instance.
68
74
@@ -85,7 +91,8 @@ def build(
85
91
naming = naming ,
86
92
opts = opts ,
87
93
prior_protos = prior_protos or {},
88
- load_services = load_services
94
+ load_services = load_services ,
95
+ all_resources = all_resources or {},
89
96
).proto
90
97
91
98
@cached_property
@@ -104,6 +111,24 @@ def messages(self) -> Mapping[str, wrappers.MessageType]:
104
111
if not v .meta .address .parent
105
112
)
106
113
114
+ @cached_property
115
+ def resource_messages (self ) -> Mapping [str , wrappers .MessageType ]:
116
+ """Return the file level resources of the proto."""
117
+ file_resource_messages = (
118
+ (res .type , wrappers .CommonResource .build (res ).message_type )
119
+ for res in self .file_pb2 .options .Extensions [resource_pb2 .resource_definition ]
120
+ )
121
+ resource_messages = (
122
+ (msg .options .Extensions [resource_pb2 .resource ].type , msg )
123
+ for msg in self .messages .values ()
124
+ if msg .options .Extensions [resource_pb2 .resource ].type
125
+ )
126
+ return collections .OrderedDict (
127
+ itertools .chain (
128
+ file_resource_messages , resource_messages ,
129
+ )
130
+ )
131
+
107
132
@property
108
133
def module_name (self ) -> str :
109
134
"""Return the appropriate module name for this service.
@@ -264,6 +289,13 @@ def disambiguate_keyword_fname(
264
289
load_services = False ,
265
290
)
266
291
292
+ # A file descriptor's file-level resources are NOT visible to any importers.
293
+ # The only way to make referenced resources visible is to aggregate them at
294
+ # the API level and then pass that around.
295
+ all_file_resources = collections .ChainMap (
296
+ * (proto .resource_messages for proto in pre_protos .values ())
297
+ )
298
+
267
299
# Second pass uses all the messages and enums defined in the entire API.
268
300
# This allows LRO returning methods to see all the types in the API,
269
301
# bypassing the above missing import problem.
@@ -274,6 +306,7 @@ def disambiguate_keyword_fname(
274
306
naming = naming ,
275
307
opts = opts ,
276
308
prior_protos = pre_protos ,
309
+ all_resources = MappingProxyType (all_file_resources ),
277
310
)
278
311
for name , proto in pre_protos .items ()
279
312
}
@@ -390,7 +423,8 @@ def __init__(
390
423
naming : api_naming .Naming ,
391
424
opts : Options = Options (),
392
425
prior_protos : Mapping [str , Proto ] = None ,
393
- load_services : bool = True
426
+ load_services : bool = True ,
427
+ all_resources : Optional [Mapping [str , wrappers .CommonResource ]] = None ,
394
428
):
395
429
self .proto_messages : Dict [str , wrappers .MessageType ] = {}
396
430
self .proto_enums : Dict [str , wrappers .EnumType ] = {}
@@ -432,9 +466,9 @@ def __init__(
432
466
# below is because `repeated DescriptorProto message_type = 4;` in
433
467
# descriptor.proto itself).
434
468
self ._load_children (file_descriptor .enum_type , self ._load_enum ,
435
- address = self .address , path = (5 ,))
469
+ address = self .address , path = (5 ,), resources = all_resources )
436
470
self ._load_children (file_descriptor .message_type , self ._load_message ,
437
- address = self .address , path = (4 ,))
471
+ address = self .address , path = (4 ,), resources = all_resources )
438
472
439
473
# Edge case: Protocol buffers is not particularly picky about
440
474
# ordering, and it is possible that a message will have had a field
@@ -469,7 +503,7 @@ def __init__(
469
503
# same files.
470
504
if file_to_generate and load_services :
471
505
self ._load_children (file_descriptor .service , self ._load_service ,
472
- address = self .address , path = (6 ,))
506
+ address = self .address , path = (6 ,), resources = all_resources )
473
507
# TODO(lukesneeringer): oneofs are on path 7.
474
508
475
509
@property
@@ -528,7 +562,8 @@ def api_messages(self) -> Mapping[str, wrappers.MessageType]:
528
562
529
563
def _load_children (self ,
530
564
children : Sequence , loader : Callable , * ,
531
- address : metadata .Address , path : Tuple [int , ...]) -> Mapping :
565
+ address : metadata .Address , path : Tuple [int , ...],
566
+ resources : Mapping [str , wrappers .CommonResource ]) -> Mapping :
532
567
"""Return wrapped versions of arbitrary children from a Descriptor.
533
568
534
569
Args:
@@ -554,7 +589,7 @@ def _load_children(self,
554
589
# applicable loader function on each.
555
590
answer = {}
556
591
for child , i in zip (children , range (0 , sys .maxsize )):
557
- wrapped = loader (child , address = address , path = path + (i ,))
592
+ wrapped = loader (child , address = address , path = path + (i ,), resources = resources )
558
593
answer [wrapped .name ] = wrapped
559
594
return answer
560
595
@@ -794,6 +829,7 @@ def _load_message(self,
794
829
message_pb : descriptor_pb2 .DescriptorProto ,
795
830
address : metadata .Address ,
796
831
path : Tuple [int ],
832
+ resources : Mapping [str , wrappers .CommonResource ],
797
833
) -> wrappers .MessageType :
798
834
"""Load message descriptions from DescriptorProtos."""
799
835
address = address .child (message_pb .name , path )
@@ -810,12 +846,14 @@ def _load_message(self,
810
846
address = address ,
811
847
loader = self ._load_enum ,
812
848
path = path + (4 ,),
849
+ resources = resources ,
813
850
)
814
851
nested_messages = self ._load_children (
815
852
message_pb .nested_type ,
816
853
address = address ,
817
854
loader = self ._load_message ,
818
855
path = path + (3 ,),
856
+ resources = resources ,
819
857
)
820
858
821
859
oneofs = self ._get_oneofs (
@@ -856,6 +894,7 @@ def _load_enum(self,
856
894
enum : descriptor_pb2 .EnumDescriptorProto ,
857
895
address : metadata .Address ,
858
896
path : Tuple [int ],
897
+ resources : Mapping [str , wrappers .CommonResource ],
859
898
) -> wrappers .EnumType :
860
899
"""Load enum descriptions from EnumDescriptorProtos."""
861
900
address = address .child (enum .name , path )
@@ -886,6 +925,7 @@ def _load_service(self,
886
925
service : descriptor_pb2 .ServiceDescriptorProto ,
887
926
address : metadata .Address ,
888
927
path : Tuple [int ],
928
+ resources : Mapping [str , wrappers .CommonResource ],
889
929
) -> wrappers .Service :
890
930
"""Load comments for a service and its methods."""
891
931
address = address .child (service .name , path )
@@ -905,6 +945,7 @@ def _load_service(self,
905
945
),
906
946
methods = methods ,
907
947
service_pb = service ,
948
+ visible_resources = resources ,
908
949
)
909
950
return self .proto_services [address .proto ]
910
951
0 commit comments