Skip to content

Commit 04a1519

Browse files
committed
chore: fix circular/relative imports and update rest_helpers transcode_request
1 parent 1af97f1 commit 04a1519

11 files changed

Lines changed: 257 additions & 156 deletions

File tree

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2

Lines changed: 79 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,105 +3,94 @@
33
from typing import Any, Dict, List, Optional, Tuple
44

55
try:
6-
from google.api_core import rest_helpers
7-
# Trigger fallback if rest_helpers is an older version missing 'transcode'
8-
if not hasattr(rest_helpers, "transcode"):
9-
raise ImportError
10-
except ImportError:
6+
from google.api_core.rest_helpers import (
7+
flatten_query_params,
8+
transcode_request,
9+
)
10+
except ImportError: # pragma: NO COVER
1111
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
1212
import functools
1313
import json
1414
import operator
1515
from google.protobuf import json_format # type: ignore
1616
from google.api_core import path_template # type: ignore
1717

18-
class _FallbackRestHelpers:
19-
@staticmethod
20-
def flatten_query_params(obj, strict=False):
21-
if obj is not None and not isinstance(obj, dict):
22-
raise TypeError("flatten_query_params must be called with dict object")
23-
return _FallbackRestHelpers._flatten(obj, key_path=[], strict=strict)
24-
25-
@staticmethod
26-
def _flatten(obj, key_path, strict=False):
27-
if obj is None:
28-
return []
29-
if isinstance(obj, dict):
30-
return _FallbackRestHelpers._flatten_dict(obj, key_path=key_path, strict=strict)
31-
if isinstance(obj, list):
32-
return _FallbackRestHelpers._flatten_list(obj, key_path=key_path, strict=strict)
33-
return _FallbackRestHelpers._flatten_value(obj, key_path=key_path, strict=strict)
34-
35-
@staticmethod
36-
def _is_primitive_value(obj):
37-
if obj is None:
38-
return False
39-
if isinstance(obj, (list, dict)):
40-
raise ValueError("query params may not contain repeated dicts or lists")
41-
return True
42-
43-
@staticmethod
44-
def _flatten_value(obj, key_path, strict=False):
45-
return [(".".join(key_path), _FallbackRestHelpers._canonicalize(obj, strict=strict))]
46-
47-
@staticmethod
48-
def _flatten_dict(obj, key_path, strict=False):
49-
items = (
50-
_FallbackRestHelpers._flatten(value, key_path=key_path + [key], strict=strict)
51-
for key, value in obj.items()
52-
)
53-
return functools.reduce(operator.concat, items, [])
54-
55-
@staticmethod
56-
def _flatten_list(elems, key_path, strict=False):
57-
items = (
58-
_FallbackRestHelpers._flatten_value(elem, key_path=key_path, strict=strict)
59-
for elem in elems
60-
if _FallbackRestHelpers._is_primitive_value(elem)
18+
def flatten_query_params(obj, strict=False): # pragma: NO COVER
19+
if obj is not None and not isinstance(obj, dict):
20+
raise TypeError("flatten_query_params must be called with dict object")
21+
return _flatten(obj, key_path=[], strict=strict)
22+
23+
def _flatten(obj, key_path, strict=False): # pragma: NO COVER
24+
if obj is None:
25+
return []
26+
if isinstance(obj, dict):
27+
return _flatten_dict(obj, key_path=key_path, strict=strict)
28+
if isinstance(obj, list):
29+
return _flatten_list(obj, key_path=key_path, strict=strict)
30+
return _flatten_value(obj, key_path=key_path, strict=strict)
31+
32+
def _is_primitive_value(obj): # pragma: NO COVER
33+
if obj is None:
34+
return False
35+
if isinstance(obj, (list, dict)):
36+
raise ValueError("query params may not contain repeated dicts or lists")
37+
return True
38+
39+
def _flatten_value(obj, key_path, strict=False): # pragma: NO COVER
40+
return [(".".join(key_path), _canonicalize(obj, strict=strict))]
41+
42+
def _flatten_dict(obj, key_path, strict=False): # pragma: NO COVER
43+
items = (
44+
_flatten(value, key_path=key_path + [key], strict=strict)
45+
for key, value in obj.items()
46+
)
47+
return functools.reduce(operator.concat, items, [])
48+
49+
def _flatten_list(elems, key_path, strict=False): # pragma: NO COVER
50+
items = (
51+
_flatten_value(elem, key_path=key_path, strict=strict)
52+
for elem in elems
53+
if _is_primitive_value(elem)
54+
)
55+
return functools.reduce(operator.concat, items, [])
56+
57+
def _canonicalize(obj, strict=False): # pragma: NO COVER
58+
if strict:
59+
value = str(obj)
60+
if isinstance(obj, bool):
61+
value = value.lower()
62+
return value
63+
return obj
64+
65+
def transcode_request( # pragma: NO COVER
66+
http_options: List[Dict[str, str]],
67+
request: Any,
68+
required_fields_default_values: Optional[Dict[str, Any]] = None,
69+
rest_numeric_enums: bool = False,
70+
) -> Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]:
71+
pb_request = getattr(request, "_pb", request)
72+
transcoded_request = path_template.transcode(http_options, pb_request)
73+
74+
body_json = None
75+
if transcoded_request.get("body") is not None:
76+
body_json = json_format.MessageToJson(
77+
transcoded_request["body"],
78+
use_integers_for_enums=rest_numeric_enums,
6179
)
62-
return functools.reduce(operator.concat, items, [])
63-
64-
@staticmethod
65-
def _canonicalize(obj, strict=False):
66-
if strict:
67-
value = str(obj)
68-
if isinstance(obj, bool):
69-
value = value.lower()
70-
return value
71-
return obj
72-
73-
@staticmethod
74-
def transcode(
75-
http_options: List[Dict[str, str]],
76-
request: Any,
77-
required_fields_default_values: Optional[Dict[str, Any]] = None,
78-
rest_numeric_enums: bool = False,
79-
) -> Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]:
80-
pb_request = getattr(request, "_pb", request)
81-
transcoded_request = path_template.transcode(http_options, pb_request)
82-
83-
body_json = None
84-
if transcoded_request.get("body") is not None:
85-
body_json = json_format.MessageToJson(
86-
transcoded_request["body"],
87-
use_integers_for_enums=rest_numeric_enums,
88-
)
89-
90-
query_params_json = {}
91-
if transcoded_request.get("query_params") is not None:
92-
query_params_json = json.loads(json_format.MessageToJson(
93-
transcoded_request["query_params"],
94-
use_integers_for_enums=rest_numeric_enums,
95-
))
9680

97-
if required_fields_default_values:
98-
for k, v in required_fields_default_values.items():
99-
if k not in query_params_json:
100-
query_params_json[k] = v
81+
query_params_json = {}
82+
if transcoded_request.get("query_params") is not None:
83+
query_params_json = json.loads(json_format.MessageToJson(
84+
transcoded_request["query_params"],
85+
use_integers_for_enums=rest_numeric_enums,
86+
))
10187

102-
if rest_numeric_enums:
103-
query_params_json["$alt"] = "json;enum-encoding=int"
88+
if required_fields_default_values:
89+
for k, v in required_fields_default_values.items():
90+
if k not in query_params_json:
91+
query_params_json[k] = v
10492

105-
return transcoded_request, body_json, query_params_json
93+
if rest_numeric_enums:
94+
query_params_json["$alt"] = "json;enum-encoding=int"
10695

107-
rest_helpers = _FallbackRestHelpers
96+
return transcoded_request, body_json, query_params_json

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def _get_http_options():
205205

206206
http_options = _Base{{ service_name }}RestTransport._Base{{method_name}}._get_http_options()
207207
request, metadata = {{ await_prefix }}self._interceptor.pre_{{ method_name|snake_case }}(request, metadata)
208-
transcoded_request, body, query_params = rest_helpers.transcode(
208+
transcoded_request, body, query_params = rest_helpers.transcode_request(
209209
http_options,
210210
request,
211211
required_fields_default_values=getattr(

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ from google.auth.transport.requests import AuthorizedSession # type: ignore
1010
from google.auth import credentials as ga_credentials # type: ignore
1111
from google.api_core import exceptions as core_exceptions
1212
from google.api_core import retry as retries
13-
from .. import _compat as rest_helpers
13+
{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
14+
from {{package_path}} import _compat as rest_helpers
1415
from google.api_core import rest_streaming
1516
from google.api_core import gapic_v1
1617
import google.protobuf

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ from google.iam.v1 import policy_pb2 # type: ignore
3232
from google.cloud.location import locations_pb2 # type: ignore
3333
{% endif %}
3434
from google.api_core import retry_async as retries
35-
from .. import _compat as rest_helpers
35+
{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
36+
from {{package_path}} import _compat as rest_helpers
3637
from google.api_core import rest_streaming_async # type: ignore
3738
import google.protobuf
3839

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# # Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
from typing import Any, Dict, List, Optional, Tuple
16+
17+
try:
18+
from google.api_core.rest_helpers import (
19+
flatten_query_params,
20+
transcode_request,
21+
)
22+
except ImportError: # pragma: NO COVER
23+
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
24+
import functools
25+
import json
26+
import operator
27+
from google.protobuf import json_format # type: ignore
28+
from google.api_core import path_template # type: ignore
29+
30+
def flatten_query_params(obj, strict=False): # pragma: NO COVER
31+
if obj is not None and not isinstance(obj, dict):
32+
raise TypeError("flatten_query_params must be called with dict object")
33+
return _flatten(obj, key_path=[], strict=strict)
34+
35+
def _flatten(obj, key_path, strict=False): # pragma: NO COVER
36+
if obj is None:
37+
return []
38+
if isinstance(obj, dict):
39+
return _flatten_dict(obj, key_path=key_path, strict=strict)
40+
if isinstance(obj, list):
41+
return _flatten_list(obj, key_path=key_path, strict=strict)
42+
return _flatten_value(obj, key_path=key_path, strict=strict)
43+
44+
def _is_primitive_value(obj): # pragma: NO COVER
45+
if obj is None:
46+
return False
47+
if isinstance(obj, (list, dict)):
48+
raise ValueError("query params may not contain repeated dicts or lists")
49+
return True
50+
51+
def _flatten_value(obj, key_path, strict=False): # pragma: NO COVER
52+
return [(".".join(key_path), _canonicalize(obj, strict=strict))]
53+
54+
def _flatten_dict(obj, key_path, strict=False): # pragma: NO COVER
55+
items = (
56+
_flatten(value, key_path=key_path + [key], strict=strict)
57+
for key, value in obj.items()
58+
)
59+
return functools.reduce(operator.concat, items, [])
60+
61+
def _flatten_list(elems, key_path, strict=False): # pragma: NO COVER
62+
items = (
63+
_flatten_value(elem, key_path=key_path, strict=strict)
64+
for elem in elems
65+
if _is_primitive_value(elem)
66+
)
67+
return functools.reduce(operator.concat, items, [])
68+
69+
def _canonicalize(obj, strict=False): # pragma: NO COVER
70+
if strict:
71+
value = str(obj)
72+
if isinstance(obj, bool):
73+
value = value.lower()
74+
return value
75+
return obj
76+
77+
def transcode_request( # pragma: NO COVER
78+
http_options: List[Dict[str, str]],
79+
request: Any,
80+
required_fields_default_values: Optional[Dict[str, Any]] = None,
81+
rest_numeric_enums: bool = False,
82+
) -> Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]:
83+
pb_request = getattr(request, "_pb", request)
84+
transcoded_request = path_template.transcode(http_options, pb_request)
85+
86+
body_json = None
87+
if transcoded_request.get("body") is not None:
88+
body_json = json_format.MessageToJson(
89+
transcoded_request["body"],
90+
use_integers_for_enums=rest_numeric_enums,
91+
)
92+
93+
query_params_json = {}
94+
if transcoded_request.get("query_params") is not None:
95+
query_params_json = json.loads(json_format.MessageToJson(
96+
transcoded_request["query_params"],
97+
use_integers_for_enums=rest_numeric_enums,
98+
))
99+
100+
if required_fields_default_values:
101+
for k, v in required_fields_default_values.items():
102+
if k not in query_params_json:
103+
query_params_json[k] = v
104+
105+
if rest_numeric_enums:
106+
query_params_json["$alt"] = "json;enum-encoding=int"
107+
108+
return transcoded_request, body_json, query_params_json

0 commit comments

Comments
 (0)