Qval’s API

Auto-generated documentation of Qval’s code.

qval.qval

class qval.qval.QueryParamValidator(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request], factories: Dict[str, Optional[type]], validators: Optional[Dict[str, Union[qval.validator.Validator, Callable[Any, bool]]]] = None, box_all: bool = True)[source]

Bases: contextlib.AbstractContextManager

Validates query parameters.

Examples:
>>> r = fwk.DummyRequest({"num": "42", "s": "str", "double": "3.14"})
>>> params = QueryParamValidator(r, dict(num=int, s=None, double=float))
>>> with params as p:
...     print(p.num, p.s, p.double, sep=', ')
42, str, 3.14
__enter__()qval.utils.FrozenBox[source]

Runs validation on the provided request. See __exit__() for additional info.

Returns

box of validated values.

__exit__(exc_type, exc_val, exc_tb)[source]

If occurred exception is not an InvalidQueryParamException, the exception will be re-raised as an APIException, which will result in the 500 error on the client side.

Parameters
  • exc_type – exception type

  • exc_val – exception instance

  • exc_tb – exception traceback

Returns

None

__init__(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request], factories: Dict[str, Optional[type]], validators: Optional[Dict[str, Union[qval.validator.Validator, Callable[Any, bool]]]] = None, box_all: bool = True)[source]

Instantiates the query validator.

Parameters
  • request – fwk.Request instance

  • factories – a mapping of {param -> factory}. Providing None as a factory is equivalent to str or lambda x: x, since parameters are stored as strings.

  • validators – a dictionary of pre-defined validators

  • box_all – include all params, even if they’re not specified in factories

add_predicate(param: str, predicate: Callable[Any, bool])[source]

Adds a new check for the provided parameter.

Parameters
  • param – name of the request parameter

  • predicate – predicate function

Returns

None

apply_to_request(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request])qval.qval.QueryParamValidator[source]

Applies the current validation settings to a new request.

Example:
>>> from qval.utils import make_request
>>> request = make_request({"a": "77"})
>>> params = QueryParamValidator(request, {"a": int}, {"a": lambda x: x > 70})
>>> with params as p:
...     print(p.a)  # Prints 77
77
>>> with params.apply_to_request({"a": "10"}): pass  # Error!
Traceback (most recent call last):
    ...
qval.exceptions.InvalidQueryParamException: ...
Parameters

request – new request instance

Returns

new QueryParamValidator instance

check(param: str, predicate: Callable[Any, bool])qval.qval.QueryParamValidator[source]

Adds a new check for the provided parameter.

Parameters
  • param – name of the request parameter

  • predicate – predicate function

Returns

self

eq(param: str, value: Any, transform: Callable[Any, Any] = <function QueryParamValidator.<lambda>>)qval.qval.QueryParamValidator[source]

Adds an equality check for the provided parameter. For example, if value = 10, param will be tested as [transform(param) == 10].

Parameters
  • param – name of the request parameter

  • value – value to compare with

  • transform – callable that transforms the parameter, default: lambda x: x

Returns

self

gt(param: str, value: Any, transform: Callable[Any, Any] = <function QueryParamValidator.<lambda>>)qval.qval.QueryParamValidator[source]

Adds a greater than comparison check for provided parameter. For example, if value = 10, param will be tested as [transform(param) > 10].

Parameters
  • param – name of the request parameter

  • value – value to compare with

  • transform – callable that transforms the parameter, default: lambda x: x

Returns

self

lt(param: str, value: Any, transform: Callable[Any, Any] = <function QueryParamValidator.<lambda>>)qval.qval.QueryParamValidator[source]

Adds a less than comparison check for the provided parameter. For example, if value = 10, param will be tested as [transform(param) < 10].

Parameters
  • param – name of the request parameter

  • value – value to compare with

  • transform – callable that transforms the parameter, default: lambda x: x

Returns

self

nonzero(param: str, transform: Callable[Any, Any] = <function QueryParamValidator.<lambda>>)qval.qval.QueryParamValidator[source]

Adds a nonzero check for the provided parameter. For example, if value = 10, param will be tested as [transform(param) != 0].

Parameters
  • param – name of the request parameter

  • transform – callable that transforms the parameter, default: lambda x: x

Returns

self

positive(param: str, transform: Callable[Any, Any] = <function QueryParamValidator.<lambda>>)qval.qval.QueryParamValidator[source]

Adds a greater than zero comparison check for the provided parameter. Provided param will be tested as [transform(param) > 0].

Parameters
  • param – name of the request parameter

  • transform – callable that transforms the parameter, default: lambda x: x

Returns

self

property query_params: Dict[str, str]

Returns the dictionary of the query parameters.

qval.qval.qval(factories: Dict[str, Optional[Callable[str, Any]]], validators: Optional[Dict[str, Union[qval.validator.Validator, Callable[Any, bool]]]] = None, box_all: bool = True, request_: Optional[Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request]] = None)[source]

A decorator that validates query parameters. The wrapped function must accept a request as the first argument (or second if it’s a method), and params as last.

Parameters
  • factories – a mapping (parameter, callable [str -> Any])

  • validators – a mapping (parameter, validator)

  • box_all – include all parameters in the output dictionary, even if they’re not specified in factories

  • request – optional request object that will always be provided to the validator

Returns

wrapped function

qval.qval.qval_curry(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request])[source]

Curries qval() decorator and provides the given request object to the curried function on each call. This is especially handy in Flask, where request is global.

Example: .. code-block:: python

>>> r = {"num": "42", "s": "str", "double": "3.14"}
>>> qval = qval_curry(r)
>>> @qval({"num": int, "double": float}, None)
... def view(request, extra_param, params):
...     print(params.num, params.double, params.s, extra_param, sep=', ')
>>> view("test")
42, 3.14, str, test
Parameters

request – request instance

Returns

wrapped qval(..., request_=request)

qval.qval.validate(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request], validators: Optional[Dict[str, Union[qval.validator.Validator, Callable[Any, bool]]]] = None, box_all: bool = True, **factories: Optional[Callable[str, Any]])qval.qval.QueryParamValidator[source]

Shortcut for QueryParamValidator.

Examples:
>>> r = {"num": "42", "s": "str", "double": "3.14"}
>>> with validate(r, num=int, s=None, double=float) as p:
...     print(p.num + p.double, p.s)
45.14 str
>>> r = {"price": "43.5$", "n_items": "1"}
>>> currency2f = lambda x: float(x[:-1])
>>> params = validate(r, price=currency2f, n_items=int
...     ).positive("n_items")  # n_items must be greater than 0
>>> with params as p:
...     print(p.price, p.n_items)
43.5 1
Parameters
  • request – a request object

  • validators – a dictionary of validators

  • box_all – include all parameters in the output dictionary, even if they’re not specified in factories

  • factories – a dictionary of callables that create a python object from their parameter

Returns

QueryParamValidator instance

qval.validator

exception qval.validator.QvalValidationError[source]

Bases: Exception

The error raised if validation fails. This exception should be used to provide a custom validation error message to the client.

Example:
>>> from qval import validate
>>> def f(v: str) -> bool:
...     if not v.isnumeric():
...         raise QvalValidationError(f"Expected a number, got '{v}'")
...     return True
>>> params = validate({"number": "42"}, {"number": f})
>>> with params: pass  # OK
>>> with params.apply_to_request({"number": "a string"}): pass
Traceback (most recent call last):
    ...
qval.exceptions.InvalidQueryParamException: ...
class qval.validator.Validator(*predicates: Union[qval.validator.Validator, Callable[Any, bool]])[source]

Bases: object

Validates the given value using the provided predicates.

__call__(value: Any)bool[source]

Applies all stored predicates to the given value.

Parameters

value – value to validate

Returns

True if all checks have passed, False otherwise

Predicate = typing.Union[_ForwardRef('Validator'), typing.Callable[[typing.Any], bool]]
ValidatorType = typing.Union[_ForwardRef('Validator'), typing.Callable[[typing.Any], bool]]
__init__(*predicates: Union[qval.validator.Validator, Callable[Any, bool]])[source]

Instantiates the validator.

Parameters

predicates (Callable[[Any], bool]) – predefined predicates

add(predicate: Union[qval.validator.Validator, Callable[Any, bool]])qval.validator.Validator[source]

Adds the predicate to the list.

Parameters

predicate – predicate function

Returns

self

qval.exceptions

exception qval.exceptions.InvalidQueryParamException(detail: Union[dict, str], status: int)[source]

Bases: rest_framework.exceptions.APIException

An error thrown when a parameter fails its validation.

__init__(detail: Union[dict, str], status: int)[source]

Instantiates the exception.

Parameters
  • detail – dict or string with the details

  • status – status code

qval.utils

class qval.utils.ExcLogger[source]

Bases: object

A class used to report critical errors.

>>> from qval.utils import log
>>> log
ExcLogger()
>>> log.is_enabled
True
>>> log.disable()
>>> print(log)
ExcLogger<<Logger qval (WARNING)>>, enabled = false>
__init__()[source]

Instantiates the logger.

Parameters

logger – a list of loggers

disable()[source]

Disables logging.

Returns

None

enable()[source]

Enables logging.

Returns

None

error(*args, **kwargs)[source]

A shortcut for log("error", ...).

Parameters
  • args – log args

  • kwargs – log kwargs

Returns

None

property is_enabled: bool

Returns True if logging is enabled.

log(level: str, *args, **kwargs)[source]

Logs a new error message on the given level if logging is enabled.

Parameters
  • args – logger args

  • kwargs – logger kwargs

Returns

None

class qval.utils.FrozenBox(dct: Dict[Any, Any])[source]

Bases: object

A frozen dictionary that allows accessing its elements with .

Example:
>>> box = FrozenBox({"num": 10, "s": "string"})
>>> print(box.num, box.s)
10 string
>>> box["num"] = 404
Traceback (most recent call last):
    ...
TypeError: 'FrozenBox' object does not support item assignment
>>> box.num = 404
Traceback (most recent call last):
    ...
TypeError: 'FrozenBox' object does not support attribute assignment
>>> box.num
10
__init__(dct: Dict[Any, Any])[source]
Parameters

dct – the dict to store

qval.utils.dummify(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request])qval.framework_integration.DummyRequest[source]

Constructs a qval.framework_integration.DummyRequest with the parameters in the given request.

Parameters

request – any supported request

Returns

DummyRequest(request.<params>)

qval.utils.get_request_params(request: (<class 'dict'>, <class 'qval.framework_integration.DummyRequest'>, <class 'rest_framework.request.Request'>, <class 'django.http.request.HttpRequest'>, <class 'flask.wrappers.Request'>, <class 'falcon.request.Request'>))[source]

Returns a dictionary of the query parameters in the given request.

Parameters

request – any supported request

Returns

dictionary of parameters

qval.utils.make_request(request: Union[dict, qval.framework_integration.DummyRequest, rest_framework.request.Request, django.http.request.HttpRequest, flask.wrappers.Request, falcon.request.Request]) -> (<class 'dict'>, <class 'qval.framework_integration.DummyRequest'>, <class 'rest_framework.request.Request'>, <class 'django.http.request.HttpRequest'>, <class 'flask.wrappers.Request'>, <class 'falcon.request.Request'>)[source]

Creates a qval.framework_integration.DummyRequest if request is a dictionary, and returns the request itself otherwise.

The behavior of this function can be customized with the @_make_request() decorator. Provide the path to your wrapper using QVAL_MAKE_REQUEST_WRAPPER in the settings file or set it as an environment variable. The wrapper function must accept request as its first argument and return an object that implements the request interface.

For example, the following code adds print each incoming request:

# settings.py
QVAL_MAKE_REQUEST_WRAPPER = "app.utils.my_wrapper"

# app/utils.py
def my_wrapper(f):
    @functools.wraps(f)
    def wrapper(request):
        print(f"Received new request: {request}")
        return f(request)
    return wrapper
Parameters

request – dict or request instance

Returns

request

qval.framework_integration

class qval.framework_integration.DummyRequest(params: Dict[str, str])[source]

Bases: object

DummyRequest. Used for compatibility with the supported frameworks.

__init__(params: Dict[str, str])[source]

Initialize self. See help(type(self)) for accurate signature.

property query_params: Dict[str, str]

More semantically correct name for request.GET.

class qval.framework_integration.HandleAPIExceptionDjango(get_response)[source]

Bases: object

__init__(get_response)[source]

Initialize self. See help(type(self)) for accurate signature.

process_exception(_: django.http.request.HttpRequest, exception: Exception)[source]
qval.framework_integration.get_module()Union[qval.framework_integration._EnvironSettings, Module][source]

Attempts to load the settings module. If none of the supported env variables are defined, returns _EnvironSettings() object.

qval.framework_integration.load_symbol(path: object)[source]

Imports an object using the given path.

Parameters

path – path to an object, e.g. my.module.func_1

Returns

loaded symbol

qval.framework_integration.setup_django_middleware(module: Module = None)[source]

Setups the exception-handling middleware.

Parameters

module – settings module

Returns

None

qval.framework_integration.setup_falcon_error_handlers(api: falcon.API)[source]

Setups the error handler for APIException.

Parameters

api – falcon.API

Returns

qval.framework_integration.setup_flask_error_handlers(app: flask.Flask)[source]

Setups the error handler for APIException.

Parameters

app – flask app

Returns

None