code: def get_field_mask_paths(
      execution: metadata_store_pb2.Execution,
      execution_copy: metadata_store_pb2.Execution,
  ) -> list[str]:
      """Get Execution field mask paths for mutations.
  
      Args:
        execution: original in-memory state of an MLMD execution.
        execution_copy: in-memory state of an MLMD execution after mutations.
  
      Returns:
        All top-level field paths, and property / custom property fields with diffs.
        Only field paths in the mask will be updated during MLMD commits.
      """
      field_mask_paths = []
  
      # Get all non-property field paths.
      for field in metadata_store_pb2.Execution.DESCRIPTOR.fields:
          # Skip property fields.
          if field.name not in ["properties", "custom_properties"]:
              field_mask_paths.append(field.name)
  
      # Get property names with diffs.  Note that Python supports == operator for
      # proto messages.
      def _get_property_names_with_diff(
          properties: containers.MessageMap[
              str, metadata_store_pb2.Value
          ],
          copy_properties: containers.MessageMap[
              str, metadata_store_pb2.Value
          ],
      ) -> list[str]:
          property_names_with_diff = []
          for name in set(properties.keys()).union(
              set(copy_properties.keys())
          ):
              if (
                  name in properties
                  and name in copy_properties
                  and properties[name] == copy_properties[name]
              ):
                  continue
              property_names_with_diff.append(name)
          return property_names_with_diff
  
      property_names_with_diff = _get_property_names_with_diff(
          execution.properties, execution_copy.properties
      )
      custom_property_names_with_diff = _get_property_names_with_diff(
          execution.custom_properties, execution_copy.custom_properties
      )
  
      field_mask_paths.extend(
          [f"properties.{name}" for name in property_names_with_diff]
      )
      field_mask_paths.extend(
          [
              f"custom_properties.{name}"
              for name in custom_property_names_with_diff
          ]
      )
      return field_mask_paths
  
imports: import collections
  import contextlib
  import copy
  import threading
  import typing
  from typing import Callable, Iterator, MutableMapping, Optional
  import cachetools
  from tfx.orchestration import metadata
  from google.protobuf.internal import containers
  from ml_metadata.proto import metadata_store_pb2
label_desc: Use image node_id 4 for calling a global function with example usage: get_field_mask_paths(execution, execution_copy) and returns: field_mask_paths, property_names_with_diff