Protobuf Messages

With the use_proto_plus configuration parameter, you can specify whether you want the library to return proto-plus messages or protobuf messages . For details on how to set this parameter, see the configuration docs .

This section describes the performance implications of choosing which types of messages to use, therefore, we recommend that you read and understand the options in order to make an informed decision.

Proto-plus versus protobuf messages

The code generator pipeline integrates proto-plus as a way to improve the ergonomics of the protobuf message interface by making them behave more like native Python objects. However, this means that using proto-plus introduces performance overhead.

Proto-plus performance

One of the core benefits of proto-plus is that it converts protobuf messages and well-known types to native Python types through a process called type marshaling .

Marshaling occurs when a field is accessed on a proto-plus message instance, specifically when a field is either read or set, for example, in a protobuf definition:

  syntax 
 = 
 "proto3" 
 ; 
 message 
 Dog 
 { 
 string 
 name 
 = 
 1 
 ; 
 } 
 

When this definition is converted to a proto-plus class, it would look something like this:

  import 
  
 proto 
 class 
  
 Dog 
 ( 
 proto 
 . 
 Message 
 ): 
 name 
 = 
 proto 
 . 
 Field 
 ( 
 proto 
 . 
 STRING 
 , 
 number 
 = 
 1 
 ) 
 

You can then initialize the Dog class and access its name field as you would any other Python object:

  dog 
 = 
 Dog 
 () 
 dog 
 . 
 name 
 = 
 "Scruffy" 
 print 
 ( 
 dog 
 . 
 name 
 ) 
 

When reading and setting the name field, the value is converted from a native Python str type to a string type so that the value is compatible with the protobuf runtime.

Based on our performance analyses, we have determined that the time spent doing these type conversions has a large enough performance impact that users should decide, based on their needs, whether or not to use protobuf messages.

Use cases for proto-plus and protobuf messages

Proto-plus message use cases
Proto-plus offers a number of ergonomic improvements over protobuf messages, so they're ideal for writing maintainable, readable code. Since they expose native Python objects, they're easier to use and understand.
Protobuf message use cases
Use protobufs for performance-sensitive use cases, specifically in apps that need to process large reports quickly, or that build mutate requests with a large number of operations, for example with BatchJobService or OfflineUserDataJobService .

Dynamically changing message types

After selecting the appropriate message type for your app, you might find that you need to use the other type for a specific workflow. In this case, it's easy to switch between the two types dynamically using utilities offered by the client library. Using the same Dog message class from above:

  from 
  
 google.ads.googleads 
  
 import 
 util 
 # Proto-plus message type 
 dog 
 = 
 Dog 
 () 
 # Protobuf message type 
 dog 
 = 
 util 
 . 
 convert_proto_plus_to_protobuf 
 ( 
 dog 
 ) 
 # Back to proto-plus message type 
 dog 
 = 
 util 
 . 
 convert_protobuf_to_proto_plus 
 ( 
 dog 
 ) 
 

Protobuf message interface differences

The proto-plus interface is documented in detail , but here we'll highlight some key differences that affect common use cases for the Google Ads client library.

Bytes serialization

Proto-plus messages
 serialized 
 = 
 type 
 ( 
 campaign 
 ) 
 . 
 serialize 
 ( 
 campaign 
 ) 
 deserialized 
 = 
 type 
 ( 
 campaign 
 ) 
 . 
 deserialize 
 ( 
 serialized 
 ) 
Protobuf messages
 serialized 
 = 
 campaign 
 . 
 SerializeToString 
 () 
 deserialized 
 = 
 campaign 
 . 
 FromString 
 ( 
 serialized 
 ) 

JSON serialization

Proto-plus messages
 serialized 
 = 
 type 
 ( 
 campaign 
 ) 
 . 
 to_json 
 ( 
 campaign 
 ) 
 deserialized 
 = 
 type 
 ( 
 campaign 
 ) 
 . 
 from_json 
 ( 
 serialized 
 ) 
Protobuf messages
 from 
  
 google.protobuf.json_format 
  
 import 
 MessageToJson 
 , 
 Parse 
 serialized 
 = 
 MessageToJson 
 ( 
 campaign 
 ) 
 deserialized 
 = 
 Parse 
 ( 
 serialized 
 , 
 campaign 
 ) 

Field masks

The field mask helper method provided by api-core is designed to use protobuf message instances. So when using proto-plus messages, convert them to protobuf messages to utilize the helper:

Proto-plus messages
 from 
  
 google.api_core.protobuf_helpers 
  
 import 
 field_mask 
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 protobuf_campaign 
 = 
 util 
 . 
 convert_proto_plus_to_protobuf 
 ( 
 campaign 
 ) 
 mask 
 = 
 field_mask 
 ( 
 None 
 , 
 protobuf_campaign 
 ) 
Protobuf messages
 from 
  
 google.api_core.protobuf_helpers 
  
 import 
 field_mask 
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 mask 
 = 
 field_mask 
 ( 
 None 
 , 
 campaign 
 ) 

Enums

Enums exposed by proto-plus messages are instances of Python's native enum type and therefore inherit a number of convenience methods.

Enum type retrieval

When using the GoogleAdsClient.get_type method to retrieve enums, the messages that are returned are slightly different depending on whether you're using proto-plus or protobuf messages. For example:

Proto-plus messages
 val 
 = 
 client 
 . 
 get_type 
 ( 
 "CampaignStatusEnum" 
 ) 
 . 
 CampaignStatus 
 . 
 PAUSED 
Protobuf messages
 val 
 = 
 client 
 . 
 get_type 
 ( 
 "CampaignStatusEnum" 
 ) 
 . 
 PAUSED 

To make retrieving enums simpler, there's a convenience attribute on GoogleAdsClient instances that has a consistent interface regardless of which message type you're using:

  val 
 = 
 client 
 . 
 enums 
 . 
 CampaignStatusEnum 
 . 
 PAUSED 
 

Enum value retrieval

Sometimes it's useful to know the value, or field ID, of a given enum, for example, PAUSED on the CampaignStatusEnum corresponds to 3 :

Proto-plus messages
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 campaign 
 . 
 status 
 = 
 client 
 . 
 enums 
 . 
 CampaignStatusEnum 
 . 
 PAUSED 
 # To read the value of campaign status 
 print 
 ( 
 campaign 
 . 
 status 
 . 
 value 
 ) 
Protobuf messages
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 status_enum 
 = 
 client 
 . 
 enums 
 . 
 CampaignStatusEnum 
 campaign 
 . 
 status 
 = 
 status_enum 
 . 
 PAUSED 
 # To read the value of campaign status 
 print 
 ( 
 status_enum 
 . 
 CampaignStatus 
 . 
 Value 
 ( 
 campaign 
 . 
 status 
 )) 

Enum name retrieval

Sometimes it's useful to know the name of an enum field. For example, when reading objects from the API you might want to know which campaign status the int 3 corresponds to:

Proto-plus messages
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 campaign 
 . 
 status 
 = 
 client 
 . 
 enums 
 . 
 CampaignStatusEnum 
 . 
 PAUSED 
 # To read the name of campaign status 
 print 
 ( 
 campaign 
 . 
 status 
 . 
 name 
 ) 
Protobuf messages
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 status_enum 
 = 
 client 
 . 
 enums 
 . 
 CampaignStatusEnum 
 # Sets the campaign status to the int value for PAUSED 
 campaign 
 . 
 status 
 = 
 status_enum 
 . 
 PAUSED 
 # To read the name of campaign status 
 status_enum 
 . 
 CampaignStatus 
 . 
 Name 
 ( 
 campaign 
 . 
 status 
 ) 

Repeated fields

As described in the proto-plus docs , repeated fields are generally equivalent to typed lists, which means that they behave almost identically to a list .

Append values to repeated scalar fields

When adding values to repeated scalar type fields, for example string or int64 fields, the interface is the same regardless of message type:

Proto-plus messages
 ad 
 . 
 final_urls 
 . 
 append 
 ( 
 "https://www.example.com" 
 ) 
Protobuf messages
 ad 
 . 
 final_urls 
 . 
 append 
 ( 
 "https://www.example.com" 
 ) 

This includes all other common list methods as well, for example extend :

Proto-plus messages
 ad 
 . 
 final_urls 
 . 
 extend 
 ([ 
 "https://www.example.com" 
 , 
 "https://www.example.com/2" 
 ]) 
Protobuf messages
 ad 
 . 
 final_urls 
 . 
 extend 
 ([ 
 "https://www.example.com" 
 , 
 "https://www.example.com/2" 
 ]) 

Append message types to repeated fields

If the repeated field is not a scalar type , the behavior when adding them to repeated fields is slightly different:

Proto-plus messages
 frequency_cap 
 = 
 client 
 . 
 get_type 
 ( 
 "FrequencyCapEntry" 
 ) 
 frequency_cap 
 . 
 cap 
 = 
 100 
 campaign 
 . 
 frequency_caps 
 . 
 append 
 ( 
 frequency_cap 
 ) 
Protobuf messages
 # The add method initializes a message and adds it to the repeated field 
 frequency_cap 
 = 
 campaign 
 . 
 frequency_caps 
 . 
 add 
 () 
 frequency_cap 
 . 
 cap 
 = 
 100 

Assign repeated fields

For both scalar and non-scalar repeated fields, you can assign lists to the field in different ways:

Proto-plus messages
 # In proto-plus it's possible to use assignment. 
 urls 
 = 
 [ 
 "https://www.example.com" 
 ] 
 ad 
 . 
 final_urls 
 = 
 urls 
Protobuf messages
 # Protobuf messages do not allow assignment, but you can replace the 
 # existing list using slice syntax. 
 urls 
 = 
 [ 
 "https://www.example.com" 
 ] 
 ad 
 . 
 final_urls 
 [:] 
 = 
 urls 

Empty messages

Sometimes it's useful to know whether a message instance contains any information, or has any of its fields set.

Proto-plus messages
 # When using proto-plus messages you can simply check the message for 
 # truthiness. 
 is_empty 
 = 
 bool 
 ( 
 campaign 
 ) 
 is_empty 
 = 
 not 
 campaign 
Protobuf messages
 is_empty 
 = 
 campaign 
 . 
 ByteSize 
 () 
 == 
 0 

Message copy

For both proto-plus and protobuf messages, we recommend using the copy_from helper method on the GoogleAdsClient :

  client 
 . 
 copy_from 
 ( 
 campaign 
 , 
 other_campaign 
 ) 
 

Empty message fields

The process for setting empty message fields is the same regardless of the message type you're using. You just need to copy an empty message into the field in question. See the Message copy section as well as the Empty Message Fields guide. Here's an example of how to set an empty message field:

  client 
 . 
 copy_from 
 ( 
 campaign 
 . 
 manual_cpm 
 , 
 client 
 . 
 get_type 
 ( 
 "ManualCpm" 
 )) 
 

Field names that are reserved words

When using proto-plus messages, field names automatically appear with a trailing underscore if the name is also a reserved word in Python. Here's an example of working with an Asset instance:

  asset 
 = 
 client 
 . 
 get_type 
 ( 
 "Asset" 
 ) 
 asset 
 . 
 type_ 
 = 
 client 
 . 
 enums 
 . 
 AssetTypeEnum 
 . 
 IMAGE 
 

The full list of reserved names is constructed in the gapic generator module. It can be accessed programmatically as well.

First, install the module:

  python 
 - 
 m 
 pip 
 install 
 gapic 
 - 
 generator 
 

Then, in a Python REPL or script:

  import 
  
 gapic.utils 
 print 
 ( 
 gapic 
 . 
 utils 
 . 
 reserved_names 
 . 
 RESERVED_NAMES 
 ) 
 

Field presence

Because the fields on protobuf message instances have default values, it's not always intuitive to know whether a field has been set or not.

Proto-plus messages
 # Use the "in" operator. 
 has_field 
 = 
 "name" 
 in 
 campaign 
Protobuf messages
 campaign 
 = 
 client 
 . 
 get_type 
 ( 
 "Campaign" 
 ) 
 # Determines whether "name" is set and not just an empty string. 
 campaign 
 . 
 HasField 
 ( 
 "name" 
 ) 

The protobuf Message class interface has a HasField method that determines whether the field on a message has been set, even if it was set to a default value.

Protobuf message methods

The protobuf message interface includes some convenience methods that are not part of the proto-plus interface; however, it's simple to access them by converting a proto-plus message to its protobuf counterpart:

  # Accessing the ListFields method 
 protobuf_campaign 
 = 
 util 
 . 
 convert_protobuf_to_proto_plus 
 ( 
 campaign 
 ) 
 print 
 ( 
 campaign 
 . 
 ListFields 
 ()) 
 # Accessing the Clear method 
 protobuf_campaign 
 = 
 util 
 . 
 convert_protobuf_to_proto_plus 
 ( 
 campaign 
 ) 
 print 
 ( 
 campaign 
 . 
 Clear 
 ()) 
 

Issue tracker

If you have any questions about these changes or any problems migrating to the latest version of the library, file an issue on our tracker.

Create a Mobile Website
View Site in Mobile | Classic
Share by: