class_docstr: Helper class to handle metadata I/O.
  
  Not thread-safe without external synchronisation.
docstr: Updates the execution proto with given type and state.
docstr_tok: ["Updates","the","execution","proto","with","given","type","and","state","."]
code: def _update_execution_proto(
      self,
      execution: metadata_store_pb2.Execution,
      pipeline_info: Optional[data_types.PipelineInfo] = None,
      component_info: Optional[data_types.ComponentInfo] = None,
      state: Optional[str] = None,
      exec_properties: Optional[Dict[str, Any]] = None,
  ) -> metadata_store_pb2.Execution:
      """Updates the execution proto with given type and state."""
      if state is not None:
          execution.properties[
              _EXECUTION_TYPE_KEY_STATE
          ].string_value = state
      # Forward-compatible change to leverage built-in schema to track states.
      if state == EXECUTION_STATE_CACHED:
          execution.last_known_state = (
              metadata_store_pb2.Execution.CACHED
          )
      elif state == EXECUTION_STATE_COMPLETE:
          execution.last_known_state = (
              metadata_store_pb2.Execution.COMPLETE
          )
      elif state == EXECUTION_STATE_NEW:
          execution.last_known_state = (
              metadata_store_pb2.Execution.RUNNING
          )
  
      exec_properties = exec_properties or {}
      # TODO(ruoyu): Enforce a formal rule for execution schema change.
      for k, v in exec_properties.items():
          # We always convert execution properties to unicode.
          if isinstance(v, bytes):
              # Decode byte string into a Unicode string.
              v = v.decode("utf-8")
          else:
              # For all other types, store its string representation.
              v = str(v)
          execution.properties[k].string_value = v
      # We also need to checksum UDF file to identify different binary being
      # used. Do we have a better way to checksum a file than hashlib.md5?
      # TODO(ruoyu): Find a better place / solution to the checksum logic.
      # TODO(ruoyu): SHA instead of MD5.
      if (
          "module_file" in exec_properties
          and exec_properties["module_file"]
          and fileio.exists(exec_properties["module_file"])
      ):
          contents = fileio.open(
              exec_properties["module_file"], "rb"
          ).read()
          execution.properties["checksum_md5"].string_value = (
              hashlib.md5(contents).hexdigest().encode("utf-8")
          )
      if pipeline_info:
          execution.properties[
              "pipeline_name"
          ].string_value = pipeline_info.pipeline_name
          execution.properties[
              "pipeline_root"
          ].string_value = pipeline_info.pipeline_root
          if pipeline_info.run_id:
              execution.properties[
                  "run_id"
              ].string_value = pipeline_info.run_id
      if component_info:
          execution.properties[
              "component_id"
          ].string_value = component_info.component_id
      return execution
  
imports: import collections
  import copy
  import hashlib
  import itertools
  import os
  import random
  import time
  import types
  from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union
  import absl
  from tfx.dsl.io import fileio
  from tfx.orchestration import data_types
  from tfx.orchestration.portable.mlmd import event_lib
  from tfx.types import artifact
  from tfx.types import artifact_utils
  import ml_metadata
  from ml_metadata.proto import metadata_store_pb2
label_desc: Use image node_id 16 for calling the Metadata obj's underlying member method code with example usage: obj._update_execution_proto(execution, pipeline_info, component_info, state, exec_properties) and returns: execution