Skip to content

Commit c8ec052

Browse files
authored
Add type annotations to all interceptor files (#1041)
1 parent 4808c74 commit c8ec052

File tree

8 files changed

+338
-215
lines changed

8 files changed

+338
-215
lines changed

google/ads/googleads/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from google.api_core.gapic_v1.client_info import ClientInfo
2020
import grpc
2121
from proto.enums import ProtoEnumMeta
22+
from google.auth.credentials import Credentials
2223

2324
from google.protobuf.message import Message as ProtobufMessageType
2425
from proto import Message as ProtoPlusMessageType
@@ -339,7 +340,7 @@ def __init__(
339340
if logging_config:
340341
logging.config.dictConfig(logging_config)
341342

342-
self.credentials: Dict[str, Any] = credentials
343+
self.credentials: Credentials = credentials
343344
self.developer_token: str = developer_token
344345
self.endpoint: Union[str, None] = endpoint
345346
self.login_customer_id: Union[str, None] = login_customer_id

google/ads/googleads/interceptors/__init__.py

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

1515
from .helpers import mask_message
16-
from .interceptor import Interceptor
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
20+
21+
__all__ = [
22+
"ContinuationType",
23+
"ExceptionInterceptor",
24+
"Interceptor",
25+
"LoggingInterceptor",
26+
"mask_message",
27+
"MetadataInterceptor",
28+
"MetadataType",
29+
]

google/ads/googleads/interceptors/exception_interceptor.py

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

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

24-
from grpc import UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor
24+
from google.protobuf.message import Message
25+
import grpc
2526

26-
from .interceptor import Interceptor
27-
from .response_wrappers import _UnaryStreamWrapper, _UnaryUnaryWrapper
27+
from google.ads.googleads.interceptors import Interceptor, ContinuationType
28+
from google.ads.googleads.interceptors.response_wrappers import _UnaryStreamWrapper, _UnaryUnaryWrapper
2829

2930

3031
class ExceptionInterceptor(
31-
Interceptor, UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor
32+
Interceptor, grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor
3233
):
3334
"""An interceptor that wraps rpc exceptions."""
3435

35-
def __init__(self, api_version, use_proto_plus=False):
36+
def __init__(self, api_version: str, use_proto_plus: bool = False):
3637
"""Initializes the ExceptionInterceptor.
3738
3839
Args:
@@ -44,7 +45,7 @@ def __init__(self, api_version, use_proto_plus=False):
4445
self._api_version = api_version
4546
self._use_proto_plus = use_proto_plus
4647

47-
def _handle_grpc_failure(self, response):
48+
def _handle_grpc_failure(self, response: Union[grpc.Call, grpc.Future]):
4849
"""Attempts to convert failed responses to a GoogleAdsException object.
4950
5051
Handles failed gRPC responses of by attempting to convert them
@@ -70,7 +71,12 @@ def _handle_grpc_failure(self, response):
7071
"""
7172
raise self._get_error_from_response(response)
7273

73-
def intercept_unary_unary(self, continuation, client_call_details, request):
74+
def intercept_unary_unary(
75+
self,
76+
continuation: ContinuationType,
77+
client_call_details: grpc.ClientCallDetails,
78+
request: Message,
79+
):
7480
"""Intercepts and wraps exceptions in the rpc response.
7581
7682
Overrides abstract method defined in grpc.UnaryUnaryClientInterceptor.
@@ -92,8 +98,8 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
9298
indicative of a GoogleAdsException, or if the exception has a
9399
status code of INTERNAL or RESOURCE_EXHAUSTED.
94100
"""
95-
response = continuation(client_call_details, request)
96-
exception = response.exception()
101+
response: grpc.Call = continuation(client_call_details, request)
102+
exception: Optional[grpc.RpcError] = response.exception()
97103

98104
if exception:
99105
self._handle_grpc_failure(response)
@@ -103,7 +109,10 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
103109
)
104110

105111
def intercept_unary_stream(
106-
self, continuation, client_call_details, request
112+
self,
113+
continuation: ContinuationType,
114+
client_call_details: grpc.ClientCallDetails,
115+
request: Message,
107116
):
108117
"""Intercepts and wraps exceptions in the rpc response.
109118
@@ -126,7 +135,7 @@ def intercept_unary_stream(
126135
indicative of a GoogleAdsException, or if the exception has a
127136
status code of INTERNAL or RESOURCE_EXHAUSTED.
128137
"""
129-
response = continuation(client_call_details, request)
138+
response: grpc.Call = continuation(client_call_details, request)
130139
return _UnaryStreamWrapper(
131140
response,
132141
self._handle_grpc_failure,

google/ads/googleads/interceptors/helpers.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
# limitations under the License.
1414

1515
from copy import deepcopy
16+
from typing import Any, Dict, List, Tuple, Union
17+
18+
from google.protobuf.descriptor import FieldDescriptor
19+
from google.protobuf.message import Message as ProtobufMessage
20+
from proto import Message as ProtoPlusMessage
21+
1622
from google.ads.googleads.util import (
1723
set_nested_message_field,
1824
get_nested_attr,
@@ -29,7 +35,7 @@
2935
# 1. They are returned as part of a Search or SearchStream request.
3036
# 2. They are returned individually in a Get request.
3137
# 3. They are sent to the API as part of a Mutate request.
32-
_MESSAGES_WITH_SENSITIVE_FIELDS = {
38+
_MESSAGES_WITH_SENSITIVE_FIELDS: Dict[str, List[str]] = {
3339
"CustomerUserAccess": ["email_address", "inviter_user_email_address"],
3440
"CustomerUserAccessInvitation": ["email_address"],
3541
"MutateCustomerUserAccessRequest": [
@@ -50,13 +56,15 @@
5056
# This is a list of the names of messages that return search results from the
5157
# API. These messages contain other messages that may contain sensitive
5258
# information that needs to be masked before being logged.
53-
_SEARCH_RESPONSE_MESSAGE_NAMES = [
59+
_SEARCH_RESPONSE_MESSAGE_NAMES: List[str] = [
5460
"SearchGoogleAdsResponse",
5561
"SearchGoogleAdsStreamResponse",
5662
]
5763

5864

59-
def _copy_message(message):
65+
def _copy_message(
66+
message: Union[ProtobufMessage, ProtoPlusMessage]
67+
) -> Union[ProtobufMessage, ProtoPlusMessage]:
6068
"""Returns a copy of the given message.
6169
6270
Args:
@@ -69,7 +77,11 @@ def _copy_message(message):
6977
return deepcopy(message)
7078

7179

72-
def _mask_message_fields(field_list, message, mask):
80+
def _mask_message_fields(
81+
field_list: List[str],
82+
message: Union[ProtobufMessage, ProtoPlusMessage],
83+
mask: str
84+
) -> Union[ProtobufMessage, ProtoPlusMessage]:
7385
"""Copies the given message and masks sensitive fields.
7486
7587
Sensitive fields are given as a list of strings and are overridden
@@ -87,7 +99,7 @@ def _mask_message_fields(field_list, message, mask):
8799
A new instance of the message object with fields copied and masked
88100
where necessary.
89101
"""
90-
copy = _copy_message(message)
102+
copy: Union[ProtobufMessage, ProtoPlusMessage] = _copy_message(message)
91103

92104
for field_path in field_list:
93105
try:
@@ -103,7 +115,9 @@ def _mask_message_fields(field_list, message, mask):
103115
return copy
104116

105117

106-
def _mask_google_ads_search_response(message, mask):
118+
def _mask_google_ads_search_response(
119+
message: Union[ProtobufMessage, ProtoPlusMessage], mask: str
120+
) -> Union[ProtobufMessage, ProtoPlusMessage]:
107121
"""Copies and masks sensitive data in a Search response
108122
109123
Response messages include instances of GoogleAdsSearchResponse and
@@ -118,7 +132,7 @@ def _mask_google_ads_search_response(message, mask):
118132
Returns:
119133
A copy of the message with sensitive fields masked.
120134
"""
121-
copy = _copy_message(message)
135+
copy: Union[ProtobufMessage, ProtoPlusMessage] = _copy_message(message)
122136

123137
for row in copy.results:
124138
# Each row is an instance of GoogleAdsRow. The ListFields method
@@ -127,21 +141,21 @@ def _mask_google_ads_search_response(message, mask):
127141
# then we need to access the native proto to call ListFields. If it's
128142
# not proto_plus we can assume it's protobuf and can access ListFields
129143
# directly.
130-
if hasattr(row, "_pb"):
131-
row_fields = convert_proto_plus_to_protobuf(row).ListFields()
144+
if hasattr(row, "_pb"): # proto-plus message
145+
row_fields: List[Tuple(FieldDescriptor, Any)] = convert_proto_plus_to_protobuf(row).ListFields()
132146
else:
133-
row_fields = row.ListFields()
147+
row_fields: List[Tuple(FieldDescriptor, Any)] = row.ListFields()
134148
for field in row_fields:
135149
field_descriptor = field[0]
136150
# field_name is the name of the field on the GoogleAdsRow instance,
137151
# for example "campaign" or "customer_user_access"
138-
field_name = field_descriptor.name
152+
field_name: str = field_descriptor.name
139153
# message_name is the name of the message, similar to the class
140154
# name, for example "Campaign" or "CustomerUserAccess"
141-
message_name = field_descriptor.message_type.name
155+
message_name: str = field_descriptor.message_type.name
142156
if message_name in _MESSAGES_WITH_SENSITIVE_FIELDS.keys():
143-
nested_message = getattr(row, field_name)
144-
masked_message = _mask_message_fields(
157+
nested_message: Union[ProtobufMessage, ProtoPlusMessage] = getattr(row, field_name)
158+
masked_message: Union[ProtobufMessage, ProtoPlusMessage] = _mask_message_fields(
145159
_MESSAGES_WITH_SENSITIVE_FIELDS[message_name],
146160
nested_message,
147161
mask,
@@ -153,7 +167,7 @@ def _mask_google_ads_search_response(message, mask):
153167
return copy
154168

155169

156-
def mask_message(message, mask):
170+
def mask_message(message: Union[ProtobufMessage, ProtoPlusMessage], mask: str) -> Union[ProtobufMessage, ProtoPlusMessage]:
157171
"""Copies and returns a message with sensitive fields masked.
158172
159173
Args:
@@ -165,12 +179,12 @@ def mask_message(message, mask):
165179
Returns:
166180
A copy of the message instance with sensitive fields masked.
167181
"""
168-
class_name = message.__class__.__name__
182+
class_name: str = message.__class__.__name__
169183

170184
if class_name in _SEARCH_RESPONSE_MESSAGE_NAMES:
171185
return _mask_google_ads_search_response(message, mask)
172186
elif class_name in _MESSAGES_WITH_SENSITIVE_FIELDS.keys():
173-
sensitive_fields = _MESSAGES_WITH_SENSITIVE_FIELDS[class_name]
187+
sensitive_fields: List[str] = _MESSAGES_WITH_SENSITIVE_FIELDS[class_name]
174188
return _mask_message_fields(sensitive_fields, message, mask)
175189
else:
176190
return message

0 commit comments

Comments
 (0)