Skip to content

Commit f64ada2

Browse files
authored
fix: avoid retaining routing parameter instances in cache (#17961)
functools.cache keeps method arguments, including self, in its unbounded cache. As a result, RoutingParameter instances remain alive after routing resolution. Use the existing instance-scoped utils.cached_property for the compiled regex and routing key while preserving the to_regex() method API. Add a regression test that verifies a populated cache does not retain the instance. Testing: - pytest packages/gapic-generator/tests/unit/schema/wrappers/test_routing.py -q (Python 3.10 and 3.14) - pytest packages/gapic-generator/tests/unit/schema/wrappers -q (Python 3.14)
1 parent bd1e224 commit f64ada2

2 files changed

Lines changed: 24 additions & 7 deletions

File tree

packages/gapic-generator/gapic/schema/wrappers.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import collections
3131
import copy
3232
import dataclasses
33-
import functools
3433
import json
3534
import keyword
3635
import re
@@ -1289,14 +1288,14 @@ def _to_regex(self, path_template: str) -> Pattern:
12891288
"""
12901289
return re.compile(f"^{self._convert_to_regex(path_template)}$")
12911290

1292-
# Use caching to avoid repeated computation
1293-
@functools.cache
1294-
def to_regex(self) -> Pattern:
1291+
@utils.cached_property
1292+
def _regex(self) -> Pattern:
12951293
return self._to_regex(self.path_template)
12961294

1297-
@property
1298-
# Use caching to avoid repeated computation
1299-
@functools.cache
1295+
def to_regex(self) -> Pattern:
1296+
return self._regex
1297+
1298+
@utils.cached_property
13001299
def key(self) -> Union[str, None]:
13011300
if self.path_template == "":
13021301
return self.field

packages/gapic-generator/tests/unit/schema/wrappers/test_routing.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414

1515
from gapic.schema import wrappers
1616

17+
import gc
1718
import json
19+
import uuid
20+
import weakref
1821
import proto
1922
import pytest
2023

@@ -146,6 +149,21 @@ def test_routing_parameter_key(field, path_template, expected):
146149
assert param.key == expected
147150

148151

152+
def test_routing_parameter_cache_does_not_retain_instance():
153+
unique_id = f"id_{uuid.uuid4().hex}"
154+
param = wrappers.RoutingParameter(
155+
f"table_name_{unique_id}", f"{{{unique_id}=projects/*}}/instances/*/**"
156+
)
157+
_ = param.to_regex()
158+
_ = param.key
159+
param_ref = weakref.ref(param)
160+
161+
del param
162+
gc.collect()
163+
164+
assert param_ref() is None
165+
166+
149167
def test_routing_parameter_multi_segment_raises():
150168
param = wrappers.RoutingParameter(
151169
"table_name", "{project_id=projects/*}/{instance_id=instances/*}/*/**"

0 commit comments

Comments
 (0)