Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit eed7529

Browse files
committed
change loc
1 parent d18e777 commit eed7529

3 files changed

Lines changed: 110 additions & 145 deletions

File tree

gapic/cli/generate.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from gapic import generator
2424
from gapic.schema import api
2525
from gapic.utils import Options
26-
26+
from gapic.utils.cache import generation_cache_context
2727

2828

2929
@click.command()
@@ -57,16 +57,16 @@ def generate(request: typing.BinaryIO, output: typing.BinaryIO) -> None:
5757
[p.package for p in req.proto_file if p.name in req.file_to_generate]
5858
).rstrip(".")
5959

60-
61-
# Build the API model object.
62-
# This object is a frozen representation of the whole API, and is sent
63-
# to each template in the rendering step.
64-
api_schema = api.API.build(req.proto_file, opts=opts, package=package)
60+
with generation_cache_context():
61+
# Build the API model object.
62+
# This object is a frozen representation of the whole API, and is sent
63+
# to each template in the rendering step.
64+
api_schema = api.API.build(req.proto_file, opts=opts, package=package)
6565

66-
# Translate into a protobuf CodeGeneratorResponse; this reads the
67-
# individual templates and renders them.
68-
# If there are issues, error out appropriately.
69-
res = generator.Generator(opts).get_response(api_schema, opts)
66+
# Translate into a protobuf CodeGeneratorResponse; this reads the
67+
# individual templates and renders them.
68+
# If there are issues, error out appropriately.
69+
res = generator.Generator(opts).get_response(api_schema, opts)
7070

7171
# Output the serialized response.
7272
output.write(res.SerializeToString())

gapic/schema/api.py

Lines changed: 99 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
from gapic.utils import Options
6565
from gapic.utils import to_snake_case
6666
from gapic.utils import RESERVED_NAMES
67-
from gapic.utils.cache import generation_cache_context
6867

6968

7069
TRANSPORT_GRPC = "grpc"
@@ -455,116 +454,117 @@ def disambiguate_keyword_sanitize_fname(
455454
# type into the proto file that defines an LRO.
456455
# We just load all the APIs types first and then
457456
# load the services and methods with the full scope of types.
458-
with generation_cache_context():
459-
pre_protos: Dict[str, Proto] = dict(prior_protos or {})
460-
for fd in file_descriptors:
461-
fd.name = disambiguate_keyword_sanitize_fname(fd.name, pre_protos)
462-
pre_protos[fd.name] = Proto.build(
463-
file_descriptor=fd,
464-
file_to_generate=fd.package.startswith(package),
465-
naming=naming,
466-
opts=opts,
467-
prior_protos=pre_protos,
468-
# Ugly, ugly hack.
469-
load_services=False,
470-
)
471-
472-
# A file descriptor's file-level resources are NOT visible to any importers.
473-
# The only way to make referenced resources visible is to aggregate them at
474-
# the API level and then pass that around.
475-
all_file_resources = collections.ChainMap(
476-
*(proto.resource_messages for proto in pre_protos.values())
457+
pre_protos: Dict[str, Proto] = dict(prior_protos or {})
458+
for fd in file_descriptors:
459+
fd.name = disambiguate_keyword_sanitize_fname(fd.name, pre_protos)
460+
pre_protos[fd.name] = Proto.build(
461+
file_descriptor=fd,
462+
file_to_generate=fd.package.startswith(package),
463+
naming=naming,
464+
opts=opts,
465+
prior_protos=pre_protos,
466+
# Ugly, ugly hack.
467+
load_services=False,
477468
)
478469

479-
# Second pass uses all the messages and enums defined in the entire API.
480-
# This allows LRO returning methods to see all the types in the API,
481-
# bypassing the above missing import problem.
482-
protos: Dict[str, Proto] = {
483-
name: Proto.build(
484-
file_descriptor=proto.file_pb2,
485-
file_to_generate=proto.file_to_generate,
486-
naming=naming,
487-
opts=opts,
488-
prior_protos=pre_protos,
489-
all_resources=MappingProxyType(all_file_resources),
490-
)
491-
for name, proto in pre_protos.items()
492-
}
493-
494-
# Parse the google.api.Service proto from the service_yaml data.
495-
service_yaml_config = service_pb2.Service()
496-
ParseDict(
497-
opts.service_yaml_config, service_yaml_config, ignore_unknown_fields=True
498-
)
499-
gapic_version = opts.gapic_version
470+
# A file descriptor's file-level resources are NOT visible to any importers.
471+
# The only way to make referenced resources visible is to aggregate them at
472+
# the API level and then pass that around.
473+
all_file_resources = collections.ChainMap(
474+
*(proto.resource_messages for proto in pre_protos.values())
475+
)
500476

501-
# Third pass for various selective GAPIC settings; these require
502-
# settings in the service.yaml and so we build the API object
503-
# before doing another pass.
504-
api = cls(
477+
# Second pass uses all the messages and enums defined in the entire API.
478+
# This allows LRO returning methods to see all the types in the API,
479+
# bypassing the above missing import problem.
480+
protos: Dict[str, Proto] = {
481+
name: Proto.build(
482+
file_descriptor=proto.file_pb2,
483+
file_to_generate=proto.file_to_generate,
505484
naming=naming,
506-
all_protos=protos,
507-
service_yaml_config=service_yaml_config,
508-
gapic_version=gapic_version,
485+
opts=opts,
486+
prior_protos=pre_protos,
487+
all_resources=MappingProxyType(all_file_resources),
509488
)
489+
for name, proto in pre_protos.items()
490+
}
510491

511-
if package in api.all_library_settings:
512-
selective_gapic_settings = api.all_library_settings[
513-
package
514-
].python_settings.common.selective_gapic_generation
515-
516-
selective_gapic_methods = set(selective_gapic_settings.methods)
517-
if selective_gapic_methods:
518-
# The list of explicitly allow-listed protos to generate, plus all
519-
# the proto dependencies regardless of the allow-list.
520-
#
521-
# Both selective GAPIC generation settings (omitting + internal) only alter
522-
# protos that are not dependencies, so we iterate over api.all_protos and copy
523-
# all dependencies as is here.
524-
new_all_protos = {
525-
k: v for k, v in api.all_protos.items() if k not in api.protos
526-
}
492+
# Parse the google.api.Service proto from the service_yaml data.
493+
service_yaml_config = service_pb2.Service()
494+
ParseDict(
495+
opts.service_yaml_config,
496+
service_yaml_config,
497+
ignore_unknown_fields=True,
498+
)
499+
gapic_version = opts.gapic_version
527500

528-
if selective_gapic_settings.generate_omitted_as_internal:
529-
for name, proto in api.protos.items():
530-
new_all_protos[name] = proto.with_internal_methods(
531-
public_methods=selective_gapic_methods
532-
)
533-
else:
534-
all_resource_messages = collections.ChainMap(
535-
*(proto.resource_messages for proto in protos.values())
501+
# Third pass for various selective GAPIC settings; these require
502+
# settings in the service.yaml and so we build the API object
503+
# before doing another pass.
504+
api = cls(
505+
naming=naming,
506+
all_protos=protos,
507+
service_yaml_config=service_yaml_config,
508+
gapic_version=gapic_version,
509+
)
510+
511+
if package in api.all_library_settings:
512+
selective_gapic_settings = api.all_library_settings[
513+
package
514+
].python_settings.common.selective_gapic_generation
515+
516+
selective_gapic_methods = set(selective_gapic_settings.methods)
517+
if selective_gapic_methods:
518+
# The list of explicitly allow-listed protos to generate, plus all
519+
# the proto dependencies regardless of the allow-list.
520+
#
521+
# Both selective GAPIC generation settings (omitting + internal) only alter
522+
# protos that are not dependencies, so we iterate over api.all_protos and copy
523+
# all dependencies as is here.
524+
new_all_protos = {
525+
k: v for k, v in api.all_protos.items() if k not in api.protos
526+
}
527+
528+
if selective_gapic_settings.generate_omitted_as_internal:
529+
for name, proto in api.protos.items():
530+
new_all_protos[name] = proto.with_internal_methods(
531+
public_methods=selective_gapic_methods
536532
)
533+
else:
534+
all_resource_messages = collections.ChainMap(
535+
*(proto.resource_messages for proto in protos.values())
536+
)
537537

538-
# Prepare a list of addresses to include in selective generation,
539-
# then prune each Proto object. We look at metadata.Addresses, not objects, because
540-
# objects that refer to the same thing in the proto are different Python objects
541-
# in memory.
542-
address_allowlist: Set["metadata.Address"] = set([])
543-
for proto in api.protos.values():
544-
proto.add_to_address_allowlist(
545-
address_allowlist=address_allowlist,
546-
method_allowlist=selective_gapic_methods,
547-
resource_messages=all_resource_messages,
548-
)
538+
# Prepare a list of addresses to include in selective generation,
539+
# then prune each Proto object. We look at metadata.Addresses, not objects, because
540+
# objects that refer to the same thing in the proto are different Python objects
541+
# in memory.
542+
address_allowlist: Set["metadata.Address"] = set([])
543+
for proto in api.protos.values():
544+
proto.add_to_address_allowlist(
545+
address_allowlist=address_allowlist,
546+
method_allowlist=selective_gapic_methods,
547+
resource_messages=all_resource_messages,
548+
)
549549

550-
# We only prune services/messages/enums from protos that are not dependencies.
551-
for name, proto in api.protos.items():
552-
proto_to_generate = (
553-
proto.prune_messages_for_selective_generation(
554-
address_allowlist=address_allowlist
555-
)
550+
# We only prune services/messages/enums from protos that are not dependencies.
551+
for name, proto in api.protos.items():
552+
proto_to_generate = (
553+
proto.prune_messages_for_selective_generation(
554+
address_allowlist=address_allowlist
556555
)
557-
if proto_to_generate:
558-
new_all_protos[name] = proto_to_generate
559-
560-
api = cls(
561-
naming=naming,
562-
all_protos=new_all_protos,
563-
service_yaml_config=service_yaml_config,
564-
gapic_version=gapic_version,
565-
)
556+
)
557+
if proto_to_generate:
558+
new_all_protos[name] = proto_to_generate
559+
560+
api = cls(
561+
naming=naming,
562+
all_protos=new_all_protos,
563+
service_yaml_config=service_yaml_config,
564+
gapic_version=gapic_version,
565+
)
566566

567-
return api
567+
return api
568568

569569
@cached_property
570570
def enums(self) -> Mapping[str, wrappers.EnumType]:

tests/unit/utils/test_cache.py

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ def test_generation_cache_context():
4343

4444

4545
def test_cached_proto_context():
46-
# A dummy object to act as a visited message
47-
class MockMessage:
48-
pass
49-
5046
class Foo:
5147
def __init__(self):
5248
self.call_count = 0
@@ -59,8 +55,6 @@ def with_context(self, collisions, *, skip_fields=False, visited_messages=None):
5955
return f"val-{self.call_count}"
6056

6157
foo = Foo()
62-
msg_a = MockMessage()
63-
msg_b = MockMessage()
6458

6559
# 1. Test Bypass (No Context)
6660
# The cache is not active, so every call increments the counter.
@@ -82,36 +76,7 @@ def with_context(self, collisions, *, skip_fields=False, visited_messages=None):
8276
assert foo.with_context({"b"}) == "val-2"
8377
assert foo.call_count == 2
8478

85-
# C. skip_fields Difference (The Critical Fix)
86-
# Verify that skip_fields=True is cached separately from the default (False).
87-
# This prevents the "Husk" object from poisoning the "Full" object cache.
88-
assert foo.with_context({"a"}, skip_fields=True) == "val-3"
89-
assert foo.with_context({"a"}, skip_fields=True) == "val-3" # Hit
90-
assert foo.call_count == 3
91-
92-
# Verify the original (skip_fields=False) is still accessible
93-
assert foo.with_context({"a"}) == "val-1" # Hit (Still 1)
94-
95-
# D. visited_messages Difference
96-
# Verify that sets of objects are correctly distinguished.
97-
98-
# Call with msg_a
99-
assert foo.with_context({"a"}, visited_messages={msg_a}) == "val-4"
100-
assert foo.with_context({"a"}, visited_messages={msg_a}) == "val-4" # Hit
101-
assert foo.call_count == 4
102-
103-
# Call with msg_b (Should be a Miss)
104-
assert foo.with_context({"a"}, visited_messages={msg_b}) == "val-5"
105-
assert foo.call_count == 5
106-
107-
# E. Set Stability
108-
# Verify that the order of items in the set doesn't matter (sets are unordered).
109-
# {a, b} should cache-hit with {b, a}
110-
assert foo.with_context({"a"}, visited_messages={msg_a, msg_b}) == "val-6"
111-
assert foo.with_context({"a"}, visited_messages={msg_b, msg_a}) == "val-6" # Hit
112-
assert foo.call_count == 6
113-
11479
# 3. Context Cleared
11580
# Everything should be forgotten now.
11681
assert cache.generation_cache.get() is None
117-
assert foo.with_context({"a"}) == "val-7"
82+
assert foo.with_context({"a"}) == "val-7"

0 commit comments

Comments
 (0)