text: from __future__ import unicode_literals
  from builtins import object
  import time
  from django.core.cache import cache
  
  
  class BaseThrottle(object):
      """
      A simplified, swappable base class for throttling.
  
      Does nothing save for simulating the throttling API and implementing
      some common bits for the subclasses.
  
      Accepts a number of optional kwargs::
  
          * ``throttle_at`` - the number of requests at which the user should
            be throttled. Default is 150 requests.
          * ``timeframe`` - the length of time (in seconds) in which the user
            make up to the ``throttle_at`` requests. Default is 3600 seconds (
            1 hour).
          * ``expiration`` - the length of time to retain the times the user
            has accessed the api in the cache. Default is 604800 (1 week).
      """
      def __init__(self, throttle_at=150, timeframe=3600, expiration=None):
          self.throttle_at = throttle_at
          # In seconds, please.
          self.timeframe = timeframe
  
          if expiration is None:
              # Expire in a week.
              expiration = 604800
  
          self.expiration = int(expiration)
  
      def convert_identifier_to_key(self, identifier):
          """
          Takes an identifier (like a username or IP address) and converts it
          into a key usable by the cache system.
          """
          bits = []
  
          for char in identifier:
              if char.isalnum() or char in ['_', '.', '-']:
                  bits.append(char)
  
          safe_string = ''.join(bits)
          return "%s_accesses" % safe_string
  
      def should_be_throttled(self, identifier, **kwargs):
          """
          Returns whether or not the user has exceeded their throttle limit.
  
          Always returns ``False``, as this implementation does not actually
          throttle the user.
          """
          return False
  
      def accessed(self, identifier, **kwargs):
          """
          Handles recording the user's access.
  
          Does nothing in this implementation.
          """
          pass
  
  
  class CacheThrottle(BaseThrottle):
      """
      A throttling mechanism that uses just the cache.
      """
      def should_be_throttled(self, identifier, **kwargs):
          """
          Returns whether or not the user has exceeded their throttle limit.
  
          Maintains a list of timestamps when the user accessed the api within
          the cache.
  
          Returns ``False`` if the user should NOT be throttled or ``True`` if
          the user should be throttled.
          """
          key = self.convert_identifier_to_key(identifier)
  
          # Weed out anything older than the timeframe.
          minimum_time = int(time.time()) - int(self.timeframe)
          times_accessed = [access for access in cache.get(key, []) if access >= minimum_time]
          cache.set(key, times_accessed, self.expiration)
  
          if len(times_accessed) >= int(self.throttle_at):
              # Throttle them.
              return True
  
          # Let them through.
          return False
  
      def accessed(self, identifier, **kwargs):
          """
          Handles recording the user's access.
  
          Stores the current timestamp in the "accesses" list within the cache.
          """
          key = self.convert_identifier_to_key(identifier)
          times_accessed = cache.get(key, [])
          times_accessed.append(int(time.time()))
          cache.set(key, times_accessed, self.expiration)
  
  
  class CacheDBThrottle(CacheThrottle):
      """
      A throttling mechanism that uses the cache for actual throttling but
      writes-through to the database.
  
      This is useful for tracking/aggregating usage through time, to possibly
      build a statistics interface or a billing mechanism.
      """
      def accessed(self, identifier, **kwargs):
          """
          Handles recording the user's access.
  
          Does everything the ``CacheThrottle`` class does, plus logs the
          access within the database using the ``ApiAccess`` model.
          """
          # Do the import here, instead of top-level, so that the model is
          # only required when using this throttling mechanism.
          from tastypie.models import ApiAccess
          super(CacheDBThrottle, self).accessed(identifier, **kwargs)
          # Write out the access to the DB for logging purposes.
          ApiAccess.objects.create(
              identifier=identifier,
              url=kwargs.get('url', ''),
              request_method=kwargs.get('request_method', '')
          )
  