Queries a stream with name `stream` for all events between `start_time` and
    `end_time` (both inclusive).  An optional `start_id` allows the client to
    restart from a failure, specifying the last ID they read; all events that
    happened after that ID will be returned. An optional `limit` limits the
    maximum number of events returned.  An optional `order` requests results in
    `ASCENDING` or `DESCENDING` order.

def get(self, stream, start_time, end_time, start_id=None, limit=None,
          order=ResultOrder.ASCENDING, namespace=None, timeout=None):
    """
    Queries a stream with name `stream` for all events between `start_time` and
    `end_time` (both inclusive).  An optional `start_id` allows the client to
    restart from a failure, specifying the last ID they read; all events that
    happened after that ID will be returned. An optional `limit` limits the
    maximum number of events returned.  An optional `order` requests results in
    `ASCENDING` or `DESCENDING` order.
    """
    if isinstance(start_time, types.StringTypes):
      start_time = parse(start_time)
    if isinstance(end_time, types.StringTypes):
      end_time = parse(end_time)
    if isinstance(start_time, datetime):
      start_time = datetime_to_kronos_time(start_time)
    if isinstance(end_time, datetime):
      end_time = datetime_to_kronos_time(end_time)

    request_dict = {
      'stream': stream,
      'end_time': end_time,
      'order': order,
    }
    if start_id is not None:
      request_dict['start_id'] = start_id
    else:
      request_dict['start_time'] = start_time

    if limit is not None:
      request_dict['limit'] = limit

    namespace = namespace or self.namespace
    if namespace is not None:
      request_dict['namespace'] = namespace

    errors = []
    last_id = None
    while True:
      try:
        response = self._make_request(self._get_url,
                                      data=request_dict,
                                      stream=True,
                                      timeout=timeout)
        for line in response.iter_lines(chunk_size=self._chunk_size):
          if line:
            # Python's json adds a lot of overhead when decoding a large
            # number of events; ujson fares better. However ujson won't work
            # on PyPy since it's a C extension.
            event = ujson.loads(line, precise_float=True)
            last_id = event[ID_FIELD]
            yield event
        break
      except Exception, e:
        if isinstance(e, requests.exceptions.Timeout):
          raise KronosClientError('Request timed out.')
        errors.append(e)
        if len(errors) == 10:
          raise KronosClientError(errors)
        if last_id is not None:
          request_dict.pop('start_time', None)
          request_dict['start_id'] = last_id
        time.sleep(len(errors) * 0.1)