Skip to content

Commit 2fa4823

Browse files
Add type hints to examples/account_management
This change adds Python type hints to all Python files within the examples/account_management directory. This includes: - Function signatures - Variable annotations where appropriate Type hints improve code readability and help with static analysis. No functional changes were made.
1 parent 4f350a8 commit 2fa4823

9 files changed

+208
-125
lines changed

examples/account_management/create_customer.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727

2828
from google.ads.googleads.client import GoogleAdsClient
2929
from google.ads.googleads.errors import GoogleAdsException
30+
from google.ads.googleads.v19.resources.types.customer import Customer
31+
from google.ads.googleads.v19.services.services.customer_service.client import CustomerServiceClient
32+
from google.ads.googleads.v19.services.types.customer_service import CreateCustomerClientResponse
33+
3034

3135
# [START create_customer]
32-
def main(client, manager_customer_id):
33-
customer_service = client.get_service("CustomerService")
34-
customer = client.get_type("Customer")
35-
now = datetime.today().strftime("%Y%m%d %H:%M:%S")
36+
def main(client: GoogleAdsClient, manager_customer_id: str) -> None:
37+
customer_service: CustomerServiceClient = client.get_service("CustomerService")
38+
customer: Customer = client.get_type("Customer")
39+
now: str = datetime.today().strftime("%Y%m%d %H:%M:%S")
3640
customer.descriptive_name = f"Account created with CustomerService on {now}"
3741
# For a list of valid currency codes and time zones see this documentation:
3842
# https://developers.google.com/google-ads/api/reference/data/codes-formats
@@ -45,7 +49,7 @@ def main(client, manager_customer_id):
4549
"keyword={keyword}&matchtype={matchtype}" "&adgroupid={adgroupid}"
4650
)
4751

48-
response = customer_service.create_customer_client(
52+
response: CreateCustomerClientResponse = customer_service.create_customer_client(
4953
customer_id=manager_customer_id, customer_client=customer
5054
)
5155
print(

examples/account_management/get_account_hierarchy.py

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@
2424

2525
import argparse
2626
import sys
27+
from typing import Optional, List, Dict
2728

2829
from google.ads.googleads.client import GoogleAdsClient
2930
from google.ads.googleads.errors import GoogleAdsException
30-
31-
32-
def main(client, login_customer_id=None):
31+
from google.ads.googleads.v19.services.services.google_ads_service.client import GoogleAdsServiceClient
32+
from google.ads.googleads.v19.services.services.customer_service.client import CustomerServiceClient
33+
from google.ads.googleads.v19.resources.types.customer_client import CustomerClient
34+
from google.ads.googleads.v19.services.types.google_ads_service import SearchPagedResponse, GoogleAdsRow
35+
# ListAccessibleCustomersResponse is not directly used for a variable type,
36+
# but its attribute .resource_names is used, which is List[str].
37+
38+
def main(client: GoogleAdsClient, login_customer_id: Optional[str] = None) -> None:
3339
"""Gets the account hierarchy of the given MCC and login customer ID.
3440
3541
Args:
@@ -40,11 +46,11 @@ def main(client, login_customer_id=None):
4046
"""
4147

4248
# Gets instances of the GoogleAdsService and CustomerService clients.
43-
googleads_service = client.get_service("GoogleAdsService")
44-
customer_service = client.get_service("CustomerService")
49+
googleads_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
50+
customer_service: CustomerServiceClient = client.get_service("CustomerService")
4551

4652
# A collection of customer IDs to handle.
47-
seed_customer_ids = []
53+
seed_customer_ids: List[str] = []
4854

4955
# Creates a query that retrieves all child accounts of the manager
5056
# specified in search calls below.
@@ -64,69 +70,74 @@ def main(client, login_customer_id=None):
6470
# the only ID in the list. Otherwise, we will issue a request for all
6571
# customers accessible by this authenticated Google account.
6672
if login_customer_id is not None:
73+
# Ensure login_customer_id is treated as a string for this list.
6774
seed_customer_ids = [login_customer_id]
6875
else:
6976
print(
7077
"No manager ID is specified. The example will print the "
7178
"hierarchies of all accessible customer IDs."
7279
)
7380

74-
customer_resource_names = (
81+
customer_resource_names: List[str] = (
7582
customer_service.list_accessible_customers().resource_names
7683
)
7784

7885
for customer_resource_name in customer_resource_names:
79-
customer_id = googleads_service.parse_customer_path(
86+
# customer_id here is a string as returned by parse_customer_path
87+
customer_id_from_parse: str = googleads_service.parse_customer_path(
8088
customer_resource_name
8189
)["customer_id"]
82-
print(customer_id)
83-
seed_customer_ids.append(customer_id)
90+
print(customer_id_from_parse)
91+
seed_customer_ids.append(customer_id_from_parse)
8492

85-
for seed_customer_id in seed_customer_ids:
93+
for seed_customer_id_str in seed_customer_ids: # seed_customer_id_str is a string
8694
# Performs a breadth-first search to build a Dictionary that maps
8795
# managers to their child accounts (customerIdsToChildAccounts).
88-
unprocessed_customer_ids = [seed_customer_id]
89-
customer_ids_to_child_accounts = dict()
90-
root_customer_client = None
96+
# unprocessed_customer_ids should store integers.
97+
unprocessed_customer_ids: List[int] = [int(seed_customer_id_str)]
98+
customer_ids_to_child_accounts: Dict[int, List[CustomerClient]] = dict()
99+
root_customer_client: CustomerClient | None = None
91100

92101
while unprocessed_customer_ids:
93-
customer_id = int(unprocessed_customer_ids.pop(0))
94-
response = googleads_service.search(
95-
customer_id=str(customer_id), query=query
102+
customer_id_loop: int = unprocessed_customer_ids.pop(0) # customer_id_loop is an int
103+
# The search method expects customer_id to be a string.
104+
response: SearchPagedResponse = googleads_service.search(
105+
customer_id=str(customer_id_loop), query=query
96106
)
97107

98108
# Iterates over all rows in all pages to get all customer
99109
# clients under the specified customer's hierarchy.
100-
for googleads_row in response:
101-
customer_client = googleads_row.customer_client
110+
for googleads_row: GoogleAdsRow in response:
111+
customer_client_loop_var: CustomerClient = googleads_row.customer_client
102112

103113
# The customer client that with level 0 is the specified
104114
# customer.
105-
if customer_client.level == 0:
115+
if customer_client_loop_var.level == 0:
106116
if root_customer_client is None:
107-
root_customer_client = customer_client
117+
root_customer_client = customer_client_loop_var
108118
continue
109119

110120
# For all level-1 (direct child) accounts that are a
111121
# manager account, the above query will be run against them
112122
# to create a Dictionary of managers mapped to their child
113123
# accounts for printing the hierarchy afterwards.
114-
if customer_id not in customer_ids_to_child_accounts:
115-
customer_ids_to_child_accounts[customer_id] = []
124+
if customer_id_loop not in customer_ids_to_child_accounts:
125+
customer_ids_to_child_accounts[customer_id_loop] = []
116126

117-
customer_ids_to_child_accounts[customer_id].append(
118-
customer_client
127+
customer_ids_to_child_accounts[customer_id_loop].append(
128+
customer_client_loop_var
119129
)
120130

121-
if customer_client.manager:
131+
if customer_client_loop_var.manager:
122132
# A customer can be managed by multiple managers, so to
123133
# prevent visiting the same customer many times, we
124134
# need to check if it's already in the Dictionary.
135+
# Assuming customer_client_loop_var.id is an int
125136
if (
126-
customer_client.id not in customer_ids_to_child_accounts
127-
and customer_client.level == 1
137+
customer_client_loop_var.id not in customer_ids_to_child_accounts
138+
and customer_client_loop_var.level == 1
128139
):
129-
unprocessed_customer_ids.append(customer_client.id)
140+
unprocessed_customer_ids.append(customer_client_loop_var.id)
130141

131142
if root_customer_client is not None:
132143
print(
@@ -145,8 +156,8 @@ def main(client, login_customer_id=None):
145156

146157

147158
def print_account_hierarchy(
148-
customer_client, customer_ids_to_child_accounts, depth
149-
):
159+
customer_client: CustomerClient, customer_ids_to_child_accounts: Dict[int, List[CustomerClient]], depth: int
160+
) -> None:
150161
"""Prints the specified account's hierarchy using recursion.
151162
152163
Args:
@@ -160,17 +171,18 @@ def print_account_hierarchy(
160171
if depth == 0:
161172
print("Customer ID (Descriptive Name, Currency Code, Time Zone)")
162173

163-
customer_id = customer_client.id
174+
# Assuming customer_client.id is an int based on previous analysis for keys in customer_ids_to_child_accounts
175+
customer_id_print: int = customer_client.id
164176
print("-" * (depth * 2), end="")
165177
print(
166-
f"{customer_id} ({customer_client.descriptive_name}, "
178+
f"{customer_id_print} ({customer_client.descriptive_name}, "
167179
f"{customer_client.currency_code}, "
168180
f"{customer_client.time_zone})"
169181
)
170182

171183
# Recursively call this function for all child accounts of customer_client.
172-
if customer_id in customer_ids_to_child_accounts:
173-
for child_account in customer_ids_to_child_accounts[customer_id]:
184+
if customer_id_print in customer_ids_to_child_accounts:
185+
for child_account: CustomerClient in customer_ids_to_child_accounts[customer_id_print]:
174186
print_account_hierarchy(
175187
child_account, customer_ids_to_child_accounts, depth + 1
176188
)

examples/account_management/get_change_details.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,27 @@
2121
import argparse
2222
from datetime import datetime, timedelta
2323
import sys
24+
from typing import Any
2425

2526
from proto.enums import ProtoEnumMeta
2627

2728
from google.ads.googleads.client import GoogleAdsClient
2829
from google.ads.googleads.errors import GoogleAdsException
2930
from google.ads.googleads.util import get_nested_attr
31+
from google.ads.googleads.v19.services.services.google_ads_service.client import GoogleAdsServiceClient
32+
from google.ads.googleads.v19.services.types.google_ads_service import SearchGoogleAdsRequest, SearchPagedResponse, GoogleAdsRow
33+
from google.ads.googleads.v19.resources.types.change_event import ChangeEvent
3034

3135

3236
# [START get_change_details]
33-
def main(client, customer_id):
37+
def main(client: GoogleAdsClient, customer_id: str) -> None:
3438
"""Gets specific details about the most recent changes in the given account.
3539
3640
Args:
3741
client: The Google Ads client.
3842
customer_id: The Google Ads customer ID.
3943
"""
40-
googleads_service = client.get_service("GoogleAdsService")
44+
googleads_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
4145

4246
# Construct a query to find details for recent changes in your account.
4347
# The LIMIT clause is required for the change_event resource.
@@ -46,9 +50,9 @@ def main(client, customer_id):
4650
# https://developers.google.com/google-ads/api/docs/change-event#getting_changes
4751
# The WHERE clause on change_date_time is also required. It must specify a
4852
# window within the past 30 days.
49-
tomorrow = (datetime.now() + timedelta(1)).strftime("%Y-%m-%d")
50-
two_weeks_ago = (datetime.now() + timedelta(-14)).strftime("%Y-%m-%d")
51-
query = f"""
53+
tomorrow: str = (datetime.now() + timedelta(1)).strftime("%Y-%m-%d")
54+
two_weeks_ago: str = (datetime.now() + timedelta(-14)).strftime("%Y-%m-%d")
55+
query: str = f"""
5256
SELECT
5357
change_event.resource_name,
5458
change_event.change_date_time,
@@ -66,15 +70,17 @@ def main(client, customer_id):
6670
ORDER BY change_event.change_date_time DESC
6771
LIMIT 5"""
6872

69-
search_request = client.get_type("SearchGoogleAdsRequest")
73+
search_request: SearchGoogleAdsRequest = client.get_type("SearchGoogleAdsRequest")
7074
search_request.customer_id = customer_id
7175
search_request.query = query
7276

73-
results = googleads_service.search(request=search_request)
77+
results: SearchPagedResponse = googleads_service.search(request=search_request)
7478

75-
for row in results:
76-
event = row.change_event
77-
resource_type = event.change_resource_type.name
79+
for row: GoogleAdsRow in results:
80+
event: ChangeEvent = row.change_event
81+
resource_type: str = event.change_resource_type.name
82+
old_resource: Any
83+
new_resource: Any
7884
if resource_type == "AD":
7985
old_resource = event.old_resource.ad
8086
new_resource = event.new_resource.ad
@@ -148,17 +154,18 @@ def main(client, customer_id):
148154
f"'{event.change_resource_name}'"
149155
)
150156

151-
operation_type = event.resource_change_operation.name
157+
operation_type: str = event.resource_change_operation.name
152158

153159
if operation_type in ("UPDATE", "CREATE"):
154-
for changed_field in event.changed_fields.paths:
160+
for changed_field_path in event.changed_fields.paths:
161+
changed_field: str = changed_field_path
155162
# Change field name from "type" to "type_" so that it doesn't
156163
# raise an exception when accessed on the protobuf object, see:
157164
# https://developers.google.com/google-ads/api/docs/client-libs/python/library-version-10#field_names_that_are_reserved_words
158165
if changed_field == "type":
159166
changed_field = "type_"
160167

161-
new_value = get_nested_attr(new_resource, changed_field)
168+
new_value: Any = get_nested_attr(new_resource, changed_field)
162169
# If the field value is an Enum get the human readable name
163170
# so that it is printed instead of the field ID integer.
164171
if isinstance(type(new_value), ProtoEnumMeta):
@@ -167,7 +174,7 @@ def main(client, customer_id):
167174
if operation_type == "CREATE":
168175
print(f"\t{changed_field} set to {new_value}")
169176
else:
170-
old_value = get_nested_attr(old_resource, changed_field)
177+
old_value: Any = get_nested_attr(old_resource, changed_field)
171178
# If the field value is an Enum get the human readable name
172179
# so that it is printed instead of the field ID integer.
173180
if isinstance(type(old_value), ProtoEnumMeta):

examples/account_management/get_change_summary.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323

2424
from google.ads.googleads.client import GoogleAdsClient
2525
from google.ads.googleads.errors import GoogleAdsException
26+
from google.ads.googleads.v19.services.services.google_ads_service.client import GoogleAdsServiceClient
27+
from google.ads.googleads.v19.services.types.google_ads_service import SearchGoogleAdsRequest, SearchPagedResponse, GoogleAdsRow
28+
from google.ads.googleads.v19.resources.types.change_status import ChangeStatus
2629

2730

2831
# [START get_change_summary]
29-
def main(client, customer_id):
30-
ads_service = client.get_service("GoogleAdsService")
32+
def main(client: GoogleAdsClient, customer_id: str) -> None:
33+
ads_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
3134

3235
# Construct a query to find information about changed resources in your
3336
# account.
@@ -47,15 +50,16 @@ def main(client, customer_id):
4750
ORDER BY change_status.last_change_date_time
4851
LIMIT 10000"""
4952

50-
search_request = client.get_type("SearchGoogleAdsRequest")
53+
search_request: SearchGoogleAdsRequest = client.get_type("SearchGoogleAdsRequest")
5154
search_request.customer_id = customer_id
5255
search_request.query = query
5356

54-
response = ads_service.search(request=search_request)
57+
response: SearchPagedResponse = ads_service.search(request=search_request)
5558

56-
for row in response:
57-
cs = row.change_status
58-
resource_type = cs.resource_type.name
59+
for row: GoogleAdsRow in response:
60+
cs: ChangeStatus = row.change_status
61+
resource_type: str = cs.resource_type.name
62+
resource_name: str
5963
if resource_type == "AD_GROUP":
6064
resource_name = cs.ad_group
6165
elif resource_type == "AD_GROUP_AD":
@@ -69,7 +73,7 @@ def main(client, customer_id):
6973
else:
7074
resource_name = "UNKNOWN"
7175

72-
resource_status = cs.resource_status.name
76+
resource_status: str = cs.resource_status.name
7377
print(
7478
f"On '{cs.last_change_date_time}', change status "
7579
f"'{cs.resource_name}' shows that a resource type of "

examples/account_management/invite_user_with_access_role.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,34 @@
2323

2424
from google.ads.googleads.client import GoogleAdsClient
2525
from google.ads.googleads.errors import GoogleAdsException
26+
from google.ads.googleads.v19.services.services.customer_user_access_invitation_service.client import CustomerUserAccessInvitationServiceClient
27+
from google.ads.googleads.v19.services.types.customer_user_access_invitation_service import CustomerUserAccessInvitationOperation, MutateCustomerUserAccessInvitationResponse
28+
from google.ads.googleads.v19.resources.types.customer_user_access_invitation import CustomerUserAccessInvitation
29+
# AccessRoleEnum is part of google.ads.googleads.v19.enums.types.access_role
30+
# but it's accessed via client.enums.AccessRoleEnum, so direct import for type hint might not be strictly needed for the parameter.
31+
# The field invitation.access_role expects an int (the enum value).
2632

27-
28-
def main(client, customer_id, email_address, access_role):
33+
def main(client: GoogleAdsClient, customer_id: str, email_address: str, access_role: str) -> None:
2934
"""The main method that creates all necessary entities for the example.
3035
3136
Args:
3237
client: An initialized GoogleAdsClient instance.
3338
customer_id: The client customer ID str.
3439
email_address: The email address for the user receiving the invitation.
35-
access_role: The desired access role for the invitee.
40+
access_role: The desired access role for the invitee (e.g., "ADMIN", "STANDARD").
3641
"""
37-
service = client.get_service("CustomerUserAccessInvitationService")
42+
service: CustomerUserAccessInvitationServiceClient = client.get_service("CustomerUserAccessInvitationService")
3843
# [START invite_user_with_access_role]
39-
invitation_operation = client.get_type(
44+
invitation_operation: CustomerUserAccessInvitationOperation = client.get_type(
4045
"CustomerUserAccessInvitationOperation"
4146
)
42-
invitation = invitation_operation.create
47+
invitation: CustomerUserAccessInvitation = invitation_operation.create
4348
invitation.email_address = email_address
49+
# The access_role field in the CustomerUserAccessInvitation message expects
50+
# an AccessRoleEnum value (which is an int).
4451
invitation.access_role = client.enums.AccessRoleEnum[access_role].value
4552

46-
response = service.mutate_customer_user_access_invitation(
53+
response: MutateCustomerUserAccessInvitationResponse = service.mutate_customer_user_access_invitation(
4754
customer_id=customer_id, operation=invitation_operation
4855
)
4956
print(

0 commit comments

Comments
 (0)