class_docstr: This class is not meant to be overridden and is meant to be common across
  different datastores.
class_docstr_tok: ["This","class","is","not","meant","to","be","overridden","and","is","meant","to","be","common","across","different","datastores","."]
docstr: Saves blobs of data to the datastore
  
  The blobs of data are saved as is if raw is True. If raw is False, the
  datastore may process the blobs and they should then only be loaded
  using load_blob
  
  NOTE: The idea here is that there are two modes to access the file once
  it is saved to the datastore:
    - if raw is True, you would be able to access it directly using the
      URI returned; the bytes that are passed in as 'blob' would be
      returned directly by reading the object at that URI. You would also
      be able to access it using load_blob passing the key returned
    - if raw is False, no URI would be returned (the URI would be None)
      and you would only be able to access the object using load_blob.
    - The API also specifically takes a list to allow for parallel writes
      if available in the datastore. We could also make a single
      save_blob' API and save_blobs but this seems superfluous
  
  Parameters
  ----------
  blob_iter : Iterator over bytes objects to save
  raw : bool, optional
      Whether to save the bytes directly or process them, by default False
  len_hint : Hint of the number of blobs that will be produced by the
      iterator, by default 0
  
  Returns
  -------
  List of save_blobs_result:
      The list order is the same as the blobs passed in. The URI will be
      None if raw is False.
docstr_tok: ["Saves","blobs","of","data","to","the","datastore","The","blobs","of","data","are","saved","as","is","if","raw","is","True",".","If","raw","is","False",",","the","datastore","may","process","the","blobs","and","they","should","then","only","be","loaded","using","load_blob","NOTE",":","The","idea","here","is","that","there","are","two","modes","to","access","the","file","once","it","is","saved","to","the","datastore",":","-","if","raw","is","True",",","you","would","be","able","to","access","it","directly","using","the","URI","returned",";","the","bytes","that","are","passed","in","as","'blob","'","would","be","returned","directly","by","reading","the","object","at","that","URI",".","You","would","also","be","able","to","access","it","using","load_blob","passing","the","key","returned","-","if","raw","is","False",",","no","URI","would","be","returned","(","the","URI","would","be","None",")","and","you","would","only","be","able","to","access","the","object","using","load_blob",".","-","The","API","also","specifically","takes","a","list","to","allow","for","parallel","writes","if","available","in","the","datastore",".","We","could","also","make","a","single","save_blob","'","API","and","save_blobs","but","this","seems","superfluous","Parameters","--","--","--","--","--","blob_iter",":","Iterator","over","bytes","objects","to","save","raw",":","bool",",","optional","Whether","to","save","the","bytes","directly","or","process","them",",","by","default","False","len_hint",":","Hint","of","the","number","of","blobs","that","will","be","produced","by","the","iterator",",","by","default","0","Returns","--","--","--","-","List","of","save_blobs_result",":","The","list","order","is","the","same","as","the","blobs","passed","in",".","The","URI","will","be","None","if","raw","is","False","."]
code: def save_blobs(self, blob_iter, raw=False, len_hint=0):
      """
      Saves blobs of data to the datastore
  
      The blobs of data are saved as is if raw is True. If raw is False, the
      datastore may process the blobs and they should then only be loaded
      using load_blob
  
      NOTE: The idea here is that there are two modes to access the file once
      it is saved to the datastore:
        - if raw is True, you would be able to access it directly using the
          URI returned; the bytes that are passed in as 'blob' would be
          returned directly by reading the object at that URI. You would also
          be able to access it using load_blob passing the key returned
        - if raw is False, no URI would be returned (the URI would be None)
          and you would only be able to access the object using load_blob.
        - The API also specifically takes a list to allow for parallel writes
          if available in the datastore. We could also make a single
          save_blob' API and save_blobs but this seems superfluous
  
      Parameters
      ----------
      blob_iter : Iterator over bytes objects to save
      raw : bool, optional
          Whether to save the bytes directly or process them, by default False
      len_hint : Hint of the number of blobs that will be produced by the
          iterator, by default 0
  
      Returns
      -------
      List of save_blobs_result:
          The list order is the same as the blobs passed in. The URI will be
          None if raw is False.
      """
      results = []
  
      def packing_iter():
          for blob in blob_iter:
              sha = sha1(blob).hexdigest()
              path = self._storage_impl.path_join(
                  self._prefix, sha[:2], sha
              )
              results.append(
                  self.save_blobs_result(
                      uri=self._storage_impl.full_uri(path)
                      if raw
                      else None,
                      key=sha,
                  )
              )
  
              if not self._storage_impl.is_file([path])[0]:
                  # only process blobs that don't exist already in the
                  # backing datastore
                  meta = {"cas_raw": raw, "cas_version": 1}
                  if raw:
                      yield path, (BytesIO(blob), meta)
                  else:
                      yield path, (self._pack_v1(blob), meta)
  
      # We don't actually want to overwrite but by saying =True, we avoid
      # checking again saving some operations. We are already sure we are not
      # sending duplicate files since we already checked.
      self._storage_impl.save_bytes(
          packing_iter(), overwrite=True, len_hint=len_hint
      )
      return results
  
file_path: metaflow/metaflow/datastore/content_addressed_store.py
imports: import gzip
  from collections import namedtuple
  from hashlib import sha1
  from io import BytesIO
  from ..exception import MetaflowInternalError
  from .exceptions import DataException
label_desc: Use image node_id 3 for calling the ContentAddressedStore obj's underlying member method code with example usage: obj.save_blobs(blob_iter, raw, len_hint) and returns: results