text: """Results backends are used to store long-running query results
  
  The Abstraction is flask-caching, which uses the BaseCache class from cachelib
  """
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  
  try:
      import cPickle as pickle
  except ImportError:
      import pickle
  
  import io
  import logging
  
  import boto3
  from cachelib import BaseCache
  
  
  class S3Cache(BaseCache):
  
      """S3 cache implementation.
  
      Adapted from examples in
      https://github.com/pallets/werkzeug/blob/master/werkzeug/contrib/cache.py.
  
      Timeout parameters are ignored as S3 doesn't support key-level expiration.
      To expire keys, set up an expiration policy as described in
      https://aws.amazon.com/blogs/aws/amazon-s3-object-expiration/.
  
      get_extra_args, put_extra_args, and head_extra_args can be used to provide additional arguments to
      underlying boto3 s3 client.
      See: http://boto3.readthedocs.io/en/latest/reference/services/s3.html for more details
      """
  
      def __init__(
              self, s3_bucket, key_prefix, default_timeout=300, get_extra_args={}, put_extra_args={}, head_extra_args={}):
          self.default_timeout = default_timeout
  
          self.s3_client = boto3.client('s3')
  
          self.bucket = s3_bucket
          self._key_prefix = key_prefix
          self.get_extra_args = get_extra_args
          self.put_extra_args = put_extra_args
          self.head_extra_args = head_extra_args
  
      @property
      def key_prefix(self):
          return (
              self._key_prefix
              if not hasattr(self._key_prefix, "__call__")
              else self._key_prefix()
          )
  
      def get(self, key):
          """Look up key in the cache and return the value for it.
  
          :param key: the key to be looked up.
          :returns: The value if it exists and is readable, else ``None``.
          """
          if not self._key_exists(key):
              return None
          else:
              value_file = io.BytesIO()
  
              try:
                  self.s3_client.download_fileobj(
                      self.bucket,
                      self._full_s3_key(key),
                      value_file,
                      ExtraArgs=self.get_extra_args
                  )
              except Exception as e:
                  logging.warn('Error while trying to get key %s', key)
                  logging.exception(e)
  
                  return None
              else:
                  value_file.seek(0)
                  return pickle.load(value_file)
  
      def delete(self, key):
          """Delete `key` from the cache.
  
          :param key: the key to delete.
          :returns: Whether the key existed and has been deleted.
          :rtype: boolean
          """
          if not self._key_exists(key):
              return False
          else:
              try:
                  self.s3_client.delete_objects(
                      Bucket=self.bucket,
                      Delete={
                          'Objects': [
                              {
                                  'Key': self._full_s3_key(key)
                              }
                          ]
                      }
                  )
              except Exception as e:
                  logging.warn('Error while trying to delete key %s', key)
                  logging.exception(e)
  
                  return False
              else:
                  return True
  
      def set(self, key, value, timeout=None):
          """Add a new key/value to the cache.
  
          If the key already exists, the existing value is overwritten.
  
          :param key: the key to set
          :param value: the value for the key
          :param timeout: the cache timeout for the key in seconds (if not
                          specified, it uses the default timeout). A timeout of
                          0 idicates that the cache never expires.
          :returns: ``True`` if key has been updated, ``False`` for backend
                    errors. Pickling errors, however, will raise a subclass of
                    ``pickle.PickleError``.
          :rtype: boolean
          """
          value_file = io.BytesIO()
          pickle.dump(value, value_file)
  
          try:
              value_file.seek(0)
              self.s3_client.upload_fileobj(
                  value_file,
                  self.bucket,
                  self._full_s3_key(key),
                  ExtraArgs=self.put_extra_args
              )
          except Exception as e:
              logging.warn('Error while trying to set key %s', key)
              logging.exception(e)
  
              return False
          else:
              return True
  
      def add(self, key, value, timeout=None):
          """Works like :meth:`set` but does not overwrite existing values.
  
          :param key: the key to set
          :param value: the value for the key
          :param timeout: the cache timeout for the key in seconds (if not
                          specified, it uses the default timeout). A timeout of
                          0 idicates that the cache never expires.
          :returns: Same as :meth:`set`, but also ``False`` for already
                    existing keys.
          :rtype: boolean
          """
          if self._key_exists(key):
              return False
          else:
              return self.set(key, value, timeout=timeout)
  
      def clear(self):
          """Clears the cache.
  
          Keep in mind that not all caches support completely clearing the cache.
          :returns: Whether the cache has been cleared.
          :rtype: boolean
          """
          return False
  
      def _full_s3_key(self, key):
          """Convert a cache key to a full S3 key, including the key prefix."""
          return '%s%s' % (self.key_prefix, key)
  
      def _key_exists(self, key):
          """Determine whether the given key exists in the bucket."""
          try:
              self.s3_client.head_object(
                  Bucket=self.bucket,
                  Key=self._full_s3_key(key),
                  **self.head_extra_args
              )
          except Exception:
              # head_object throws an exception when object doesn't exist
              return False
          else:
              return True
  