class_docstr: TaskDataStore is obtained through FlowDataStore.get_datastore_for_task and
  is used to store three things:
      - Task artifacts (using save_artifacts and load_artifacts) which will
        ultimately be stored using ContentAddressedStore's save_blobs and
        load_blobs. This is basically the content indexed portion of the
        storage (identical objects are stored only once).
      - Metadata information (using save_metadata and load_metadata) which
        stores JSON encoded metadata about a task in a non-content indexed
        way in a hierarchical manner (ie: the files are stored
        in a path indicated by the pathspec (run_id/step_name/task_id)).
        This portion of the store can be viewed as name indexed (storing
        two metadata items with the same name will overwrite the previous item
        so the condition of equality is the name as
        opposed to the content).
      - Logs which are a special sort of task metadata but are handled
        differently (they are not JSON-encodable dictionaries).
class_docstr_tok: ["TaskDataStore","is","obtained","through","FlowDataStore.get_datastore_for_task","and","is","used","to","store","three","things",":","-","Task","artifacts","(","using","save_artifacts","and","load_artifacts",")","which","will","ultimately","be","stored","using","ContentAddressedStore","'s","save_blobs","and","load_blobs",".","This","is","basically","the","content","indexed","portion","of","the","storage","(","identical","objects","are","stored","only","once",")",".","-","Metadata","information","(","using","save_metadata","and","load_metadata",")","which","stores","JSON","encoded","metadata","about","a","task","in","a","non-content","indexed","way","in","a","hierarchical","manner","(","ie",":","the","files","are","stored","in","a","path","indicated","by","the","pathspec","(","run_id\/step_name\/task_id",")",")",".","This","portion","of","the","store","can","be","viewed","as","name","indexed","(","storing","two","metadata","items","with","the","same","name","will","overwrite","the","previous","item","so","the","condition","of","equality","is","the","name","as","opposed","to","the","content",")",".","-","Logs","which","are","a","special","sort","of","task","metadata","but","are","handled","differently","(","they","are","not","JSON-encodable","dictionaries",")","."]
docstr: Mirror function to save_artifacts
  
  This function will retrieve the objects referenced by 'name'. Each
  object will be fetched and returned if found. Note that this function
  will return objects that may not be the same as the ones saved using
  saved_objects (taking into account possible environment changes, for
  example different conda environments) but it will return objects that
  can be used as the objects passed in to save_objects.
  
  This method can be used in both 'r' and 'w' mode. For the latter use
  case, this can happen when `passdown_partial` is called and an artifact
  passed down that way is then loaded.
  
  Parameters
  ----------
  names : List[string]
      List of artifacts to retrieve
  
  Returns
  -------
  Iterator[(string, object)] :
      An iterator over objects retrieved.
docstr_tok: ["Mirror","function","to","save_artifacts","This","function","will","retrieve","the","objects","referenced","by","'name","'",".","Each","object","will","be","fetched","and","returned","if","found",".","Note","that","this","function","will","return","objects","that","may","not","be","the","same","as","the","ones","saved","using","saved_objects","(","taking","into","account","possible","environment","changes",",","for","example","different","conda","environments",")","but","it","will","return","objects","that","can","be","used","as","the","objects","passed","in","to","save_objects",".","This","method","can","be","used","in","both","'","r","'","and","'","w","'","mode",".","For","the","latter","use","case",",","this","can","happen","when","`","passdown_partial","`","is","called","and","an","artifact","passed","down","that","way","is","then","loaded",".","Parameters","--","--","--","--","--","names",":","List","[","string","]","List","of","artifacts","to","retrieve","Returns","--","--","--","-","Iterator","[","(","string",",","object",")","]",":","An","iterator","over","objects","retrieved","."]
code: def load_artifacts(self, names):
      """
      Mirror function to save_artifacts
  
      This function will retrieve the objects referenced by 'name'. Each
      object will be fetched and returned if found. Note that this function
      will return objects that may not be the same as the ones saved using
      saved_objects (taking into account possible environment changes, for
      example different conda environments) but it will return objects that
      can be used as the objects passed in to save_objects.
  
      This method can be used in both 'r' and 'w' mode. For the latter use
      case, this can happen when `passdown_partial` is called and an artifact
      passed down that way is then loaded.
  
      Parameters
      ----------
      names : List[string]
          List of artifacts to retrieve
  
      Returns
      -------
      Iterator[(string, object)] :
          An iterator over objects retrieved.
      """
      if not self._info:
          raise DataException(
              "Datastore for task '%s' does not have the required metadata to "
              "load artifacts" % self._path
          )
      to_load = defaultdict(list)
      for name in names:
          info = self._info.get(name)
          # We use gzip+pickle-v2 as this is the oldest/most compatible.
          # This datastore will always include the proper encoding version so
          # this is just to be able to read very old artifacts
          if info:
              encode_type = info.get("encoding", "gzip+pickle-v2")
          else:
              encode_type = "gzip+pickle-v2"
          if encode_type not in self._encodings:
              raise DataException(
                  "Python 3.4 or later is required to load artifact '%s'"
                  % name
              )
          else:
              to_load[self._objects[name]].append(name)
      # At this point, we load what we don't have from the CAS
      # We assume that if we have one "old" style artifact, all of them are
      # like that which is an easy assumption to make since artifacts are all
      # stored by the same implementation of the datastore for a given task.
      for key, blob in self._ca_store.load_blobs(to_load.keys()):
          names = to_load[key]
          for name in names:
              # We unpickle everytime to have fully distinct objects (the user
              # would not expect two artifacts with different names to actually
              # be aliases of one another)
              yield name, pickle.loads(blob)
  
imports: from collections import defaultdict
  import json
  import pickle
  import sys
  import time
  from functools import wraps
  from io import BufferedIOBase, FileIO, RawIOBase
  from types import MethodType, FunctionType
  from ..None import metaflow_config
  from ..exception import MetaflowInternalError
  from ..metadata import DataArtifact, MetaDatum
  from ..parameters import Parameter
  from ..util import Path, is_stringish, to_fileobj
  from .exceptions import DataException, UnpicklableArtifactException
label_desc: Use image node_id 16 for calling the TaskDataStore obj's underlying member method code with example usage: obj.load_artifacts(names) without return types