Skip to content

Commit 526a2d3

Browse files
committed
Add annotations to metadata interceptor, other style improvements.
Change-Id: I58040150b1dbbbbfe4d2e19dc6384d8f26d9f6c1
1 parent 7ed19c7 commit 526a2d3

File tree

5 files changed

+81
-54
lines changed

5 files changed

+81
-54
lines changed

google/ads/googleads/interceptors/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
# limitations under the License.
1414

1515
from .helpers import mask_message
16-
from .interceptor import Interceptor, MetadataType
16+
from .interceptor import Interceptor, MetadataType, ContinuationType
1717
from .metadata_interceptor import MetadataInterceptor
1818
from .exception_interceptor import ExceptionInterceptor
1919
from .logging_interceptor import LoggingInterceptor
2020

2121
__all__ = [
22+
"ContinuationType",
2223
"ExceptionInterceptor",
2324
"Interceptor",
2425
"LoggingInterceptor",

google/ads/googleads/interceptors/exception_interceptor.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
so it translates the error to a GoogleAdsFailure instance and raises it.
2020
"""
2121

22-
from typing import Callable, Optional, Union
22+
from typing import Optional, Union
2323

2424
from google.protobuf.message import Message
2525
import grpc
2626

27-
from google.ads.googleads.interceptors import Interceptor
27+
from google.ads.googleads.interceptors import Interceptor, ContinuationType
2828
from google.ads.googleads.response_wrappers import _UnaryStreamWrapper, _UnaryUnaryWrapper
2929

3030

@@ -73,7 +73,7 @@ def _handle_grpc_failure(self, response: Union[grpc.Call, grpc.Future]):
7373

7474
def intercept_unary_unary(
7575
self,
76-
continuation: Callable[[grpc.ClientCallDetails, Message], grpc.Call],
76+
continuation: ContinuationType,
7777
client_call_details: grpc.ClientCallDetails,
7878
request: Message,
7979
):
@@ -98,7 +98,7 @@ def intercept_unary_unary(
9898
indicative of a GoogleAdsException, or if the exception has a
9999
status code of INTERNAL or RESOURCE_EXHAUSTED.
100100
"""
101-
response: grpc._interceptor._UnaryOutcome = continuation(client_call_details, request)
101+
response: grpc.Call = continuation(client_call_details, request)
102102
exception: Optional[grpc.RpcError] = response.exception()
103103

104104
if exception:
@@ -110,7 +110,7 @@ def intercept_unary_unary(
110110

111111
def intercept_unary_stream(
112112
self,
113-
continuation: Callable[[grpc.ClientCallDetails, Message], grpc.Call],
113+
continuation: ContinuationType,
114114
client_call_details: grpc.ClientCallDetails,
115115
request: Message,
116116
):
@@ -135,7 +135,7 @@ def intercept_unary_stream(
135135
indicative of a GoogleAdsException, or if the exception has a
136136
status code of INTERNAL or RESOURCE_EXHAUSTED.
137137
"""
138-
response: grpc._interceptor._UnaryOutcome = continuation(client_call_details, request)
138+
response: grpc.Call = continuation(client_call_details, request)
139139
return _UnaryStreamWrapper(
140140
response,
141141
self._handle_grpc_failure,

google/ads/googleads/interceptors/interceptor.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,38 @@
2525
from typing import (
2626
Any,
2727
AnyStr,
28+
Callable,
2829
Dict,
2930
Optional,
3031
Sequence,
3132
Tuple
3233
)
3334

3435
from google.protobuf.message import DecodeError, Message
35-
from grpc import Call, ClientCallDetails, StatusCode, CallCredentials, RpcError
36+
import grpc
37+
# from grpc import Call, ClientCallDetails, StatusCode, CallCredentials, RpcError
3638

3739
from google.ads.googleads.errors import GoogleAdsException
3840

3941

4042
_REQUEST_ID_KEY: str = "request-id"
4143
# Codes that are retried upon by google.api_core.
42-
_RETRY_STATUS_CODES: Tuple[StatusCode]= (StatusCode.INTERNAL, StatusCode.RESOURCE_EXHAUSTED)
44+
_RETRY_STATUS_CODES: Tuple[grpc.StatusCode]= (grpc.StatusCode.INTERNAL, grpc.StatusCode.RESOURCE_EXHAUSTED)
4345

4446
MetadataType = Sequence[Tuple[str, AnyStr]]
45-
47+
ContinuationType = Callable[[grpc.ClientCallDetails, Message], grpc.Call]
4648

4749
class Interceptor:
4850
_SENSITIVE_INFO_MASK: str = "REDACTED"
4951

5052
@dataclass
51-
class _ClientCallDetails(ClientCallDetails):
53+
class _ClientCallDetails(grpc.ClientCallDetails):
5254
"""Wrapper class for initializing a new ClientCallDetails instance."""
5355

5456
method: str
5557
timeout: Optional[float]
5658
metadata: Optional[MetadataType]
57-
credentials: Optional[CallCredentials]
59+
credentials: Optional[grpc.CallCredentials]
5860

5961
@classmethod
6062
def get_request_id_from_metadata(
@@ -134,7 +136,7 @@ def default_serializer(value: Any) -> Optional[str]:
134136

135137
@classmethod
136138
def get_trailing_metadata_from_interceptor_exception(
137-
cls, exception: RpcError
139+
cls, exception: grpc.RpcError
138140
) -> MetadataType:
139141
"""Retrieves trailing metadata from an exception object.
140142
@@ -164,8 +166,8 @@ def get_client_call_details_instance(
164166
method: str,
165167
timeout: float,
166168
metadata: MetadataType,
167-
credentials: Optional[CallCredentials] = None,
168-
) -> ClientCallDetails:
169+
credentials: Optional[grpc.CallCredentials] = None,
170+
) -> grpc.ClientCallDetails:
169171
"""Initializes an instance of the ClientCallDetails with the given data.
170172
171173
Args:
@@ -186,7 +188,7 @@ def __init__(self, api_version: str):
186188
)
187189
self._api_version: str = api_version
188190

189-
def _get_error_from_response(self, response: Call) -> Call:
191+
def _get_error_from_response(self, response: grpc.Call) -> grpc.Call:
190192
"""Attempts to wrap failed responses as GoogleAdsException instances.
191193
192194
Handles failed gRPC responses of by attempting to convert them
@@ -210,8 +212,8 @@ def _get_error_from_response(self, response: Call) -> Call:
210212
Exception: If not a GoogleAdsException or RpcException the error
211213
will be raised as-is.
212214
"""
213-
status_code: StatusCode = response.code()
214-
response_exception: Call = response.exception()
215+
status_code: grpc.StatusCode = response.code()
216+
response_exception: grpc.Call = response.exception()
215217

216218
if status_code not in _RETRY_STATUS_CODES:
217219
trailing_metadata: MetadataType = response.trailing_metadata()

google/ads/googleads/interceptors/logging_interceptor.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121

2222
import json
2323
import logging
24-
from typing import Callable, Optional, Union
24+
from typing import Optional, Union
2525

2626
from google.protobuf.message import Message as ProtobufMessageType
2727
import grpc
2828

29-
from google.ads.googleads.interceptors import Interceptor, MetadataType, mask_message
29+
from google.ads.googleads.interceptors import (
30+
Interceptor, MetadataType, ContinuationType, mask_message
31+
)
3032

3133

3234
class LoggingInterceptor(
@@ -339,7 +341,7 @@ def log_request(
339341

340342
def intercept_unary_unary(
341343
self,
342-
continuation: Callable[[grpc.ClientCallDetails, ProtobufMessageType], grpc.Call],
344+
continuation: ContinuationType,
343345
client_call_details: grpc.ClientCallDetails,
344346
request: ProtobufMessageType,
345347
) -> Union[grpc.Call, grpc.Future]:
@@ -357,7 +359,7 @@ def intercept_unary_unary(
357359
Returns:
358360
A grpc.Call/grpc.Future instance representing a service response.
359361
"""
360-
response: grpc._interceptor._UnaryOutcome = continuation(client_call_details, request)
362+
response: grpc.Call = continuation(client_call_details, request)
361363

362364
if self.logger.isEnabledFor(logging.WARNING):
363365
self.log_request(client_call_details, request, response)
@@ -366,7 +368,7 @@ def intercept_unary_unary(
366368

367369
def intercept_unary_stream(
368370
self,
369-
continuation: Callable[[grpc.ClientCallDetails, ProtobufMessageType], grpc.Call],
371+
continuation: ContinuationType,
370372
client_call_details: grpc.ClientCallDetails,
371373
request: ProtobufMessageType,
372374
) -> Union[grpc.Call, grpc.Future]:
@@ -388,7 +390,7 @@ def on_rpc_complete(response_future: grpc.Future) -> None:
388390
if self.logger.isEnabledFor(logging.WARNING):
389391
self.log_request(client_call_details, request, response_future)
390392

391-
response: grpc._interceptor._UnaryOutcome = continuation(client_call_details, request)
393+
response: grpc.Call = continuation(client_call_details, request)
392394

393395
response.add_done_callback(on_rpc_complete)
394396
self._cache = response.get_cache()

google/ads/googleads/interceptors/metadata_interceptor.py

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,21 @@
2424
# request user-agent directly by the google-api-core package:
2525
# https://github.com/googleapis/python-api-core/issues/416
2626
from importlib import metadata
27+
from typing import List, Optional, Tuple, Union
2728

29+
from google.protobuf.internal import api_implementation
30+
from google.protobuf.message import Message as ProtobufMessageType
31+
import grpc
32+
33+
from google.ads.googleads.interceptors import Interceptor, MetadataType, ContinuationType
34+
35+
36+
# Determine which version of the package is installed.
2837
try:
2938
_PROTOBUF_VERSION = metadata.version("protobuf")
3039
except metadata.PackageNotFoundError:
3140
_PROTOBUF_VERSION = None
3241

33-
34-
from google.protobuf.internal import api_implementation
35-
from grpc import UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor
36-
37-
from .interceptor import Interceptor
38-
3942
# Determine which protobuf implementation is being used.
4043
if api_implementation.Type() == "cpp":
4144
_PB_IMPL_HEADER = "+c"
@@ -44,18 +47,19 @@
4447
else:
4548
_PB_IMPL_HEADER = ""
4649

47-
4850
class MetadataInterceptor(
49-
Interceptor, UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor
51+
Interceptor,
52+
grpc.UnaryUnaryClientInterceptor,
53+
grpc.UnaryStreamClientInterceptor
5054
):
5155
"""An interceptor that appends custom metadata to requests."""
5256

5357
def __init__(
5458
self,
55-
developer_token,
56-
login_customer_id,
57-
linked_customer_id=None,
58-
use_cloud_org_for_api_access=None,
59+
developer_token: str,
60+
login_customer_id: Optional[str]= None,
61+
linked_customer_id: Optional[str] = None,
62+
use_cloud_org_for_api_access: Optional[bool] = None,
5963
):
6064
"""Initialization method for this class.
6165
@@ -69,21 +73,26 @@ def __init__(
6973
levels. Use this flag only if you are enrolled into a limited
7074
pilot that supports this configuration
7175
"""
72-
self.developer_token_meta = ("developer-token", developer_token)
73-
self.login_customer_id_meta = (
76+
self.developer_token_meta: Tuple[str, str] = (
77+
"developer-token",
78+
developer_token,
79+
)
80+
self.login_customer_id_meta: Optional[Tuple[str, str]] = (
7481
("login-customer-id", login_customer_id)
7582
if login_customer_id
7683
else None
7784
)
78-
self.linked_customer_id_meta = (
85+
self.linked_customer_id_meta: Optional[Tuple[str, str]] = (
7986
("linked-customer-id", linked_customer_id)
8087
if linked_customer_id
8188
else None
8289
)
83-
self.use_cloud_org_for_api_access = use_cloud_org_for_api_access
90+
self.use_cloud_org_for_api_access: Optional[
91+
bool
92+
] = use_cloud_org_for_api_access
8493

8594
def _update_client_call_details_metadata(
86-
self, client_call_details, metadata
95+
self, client_call_details: grpc.ClientCallDetails, metadata: MetadataType
8796
):
8897
"""Updates the client call details with additional metadata.
8998
@@ -95,7 +104,7 @@ def _update_client_call_details_metadata(
95104
An new instance of grpc.ClientCallDetails with additional metadata
96105
from the GoogleAdsClient.
97106
"""
98-
client_call_details = self.get_client_call_details_instance(
107+
client_call_details: grpc.ClientCallDetails = self.get_client_call_details_instance(
99108
client_call_details.method,
100109
client_call_details.timeout,
101110
metadata,
@@ -104,7 +113,12 @@ def _update_client_call_details_metadata(
104113

105114
return client_call_details
106115

107-
def _intercept(self, continuation, client_call_details, request):
116+
def _intercept(
117+
self,
118+
continuation: ContinuationType,
119+
client_call_details: grpc.ClientCallDetails,
120+
request: ProtobufMessageType,
121+
) -> Union[grpc.Call, grpc.Future]:
108122
"""Generic interceptor used for Unary-Unary and Unary-Stream requests.
109123
110124
Args:
@@ -118,9 +132,9 @@ def _intercept(self, continuation, client_call_details, request):
118132
A grpc.Call/grpc.Future instance representing a service response.
119133
"""
120134
if client_call_details.metadata is None:
121-
metadata = []
135+
metadata: MetadataType = []
122136
else:
123-
metadata = list(client_call_details.metadata)
137+
metadata: MetadataType = list(client_call_details.metadata)
124138

125139
# If self.use_cloud_org_for_api_access is not True, add the developer
126140
# token to the request's metadata
@@ -135,31 +149,36 @@ def _intercept(self, continuation, client_call_details, request):
135149

136150
# TODO: This logic should be updated or removed once the following is
137151
# fixed: https://github.com/googleapis/python-api-core/issues/416
138-
for i, metadatum in enumerate(metadata):
152+
for i, metadatum_tuple in enumerate(metadata):
139153
# Check if the user agent header key is in the current metadatum
140-
if "x-goog-api-client" in metadatum and _PROTOBUF_VERSION:
154+
if "x-goog-api-client" in metadatum_tuple and _PROTOBUF_VERSION:
141155
# Convert the tuple to a list so it can be modified.
142-
metadatum = list(metadatum)
156+
metadatum: List[str] = list(metadatum_tuple)
143157
# Check that "pb" isn't already included in the user agent.
144158
if "pb" not in metadatum[1]:
145159
# Append the protobuf version key value pair to the end of
146160
# the string.
147161
metadatum[1] += f" pb/{_PROTOBUF_VERSION}{_PB_IMPL_HEADER}"
148162
# Convert the metadatum back to a tuple.
149-
metadatum = tuple(metadatum)
163+
metadatum_tuple: Tuple[str, str] = tuple(metadatum)
150164
# Splice the metadatum back in its original position in
151165
# order to preserve the order of the metadata list.
152-
metadata[i] = metadatum
166+
metadata[i] = metadatum_tuple
153167
# Exit the loop since we already found the user agent.
154168
break
155169

156-
client_call_details = self._update_client_call_details_metadata(
170+
client_call_details: grpc.ClientCallDetails = self._update_client_call_details_metadata(
157171
client_call_details, metadata
158172
)
159173

160174
return continuation(client_call_details, request)
161175

162-
def intercept_unary_unary(self, continuation, client_call_details, request):
176+
def intercept_unary_unary(
177+
self,
178+
continuation: ContinuationType,
179+
client_call_details: grpc.ClientCallDetails,
180+
request: ProtobufMessageType,
181+
) -> Union[grpc.Call, grpc.Future]:
163182
"""Intercepts and appends custom metadata for Unary-Unary requests.
164183
165184
Overrides abstract method defined in grpc.UnaryUnaryClientInterceptor.
@@ -177,8 +196,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
177196
return self._intercept(continuation, client_call_details, request)
178197

179198
def intercept_unary_stream(
180-
self, continuation, client_call_details, request
181-
):
199+
self,
200+
continuation: ContinuationType,
201+
client_call_details: grpc.ClientCallDetails,
202+
request: ProtobufMessageType,
203+
) -> Union[grpc.Call, grpc.Future]:
182204
"""Intercepts and appends custom metadata to Unary-Stream requests.
183205
184206
Overrides abstract method defined in grpc.UnaryStreamClientInterceptor.

0 commit comments

Comments
 (0)