docs: def _get_range_from_filters(cls, filters, model_class):
      """Get property range from filters user provided.
  
      This method also validates there is one and only one closed range on a
      single property.
  
      Args:
        filters: user supplied filters. Each filter should be a list or tuple of
          format (<property_name_as_str>, <query_operator_as_str>,
          <value_of_certain_type>). Value type should satisfy the property's type.
        model_class: the model class for the entity type to apply filters on.
  
      Returns:
        a tuple of (property, start_filter, end_filter). property is the model's
      field that the range is about. start_filter and end_filter define the
      start and the end of the range. (None, None, None) if no range is found.
  
      Raises:
        BadReaderParamsError: if any filter is invalid in any way.
      """
      if not filters:
        return None, None, None
  
      range_property = None
      start_val = None
      end_val = None
      start_filter = None
      end_filter = None
      for f in filters:
        prop, op, val = f
  
        if op in [">", ">=", "<", "<="]:
          if range_property and range_property != prop:
            raise errors.BadReaderParamsError(
                "Range on only one property is supported.")
          range_property = prop
  
          if val is None:
            raise errors.BadReaderParamsError(
                "Range can't be None in filter %s", f)
  
          if op in [">", ">="]:
            if start_val is not None:
              raise errors.BadReaderParamsError(
                  "Operation %s is specified more than once.", op)
            start_val = val
            start_filter = f
          else:
            if end_val is not None:
              raise errors.BadReaderParamsError(
                  "Operation %s is specified more than once.", op)
            end_val = val
            end_filter = f
        elif op != "=":
          raise errors.BadReaderParamsError(
              "Only < <= > >= = are supported as operation. Got %s", op)
  
      if not range_property:
        return None, None, None
  
      if start_val is None or end_val is None:
        raise errors.BadReaderParamsError(
            "Filter should contains a complete range on property %s",
            range_property)
      if issubclass(model_class, db.Model):
        property_obj = model_class.properties()[range_property]
      else:
        property_obj = (
            model_class._properties[  # pylint: disable=protected-access
                range_property])
      supported_properties = (
          _DISCRETE_PROPERTY_SPLIT_FUNCTIONS.keys() +
          _CONTINUOUS_PROPERTY_SPLIT_FUNCTIONS.keys())
      if not isinstance(property_obj, tuple(supported_properties)):
        raise errors.BadReaderParamsError(
            "Filtered property %s is not supported by sharding.", range_property)
      if not start_val < end_val:
        raise errors.BadReaderParamsError(
            "Start value %s should be smaller than end value %s",
            start_val, end_val)
  
      return property_obj, start_filter, end_filter