Set the given key on the object.

    Args:
        pb_or_dict (Union[~google.protobuf.message.Message, Mapping]): the
            object.
        key (str): The key on the object in question.
        value (Any): The value to set.

    Raises:
        TypeError: If pb_or_dict is not a Message or Mapping.

def set(pb_or_dict, key, value):
    """Set the given key on the object.

    Args:
        pb_or_dict (Union[~google.protobuf.message.Message, Mapping]): the
            object.
        key (str): The key on the object in question.
        value (Any): The value to set.

    Raises:
        TypeError: If pb_or_dict is not a Message or Mapping.
    """
    # pylint: disable=redefined-builtin,too-many-branches
    # redefined-builtin: We want 'set' to be part of the public interface.
    # too-many-branches: This method is inherently complex.

    # Sanity check: Is our target object valid?
    if not isinstance(pb_or_dict, (collections.MutableMapping, Message)):
        raise TypeError('Tried to set a key %s on an invalid object; '
                        'expected a dict or protobuf message.' % key)

    # We may be setting a nested key. Resolve this.
    key, subkey = _resolve_subkeys(key)

    # If a subkey exists, then get that object and call this method
    # recursively against it using the subkey.
    if subkey is not None:
        if isinstance(pb_or_dict, collections.MutableMapping):
            pb_or_dict.setdefault(key, {})
        set(get(pb_or_dict, key), subkey, value)
        return

    # Attempt to set the value on the types of objects we know how to deal
    # with.
    if isinstance(pb_or_dict, collections.MutableMapping):
        pb_or_dict[key] = value
    elif isinstance(value, (collections.MutableSequence, tuple)):
        # Clear the existing repeated protobuf message of any elements
        # currently inside it.
        while getattr(pb_or_dict, key):
            getattr(pb_or_dict, key).pop()

        # Write our new elements to the repeated field.
        for item in value:
            if isinstance(item, collections.Mapping):
                getattr(pb_or_dict, key).add(**item)
            else:
                getattr(pb_or_dict, key).extend([item])
    elif isinstance(value, collections.Mapping):
        # Assign the dictionary values to the protobuf message.
        for item_key, item_value in value.items():
            set(getattr(pb_or_dict, key), item_key, item_value)
    elif isinstance(value, Message):
        # Assign the protobuf message values to the protobuf message.
        for item_key, item_value in value.ListFields():
            set(getattr(pb_or_dict, key), item_key.name, item_value)
    else:
        setattr(pb_or_dict, key, value)