Skip to content

Commit 8eef1d0

Browse files
authored
Changes for release v23_1. (#1056)
1 parent e8bcaaf commit 8eef1d0

1,826 files changed

Lines changed: 28482 additions & 10878 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ChangeLog

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
* 29.2.0
2+
- Google Ads API v23_1 release.
3+
- Update Google Ads API v20, v21, and v22 to include EU political advertising changes.
4+
- Add upload_video example.
5+
- Add text guidelines to performance max example.
6+
7+
* 29.1.0
8+
- Add configurable metadata header for ads api assistant.
9+
110
* 29.1.0
211
- Add configurable metadata header for ads api assistant.
312

examples/advanced_operations/add_performance_max_campaign.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
shopping_ads/add_performance_max_retail_campaign.py
2828
"""
2929

30-
3130
import argparse
3231
from datetime import datetime, timedelta
3332
import sys
@@ -83,7 +82,6 @@
8382
MutateOperationResponse,
8483
)
8584

86-
8785
# We specify temporary IDs that are specific to a single mutate request.
8886
# Temporary IDs are always negative and unique within one mutate request.
8987
#
@@ -318,8 +316,24 @@ def create_performance_max_campaign_operation(
318316
)
319317

320318
# Optional fields
321-
campaign.start_date_time = (datetime.now() + timedelta(1)).strftime("%Y%m%d 00:00:00")
322-
campaign.end_date_time = (datetime.now() + timedelta(365)).strftime("%Y%m%d 23:59:59")
319+
campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(
320+
"%Y%m%d 00:00:00"
321+
)
322+
campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(
323+
"%Y%m%d 23:59:59"
324+
)
325+
326+
# [START add_performance_max_text_guidelines]
327+
campaign.text_guidelines.term_exclusions = ["cheap", "free"]
328+
messaging_restriction = campaign.MessagingRestriction()
329+
messaging_restriction.restriction_text = "Don't mention competitor names"
330+
messaging_restriction.restriction_type = (
331+
client.enums.MessagingRestrictionTypeEnum.RESTRICTION_BASED_EXCLUSION
332+
)
333+
campaign.text_guidelines.messaging_restrictions.append(
334+
messaging_restriction
335+
)
336+
# [END add_performance_max_text_guidelines]
323337

324338
# [START add_pmax_asset_automation_settings]
325339
# Configures the optional opt-in/out status for asset automation settings.
@@ -328,11 +342,17 @@ def create_performance_max_campaign_operation(
328342
client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,
329343
client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,
330344
client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,
331-
client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT
345+
client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,
332346
]:
333-
asset_automattion_setting: Campaign.AssetAutomationSetting = client.get_type("Campaign").AssetAutomationSetting()
334-
asset_automattion_setting.asset_automation_type = asset_automation_type_enum
335-
asset_automattion_setting.asset_automation_status = client.enums.AssetAutomationStatusEnum.OPTED_IN
347+
asset_automattion_setting: Campaign.AssetAutomationSetting = (
348+
client.get_type("Campaign").AssetAutomationSetting()
349+
)
350+
asset_automattion_setting.asset_automation_type = (
351+
asset_automation_type_enum
352+
)
353+
asset_automattion_setting.asset_automation_status = (
354+
client.enums.AssetAutomationStatusEnum.OPTED_IN
355+
)
336356
campaign.asset_automation_settings.append(asset_automattion_setting)
337357
# [END add_pmax_asset_automation_settings]
338358

@@ -712,6 +732,7 @@ def create_and_link_image_asset(
712732
return operations
713733
# [END add_performance_max_campaign_8]
714734

735+
715736
# [START create_and_link_brand_assets]
716737
def create_and_link_brand_assets(
717738
client: GoogleAdsClient,
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env python
2+
# Copyright 2026s Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
"""This example illustrates how to upload videos to YouTube."""
16+
17+
import argparse
18+
import itertools
19+
import os
20+
import sys
21+
import logging
22+
from typing import Iterator, Iterable, List, MutableSequence
23+
24+
import google.auth
25+
from google.auth.credentials import Credentials
26+
from google.auth import impersonated_credentials
27+
28+
from google.ads.googleads.client import GoogleAdsClient
29+
from google.ads.googleads.errors import GoogleAdsException
30+
from google.ads.googleads.v23.services.services.google_ads_service import (
31+
GoogleAdsServiceClient,
32+
)
33+
from google.ads.googleads.v23.services.types.google_ads_service import (
34+
SearchGoogleAdsStreamResponse,
35+
GoogleAdsRow,
36+
)
37+
38+
from google.ads.googleads.v23.services.services.you_tube_video_upload_service.client import (
39+
YouTubeVideoUploadServiceClient,
40+
)
41+
from google.ads.googleads.v23.services.types import youtube_video_upload_service
42+
from google.ads.googleads.v23.services.types.youtube_video_upload_service import (
43+
CreateYouTubeVideoUploadRequest,
44+
CreateYouTubeVideoUploadResponse,
45+
UpdateYouTubeVideoUploadRequest,
46+
UpdateYouTubeVideoUploadResponse,
47+
RemoveYouTubeVideoUploadRequest,
48+
RemoveYouTubeVideoUploadResponse,
49+
)
50+
51+
from google.protobuf import field_mask_pb2
52+
from google.ads.googleads.v23.resources.types import youtube_video_upload
53+
54+
55+
def main(client: GoogleAdsClient, customer_id: str, video_file_path: str) -> None:
56+
"""The main method that uploads a video and retrieves its state.
57+
58+
Args:
59+
client: an initialized GoogleAdsClient instance.
60+
customer_id: a client customer ID.
61+
video_file_path: the absolute path to a video file on your machine.
62+
"""
63+
64+
# [START upload_video_1]
65+
yt_service: YouTubeVideoUploadServiceClient = client.get_service(
66+
"YouTubeVideoUploadService"
67+
)
68+
69+
create_upload_request: CreateYouTubeVideoUploadRequest = (
70+
youtube_video_upload_service.CreateYouTubeVideoUploadRequest()
71+
)
72+
create_upload_request.customer_id = customer_id
73+
create_upload_request.you_tube_video_upload.video_title = "Test Video"
74+
create_upload_request.you_tube_video_upload.video_description = (
75+
"Test Video Description"
76+
)
77+
create_upload_request.you_tube_video_upload.video_privacy = (
78+
client.enums.YouTubeVideoPrivacyEnum.UNLISTED
79+
)
80+
81+
video_upload_resource_name: str
82+
with open(video_file_path, "rb") as stream:
83+
response: CreateYouTubeVideoUploadResponse = (
84+
yt_service.create_you_tube_video_upload(
85+
stream=stream,
86+
request=create_upload_request,
87+
retry=None,
88+
)
89+
)
90+
print(f"Created YouTube video upload: {response.resource_name}")
91+
# [END upload_video_1]
92+
93+
# [START upload_video_3]
94+
# Retrieve the metadata of the newly uploaded video.
95+
query: str = f"""
96+
SELECT
97+
you_tube_video_upload.resource_name,
98+
you_tube_video_upload.video_id,
99+
you_tube_video_upload.state
100+
FROM you_tube_video_upload
101+
WHERE you_tube_video_upload.resource_name = '{video_upload_resource_name}'"""
102+
103+
ga_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
104+
stream: Iterator[SearchGoogleAdsStreamResponse] = ga_service.search_stream(
105+
customer_id=customer_id, query=query
106+
)
107+
108+
for row in itertools.chain.from_iterable(batch.results for batch in stream):
109+
video = row.you_tube_video_upload
110+
print(
111+
f"Video with ID {row.you_tube_video_upload.video_id} was found in state {row.you_tube_video_upload.state}."
112+
)
113+
# [END upload_video_3]
114+
115+
116+
if __name__ == "__main__":
117+
parser = argparse.ArgumentParser(
118+
description="Lists all campaigns for specified customer."
119+
)
120+
# The following argument(s) should be provided to run the example.
121+
parser.add_argument(
122+
"-c",
123+
"--customer_id",
124+
type=str,
125+
required=True,
126+
help="The Google Ads customer ID.",
127+
)
128+
parser.add_argument(
129+
"-v",
130+
"--video_file_path",
131+
type=str,
132+
required=True,
133+
help="The path to a video file to upload to YouTube.",
134+
)
135+
args: argparse.Namespace = parser.parse_args()
136+
137+
# GoogleAdsClient will read the google-ads.yaml configuration file in the
138+
# home directory if none is specified.
139+
googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(version="v23")
140+
try:
141+
main(
142+
googleads_client,
143+
customer_id=args.customer_id,
144+
video_file_path=args.video_file_path,
145+
)
146+
except GoogleAdsException as ex:
147+
print(
148+
f'Request with ID "{ex.request_id}" failed with status '
149+
f'"{ex.error.code().name}" and includes the following errors:'
150+
)
151+
if hasattr(ex.error, "_response"):
152+
raw_res = getattr(ex.error, "_response")
153+
print(f"\n--- Full Response (Reflected from Proxy) ---")
154+
print(f"Status Code: {raw_res.status_code}")
155+
print(f"Headers: {raw_res.headers}")
156+
print(f"Body: {raw_res.text}")
157+
print(f"-------------------------------------------\n")
158+
159+
for error in ex.failure.errors:
160+
print(f'\tError with message "{error.message}".')
161+
if hasattr(error, "location") and error.location:
162+
for field_path_element in error.location.field_path_elements:
163+
print(f"\t\tOn field: {field_path_element.field_name}")
164+
sys.exit(1)

google/ads/googleads/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import google.ads.googleads.errors
2020
import google.ads.googleads.util
2121

22-
VERSION = "29.1.0"
22+
VERSION = "29.2.0"
2323

2424
# Checks if the current runtime is Python 3.9.
2525
if sys.version_info.major == 3 and sys.version_info.minor <= 9:

google/ads/googleads/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,18 @@ def get_service(
476476
channel=channel, client_info=_CLIENT_INFO
477477
)
478478

479+
if name == "YouTubeVideoUploadService":
480+
# YouTubeVideoUploadService uses REST, so we cannot pass the credentials inside the gRPC
481+
# channel; we need to pass them explicitly.
482+
return service_client_class(
483+
transport=service_transport,
484+
credentials=self.credentials,
485+
developer_token=self.developer_token,
486+
login_customer_id=self.login_customer_id,
487+
linked_customer_id=self.linked_customer_id,
488+
use_cloud_org_for_api_access=self.use_cloud_org_for_api_access
489+
)
490+
479491
return service_client_class(transport=service_transport)
480492

481493
channel: grpc.Channel = service_transport_class.create_channel(
@@ -504,6 +516,18 @@ def get_service(
504516
channel=channel, client_info=_CLIENT_INFO
505517
)
506518

519+
if name == "YouTubeVideoUploadService":
520+
# YouTubeVideoUploadService uses REST, so we cannot pass the credentials inside the gRPC
521+
# channel; we need to pass them explicitly.
522+
return service_client_class(
523+
transport=service_transport,
524+
credentials=self.credentials,
525+
developer_token=self.developer_token,
526+
login_customer_id=self.login_customer_id,
527+
linked_customer_id=self.linked_customer_id,
528+
use_cloud_org_for_api_access=self.use_cloud_org_for_api_access
529+
)
530+
507531
return service_client_class(transport=service_transport)
508532

509533
def get_type(self, name: str, version: str = _DEFAULT_VERSION) -> Union[ProtoPlusMessageType, ProtobufMessageType]:

0 commit comments

Comments
 (0)