Page Summary
-
The
GoogleAdsService.search_streammethod returns a streaming response iterator. -
The iterator should stay in the same scope as the
GoogleAdsServiceclient to prevent broken streams or segmentation faults. -
If the
GoogleAdsServiceobject goes out of scope before the iterator is used, the underlying gRPCChannelobject may be garbage-collected, leading to undefined behavior. -
Accessing the iterator in a different scope than where the service object was created is incorrect usage.
-
The correct approach is to keep the streaming iterator in the same scope as the
GoogleAdsServiceclient while it is being used.
The search_stream method returns an iterator of
SearchGoogleAdsStreamResponse
objects.
Iterate through stream responses
You can iterate through each GoogleAdsRow in
each response's results field as shown in the following code example:
def main(client: GoogleAdsClient, customer_id: str) -> None: ga_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService") query: str = """ SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id""" # Issues a search request using streaming. stream: Iterator[SearchGoogleAdsStreamResponse] = ga_service.search_stream( customer_id=customer_id, query=query ) for batch in stream: rows: List[GoogleAdsRow] = batch.results for row in rows: print( f"Campaign with ID {row.campaign.id} and name " f'"{row.campaign.name}" was found.' )
The structure of each GoogleAdsRow is determined by the fields you select in
your Google Ads Query Language (GAQL) query. For more details about the response
structure, see Google Ads Query Language.
Keep the iterator in scope
When calling
GoogleAdsService.search_stream,
a streaming response iterator is returned. This iterator must remain in the same
scope as the GoogleAdsService client while being consumed to avoid broken
streams or segmentation faults. This is because the underlying gRPC Channel
object is garbage-collected once the open GoogleAdsService object goes out of
scope. If the GoogleAdsService object is no longer in scope when iteration
occurs on the result of search_stream, the Channel object may already be
destroyed, causing undefined behavior when the iterator attempts to retrieve the
next value.
The following code demonstrates incorrect usage of stream iterators:
def stream_response(client, customer_id, query):
# The GoogleAdsService instance goes out of scope when this function
# returns.
ga_service = client.get_service(
"GoogleAdsService", version="v26"
)
return ga_service.search_stream(customer_id=customer_id, query=query)
def main(client, customer_id):
query = "SELECT campaign.name FROM campaign LIMIT 10"
response = stream_response(client, customer_id, query=query)
# Accessing the iterator in a different scope from where the service object
# was created results in undefined behavior.
for batch in response:
print(batch)
In this code, the GoogleAdsService object is created within a different scope
from where the iterator is accessed. As a result, the Channel object may be
destroyed before the iterator consumes the entire response.
Instead, keep the streaming iterator in the same scope as the GoogleAdsService
client for as long as it is being used:
def main(client, customer_id):
ga_service = client.get_service(
"GoogleAdsService", version="v26"
)
query = "SELECT campaign.name FROM campaign LIMIT 10"
response = ga_service.search_stream(customer_id=customer_id, query=query)
# Access the iterator in the same scope where the service object was
# created.
for batch in response:
print(batch)