Skip to content

Commit 1f44f0d

Browse files
google-labs-jules[bot]BenRKarl
authored andcommitted
Here's how I've added type annotations to your google/ads/googleads files:
This change introduces Python type hints and annotations to the following files: - google/ads/googleads/client.py - google/ads/googleads/config.py - google/ads/googleads/errors.py - google/ads/googleads/oauth2.py - google/ads/googleads/util.py Type hints improve code readability and help with static analysis. I used `typing.Any` and `# type: ignore` where necessary to handle dynamic typing or external library interactions. No changes were needed for `google/ads/googleads/__init__.py` as it only contains imports and a version string.
1 parent c7535c7 commit 1f44f0d

File tree

5 files changed

+216
-140
lines changed

5 files changed

+216
-140
lines changed

google/ads/googleads/client.py

Lines changed: 78 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,18 @@ class _EnumGetter:
6060
class instances when accessed.
6161
"""
6262

63-
def __init__(self, client):
63+
def __init__(self, client: "GoogleAdsClient"):
6464
"""Initializer for the _EnumGetter class.
6565
6666
Args:
67-
version: a str indicating the version of the Google Ads API to be
68-
used.
67+
client: An instance of the GoogleAdsClient class.
6968
"""
70-
self._client = client
71-
self._version = client.version or _DEFAULT_VERSION
72-
self._enums = None
73-
self._use_proto_plus = client.use_proto_plus
69+
self._client: "GoogleAdsClient" = client
70+
self._version: str = client.version or _DEFAULT_VERSION
71+
self._enums: list[str] | None = None
72+
self._use_proto_plus: bool = client.use_proto_plus
7473

75-
def __dir__(self):
74+
def __dir__(self) -> list[str]:
7675
"""Overrides behavior when dir() is called on instances of this class.
7776
7877
It's useful to use dir() to see a list of available attributes. Since
@@ -86,7 +85,7 @@ def __dir__(self):
8685

8786
return self._enums
8887

89-
def __getattr__(self, name):
88+
def __getattr__(self, name: str):
9089
"""Dynamically loads the given enum class instance.
9190
9291
Args:
@@ -95,7 +94,7 @@ def __getattr__(self, name):
9594
Returns:
9695
An instance of the enum proto message class.
9796
"""
98-
if not name in self.__dir__():
97+
if name not in self.__dir__():
9998
raise AttributeError(
10099
f"'{type(self).__name__}' object has no attribute '{name}'"
101100
)
@@ -143,7 +142,7 @@ class GoogleAdsClient:
143142
"""Google Ads client used to configure settings and fetch services."""
144143

145144
@classmethod
146-
def copy_from(cls, destination, origin):
145+
def copy_from(cls, destination, origin): # type: ignore[no-untyped-def]
147146
"""Copies protobuf and proto-plus messages into one-another.
148147
149148
This method consolidates the CopyFrom logic of protobuf and proto-plus
@@ -157,7 +156,7 @@ def copy_from(cls, destination, origin):
157156
return util.proto_copy_from(destination, origin)
158157

159158
@classmethod
160-
def _get_client_kwargs(cls, config_data):
159+
def _get_client_kwargs(cls, config_data: dict) -> dict:
161160
"""Converts configuration dict into kwargs required by the client.
162161
163162
Args:
@@ -171,7 +170,7 @@ def _get_client_kwargs(cls, config_data):
171170
ValueError: If the configuration lacks a required field.
172171
"""
173172
return {
174-
"credentials": oauth2.get_credentials(config_data),
173+
"credentials": oauth2.get_credentials(config_data), # type: ignore[no-untyped-call]
175174
"developer_token": config_data.get("developer_token"),
176175
"endpoint": config_data.get("endpoint"),
177176
"login_customer_id": config_data.get("login_customer_id"),
@@ -185,7 +184,7 @@ def _get_client_kwargs(cls, config_data):
185184
}
186185

187186
@classmethod
188-
def _get_api_services_by_version(cls, version):
187+
def _get_api_services_by_version(cls, version: str): # type: ignore[no-untyped-def]
189188
"""Returns a module with all services and types for a given API version.
190189
191190
Args:
@@ -207,7 +206,9 @@ def _get_api_services_by_version(cls, version):
207206
return version_module
208207

209208
@classmethod
210-
def load_from_env(cls, version=None):
209+
def load_from_env(
210+
cls, version: str | None = None
211+
) -> "GoogleAdsClient":
211212
"""Creates a GoogleAdsClient with data stored in the env variables.
212213
213214
Args:
@@ -220,12 +221,14 @@ def load_from_env(cls, version=None):
220221
Raises:
221222
ValueError: If the configuration lacks a required field.
222223
"""
223-
config_data = config.load_from_env()
224-
kwargs = cls._get_client_kwargs(config_data)
224+
config_data: dict = config.load_from_env() # type: ignore[no-untyped-call]
225+
kwargs: dict = cls._get_client_kwargs(config_data)
225226
return cls(**dict(version=version, **kwargs))
226227

227228
@classmethod
228-
def load_from_string(cls, yaml_str, version=None):
229+
def load_from_string(
230+
cls, yaml_str: str, version: str | None = None
231+
) -> "GoogleAdsClient":
229232
"""Creates a GoogleAdsClient with data stored in the YAML string.
230233
231234
Args:
@@ -240,12 +243,14 @@ def load_from_string(cls, yaml_str, version=None):
240243
Raises:
241244
ValueError: If the configuration lacks a required field.
242245
"""
243-
config_data = config.parse_yaml_document_to_dict(yaml_str)
244-
kwargs = cls._get_client_kwargs(config_data)
246+
config_data: dict = config.parse_yaml_document_to_dict(yaml_str) # type: ignore[no-untyped-call]
247+
kwargs: dict = cls._get_client_kwargs(config_data)
245248
return cls(**dict(version=version, **kwargs))
246249

247250
@classmethod
248-
def load_from_dict(cls, config_dict, version=None):
251+
def load_from_dict(
252+
cls, config_dict: dict, version: str | None = None
253+
) -> "GoogleAdsClient":
249254
"""Creates a GoogleAdsClient with data stored in the config_dict.
250255
251256
Args:
@@ -260,12 +265,14 @@ def load_from_dict(cls, config_dict, version=None):
260265
Raises:
261266
ValueError: If the configuration lacks a required field.
262267
"""
263-
config_data = config.load_from_dict(config_dict)
264-
kwargs = cls._get_client_kwargs(config_data)
268+
config_data: dict = config.load_from_dict(config_dict) # type: ignore[no-untyped-call]
269+
kwargs: dict = cls._get_client_kwargs(config_data)
265270
return cls(**dict(version=version, **kwargs))
266271

267272
@classmethod
268-
def load_from_storage(cls, path=None, version=None):
273+
def load_from_storage(
274+
cls, path: str | None = None, version: str | None = None
275+
) -> "GoogleAdsClient":
269276
"""Creates a GoogleAdsClient with data stored in the specified file.
270277
271278
Args:
@@ -282,22 +289,22 @@ def load_from_storage(cls, path=None, version=None):
282289
IOError: If the configuration file can't be loaded.
283290
ValueError: If the configuration file lacks a required field.
284291
"""
285-
config_data = config.load_from_yaml_file(path)
286-
kwargs = cls._get_client_kwargs(config_data)
292+
config_data: dict = config.load_from_yaml_file(path) # type: ignore[no-untyped-call]
293+
kwargs: dict = cls._get_client_kwargs(config_data)
287294
return cls(**dict(version=version, **kwargs))
288295

289296
def __init__(
290297
self,
291-
credentials,
292-
developer_token,
293-
endpoint=None,
294-
login_customer_id=None,
295-
logging_config=None,
296-
linked_customer_id=None,
297-
version=None,
298-
http_proxy=None,
299-
use_proto_plus=False,
300-
use_cloud_org_for_api_access=None,
298+
credentials, # type: ignore[no-untyped-def]
299+
developer_token: str,
300+
endpoint: str | None = None,
301+
login_customer_id: str | None = None,
302+
logging_config: dict | None = None,
303+
linked_customer_id: str | None = None,
304+
version: str | None = None,
305+
http_proxy: str | None = None,
306+
use_proto_plus: bool = False,
307+
use_cloud_org_for_api_access: str | None = None,
301308
):
302309
"""Initializer for the GoogleAdsClient.
303310
@@ -322,21 +329,28 @@ def __init__(
322329
logging.config.dictConfig(logging_config)
323330

324331
self.credentials = credentials
325-
self.developer_token = developer_token
326-
self.endpoint = endpoint
327-
self.login_customer_id = login_customer_id
328-
self.linked_customer_id = linked_customer_id
329-
self.version = version
330-
self.http_proxy = http_proxy
331-
self.use_proto_plus = use_proto_plus
332-
self.use_cloud_org_for_api_access = use_cloud_org_for_api_access
333-
self.enums = _EnumGetter(self)
332+
self.developer_token: str = developer_token
333+
self.endpoint: str | None = endpoint
334+
self.login_customer_id: str | None = login_customer_id
335+
self.linked_customer_id: str | None = linked_customer_id
336+
self.version: str | None = version
337+
self.http_proxy: str | None = http_proxy
338+
self.use_proto_plus: bool = use_proto_plus
339+
self.use_cloud_org_for_api_access: str | None = (
340+
use_cloud_org_for_api_access
341+
)
342+
self.enums: _EnumGetter = _EnumGetter(self)
334343

335344
# If given, write the http_proxy channel option for GRPC to use
336345
if http_proxy:
337346
_GRPC_CHANNEL_OPTIONS.append(("grpc.http_proxy", http_proxy))
338347

339-
def get_service(self, name, version=_DEFAULT_VERSION, interceptors=None):
348+
def get_service( # type: ignore[no-untyped-def]
349+
self,
350+
name: str,
351+
version: str = _DEFAULT_VERSION,
352+
interceptors: list | None = None,
353+
):
340354
"""Returns a service client instance for the specified service_name.
341355
342356
Args:
@@ -359,13 +373,15 @@ def get_service(self, name, version=_DEFAULT_VERSION, interceptors=None):
359373
# override any version specified as an argument.
360374
version = self.version if self.version else version
361375
# api_module = self._get_api_services_by_version(version)
362-
services_path = f"google.ads.googleads.{version}.services.services"
363-
snaked = util.convert_upper_case_to_snake_case(name)
376+
services_path: str = (
377+
f"google.ads.googleads.{version}.services.services"
378+
)
379+
snaked: str = util.convert_upper_case_to_snake_case(name) # type: ignore[no-untyped-call]
364380
interceptors = interceptors or []
365381

366382
try:
367383
service_module = import_module(f"{services_path}.{snaked}")
368-
service_client_class = util.get_nested_attr(
384+
service_client_class = util.get_nested_attr( # type: ignore[no-untyped-call]
369385
service_module, _SERVICE_CLIENT_TEMPLATE.format(name)
370386
)
371387
except (AttributeError, ModuleNotFoundError):
@@ -389,25 +405,27 @@ def get_service(self, name, version=_DEFAULT_VERSION, interceptors=None):
389405
)
390406

391407
interceptors = interceptors + [
392-
MetadataInterceptor(
408+
MetadataInterceptor( # type: ignore[no-untyped-call]
393409
self.developer_token,
394410
self.login_customer_id,
395411
self.linked_customer_id,
396412
self.use_cloud_org_for_api_access,
397413
),
398-
LoggingInterceptor(_logger, version, endpoint),
399-
ExceptionInterceptor(version, use_proto_plus=self.use_proto_plus),
414+
LoggingInterceptor(_logger, version, endpoint), # type: ignore[no-untyped-call]
415+
ExceptionInterceptor( # type: ignore[no-untyped-call]
416+
version, use_proto_plus=self.use_proto_plus
417+
),
400418
]
401419

402-
channel = grpc.intercept_channel(channel, *interceptors)
420+
channel = grpc.intercept_channel(channel, *interceptors) # type: ignore[no-untyped-call]
403421

404422
service_transport = service_transport_class(
405423
channel=channel, client_info=_CLIENT_INFO
406424
)
407425

408426
return service_client_class(transport=service_transport)
409427

410-
def get_type(self, name, version=_DEFAULT_VERSION):
428+
def get_type(self, name: str, version: str = _DEFAULT_VERSION): # type: ignore[no-untyped-def]
411429
"""Returns the specified common, enum, error, or resource type.
412430
413431
Args:
@@ -444,19 +462,19 @@ def get_type(self, name, version=_DEFAULT_VERSION):
444462
version = self.version if self.version else version
445463
type_classes = self._get_api_services_by_version(version)
446464

447-
for type in _MESSAGE_TYPES:
448-
if type == "services":
449-
path = f"{type}.types.{name}"
465+
for type_name in _MESSAGE_TYPES:
466+
if type_name == "services":
467+
path = f"{type_name}.types.{name}"
450468
else:
451-
path = f"{type}.{name}"
469+
path = f"{type_name}.{name}"
452470

453471
try:
454-
message_class = util.get_nested_attr(type_classes, path)
472+
message_class = util.get_nested_attr(type_classes, path) # type: ignore[no-untyped-call]
455473

456474
if self.use_proto_plus == True:
457475
return message_class()
458476
else:
459-
return util.convert_proto_plus_to_protobuf(message_class())
477+
return util.convert_proto_plus_to_protobuf(message_class()) # type: ignore[no-untyped-call]
460478
except AttributeError:
461479
pass
462480

0 commit comments

Comments
 (0)