@log_calls
  def s3_copyfile_atomic(self, source_path, dest_path, make_parents=True, backup_suffix=None):
    '''Copy file on local filesystem in an atomic way, so partial copies aren't possible. Return file's MD5.'''
    # TODO catch S3ResponseError and re-raise as MissingFile
    with atomic_output_file(dest_path, make_parents=make_parents, backup_suffix=backup_suffix) as tmp_path:
      k = boto.s3.key.Key(self.root_bucket)
      key_path = join_path(self.root_dir, source_path)
      k.key = key_path
      # TODO We should also validate the MD5 of the local path against what S3
      # lists, ideally computing the MD5 during the download.
      # Boto sets modification time on file.
      num_attempts = 0
      md5 = None
      # boto does not retry for 404 and IncompleteRead exceptions.
      # Zinc sometimes encounters these (some to the S3 eventual consistency), so add
      # some retry above boto.
      while True:
        try:
          # Use get_contents_to_file so we can catch IncompleteRead and check the file.
          # If we use get_contents_to_filename, boto may not flush the data from s3 into
          # the file and so there is no good way of recovering from this error.
          # Retry alone seems not to fix the problem.
          with open(tmp_path, 'wb') as tmp_f:
            k.get_contents_to_file(tmp_f)
          log.debug("got contents of %s to %s", key_path, tmp_f)
          break
        except httplib.IncompleteRead, e:
          log.warn("[attempt %s] %s. Going to test if file is complete.", num_attempts, e)
          tmp_k = self.root_bucket.get_key(key_path)
          log.info("IncompleteRead exception handler: tmp_k = %s", tmp_k)
          # Get the metadata md5 so correctness is preserved for >5GB files.
          repo_md5 = tmp_k.get_metadata(ZINC_S3_MD5_NAME)
          log.info("IncompleteRead exception handler: repo_md5 = %s", repo_md5)
          if repo_md5:
            md5 = file_md5(tmp_path)
            log.info("IncompleteRead exception handler: md5 = %s", md5)
            if repo_md5 == md5:
              log.info("IncompleteRead exception handler: recovered from IncompleteRead - file is complete - carrying on.")
              break
        except Exception, e:
          log.warn("S3 failed downloading '%s', reason: '%s', attempt number %s", source_path, e, num_attempts)
        num_attempts += 1
        if num_attempts > self.retries:
          raise e
        # If retrying, remove any existing tmp file.
        if os.path.exists(tmp_path):
          os.remove(tmp_path)
        sleep_time_secs = 4 ** num_attempts
        log.warn("Sleeping %d sec before retrying", sleep_time_secs)
        time.sleep(sleep_time_secs)

      # Replicate some boto functionality from get_contents_to_filename to set
      # timestamps on the file.
      log.debug("adding timestamp to file")
      if k.last_modified != None:
        try:
          modified_tuple = rfc822.parsedate_tz(k.last_modified)
          modified_stamp = int(rfc822.mktime_tz(modified_tuple))
          os.utime(tmp_path, (modified_stamp, modified_stamp))
        except Exception, e:
          log.warn("Could not set file timestamp. Ignoring. (%s)", e)

      # If md5 has already been calculated, no need to do it again. Just return it.
      if md5:
        return md5

      # Unfortunately, the logic to get the MD5 from S3 is a bit convoluted. First look for our own header.
      md5 = k.get_metadata(ZINC_S3_MD5_NAME)
      if md5:
        log.debug("S3 key %s has zinc MD5 header: %s", k.key, md5)
      else:
        # Otherwise, we can use the etag as well, but S3 only sets this correctly if it wasn't a multipart upload.
        md5 = str(k.etag).strip('"')
        if md5 in "-": # looks like "7387bb45d8098d7e573f7ced50e474cb-1", which is an invalid MD5
          log.info("S3 item does not have a zinc MD5 -- recomputing: %s", source_path)
          md5 = file_md5(tmp_path)
        else:
          log.debug("S3 key %s has etag header: %s", k.key, md5)

    return md5
dataset/ETHPy150Open bloomreach/zinc/zinc.py/S3Store.s3_copyfile_atomic/swapped operands of `in`