class_docstr: TFX artifact used for orchestration.
  
  This is used for type-checking and inter-component communication. Currently,
  it wraps a tuple of (ml_metadata.proto.Artifact,
  ml_metadata.proto.ArtifactType) with additional property accessors for
  internal state.
  
  A user may create a subclass of Artifact and override the TYPE_NAME property
  with the type for this artifact subclass. Users of the subclass may then omit
  the "type_name" field when construction the object.
  
  A user may specify artifact type-specific properties for an Artifact subclass
  by overriding the PROPERTIES dictionary, as detailed below.
  
  Note: the behavior of this class is experimental, without backwards
  compatibility guarantees, and may change in upcoming releases.
class_docstr_tok: ["TFX","artifact","used","for","orchestration",".","This","is","used","for","type-checking","and","inter-component","communication",".","Currently",",","it","wraps","a","tuple","of","(","ml_metadata.proto.Artifact",",","ml_metadata.proto.ArtifactType",")","with","additional","property","accessors","for","internal","state",".","A","user","may","create","a","subclass","of","Artifact","and","override","the","TYPE_NAME","property","with","the","type","for","this","artifact","subclass",".","Users","of","the","subclass","may","then","omit","the","``","type_name","''","field","when","construction","the","object",".","A","user","may","specify","artifact","type-specific","properties","for","an","Artifact","subclass","by","overriding","the","PROPERTIES","dictionary",",","as","detailed","below",".","Note",":","the","behavior","of","this","class","is","experimental",",","without","backwards","compatibility","guarantees",",","and","may","change","in","upcoming","releases","."]
docstr: Custom __getattr__ to allow access to artifact properties.
docstr_tok: ["Custom","__getattr__","to","allow","access","to","artifact","properties","."]
code: def __getattr__(self, name: str) -> Any:
      """Custom __getattr__ to allow access to artifact properties."""
      if name == "_artifact_type":
          # Prevent infinite recursion when used with copy.deepcopy().
          raise AttributeError()
      if name not in self._artifact_type.properties:
          raise AttributeError("Artifact has no property %r." % name)
      property_mlmd_type = self._artifact_type.properties[name]
      if property_mlmd_type == metadata_store_pb2.STRING:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return ""
          return self._artifact.properties[name].string_value
      elif property_mlmd_type == metadata_store_pb2.INT:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return 0
          return self._artifact.properties[name].int_value
      elif property_mlmd_type == metadata_store_pb2.DOUBLE:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return 0.0
          return self._artifact.properties[name].double_value
      elif property_mlmd_type == metadata_store_pb2.BOOLEAN:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return False
          return self._artifact.properties[name].bool_value
      elif property_mlmd_type == metadata_store_pb2.STRUCT:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return None
          if name in self._cached_modifiable_properties:
              return self._cached_modifiable_properties[name]
          value = _decode_struct_value(
              self._artifact.properties[name].struct_value
          )
          # We must cache the decoded lists or dictionaries returned here so that
          # if their recursive contents are modified, the Metadata proto message
          # can be updated to reflect this.
          if isinstance(value, (dict, list)):
              self._cached_modifiable_properties[name] = value
          return value
      elif property_mlmd_type == metadata_store_pb2.PROTO:
          if name not in self._artifact.properties:
              # Avoid populating empty property protobuf with the [] operator.
              return None
          if name in self._cached_modifiable_properties:
              return self._cached_modifiable_properties[name]
          value = proto_utils.unpack_proto_any(
              self._artifact.properties[name].proto_value
          )
          # We must cache the protobuf message here so that if its contents are
          # modified, the Metadata proto message can be updated to reflect this.
          self._cached_modifiable_properties[name] = value
          return value
      else:
          raise ValueError(
              "Unknown MLMD type %r for property %r."
              % (property_mlmd_type, name)
          )
  
imports: import builtins
  import copy
  import importlib
  import json
  from typing import Any, Dict, List, Optional, Type, Union
  from absl import logging
  from tfx.types import artifact_property
  from tfx.types.system_artifacts import SystemArtifact
  from tfx.utils import doc_controls
  from tfx.utils import json_utils
  from tfx.utils import proto_utils
  from google.protobuf import struct_pb2
  from google.protobuf import json_format
  from google.protobuf import message
  from ml_metadata.proto import metadata_store_pb2
label_desc: Use image node_id 3 for calling the Artifact obj's underlying member method code with example usage: obj.__getattr__(name) and returns: self, str, self, int, self, int, self, False, value, None, self, value, None, self