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: Dict[str, Union[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 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: Dict[str, Union[Validator, Callable[Any, bool]]] = None, box_all: bool = True)[source]

Instantiates the query validator.

Parameters
  • request – fwk.Request instance

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

  • validators – 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 the 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 the 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 the 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 the 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 the 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

Returns the dictionary of query parameters.

qval.qval.qval(factories: Dict[str, Optional[Callable[str, Any]]], validators: Dict[str, Union[Validator, Callable[Any, bool]]] = None, box_all: bool = True, request_: 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 – mapping (parameter, callable [str -> Any])

  • validators – mapping (parameter, validator)

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

  • request – optional request object will be always 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 given request object 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: Dict[str, Union[Validator, Callable[Any, bool]]] = None, box_all: bool = True, **factories) → 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 – request instance

  • validators – dictionary of predefined validators

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

  • factories – factories that create python object from string parameters

Returns

QueryParamValidator instance

qval.validator

exception qval.validator.QvalValidationError[source]

Bases: Exception

An 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[Validator, Callable[Any, bool]])[source]

Bases: object

Validates the given value using 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[Validator, Callable[Any, bool]])[source]

Instantiates the validator.

Parameters

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

add(predicate: Union[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 param fails the validation.

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

Instantiates the exception.

Parameters
  • detail – dict or string with details

  • status – status code

qval.utils

class qval.utils.ExcLogger(logger_factories: List[Callable[str, Any]])[source]

Bases: object

A class used to report critical errors.

>>> from qval.utils import log
>>> log
ExcLogger([getLogger])
>>> log.is_enabled
True
>>> log.disable()
>>> print(log)
ExcLogger<[getLogger], enabled = false>
__init__(logger_factories: List[Callable[str, Any]])[source]

Instantiates the logger.

Parameters

logger_factories – list of logger factories

add_logger(log_factory: Callable[str, Any])[source]

Adds new logger factory to the list.

Parameters

log_factory – logger

Returns

None

clear()[source]

Removes all saved factories.

Returns

None

static collect_loggers() → list[source]

Looks for configuration and returns a list of detected loggers or logging.getLogger().

Returns

list of collected loggers

classmethod detect_loggers(silent: bool = False) → qval.utils.ExcLogger[source]

Looks for configuration and instantiates ExcLogger with the detected loggers or default logging.getLogger().

Parameters

silent – omit logging test message

Returns

ExcLogger object

disable()[source]

Disables logging.

Returns

None

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

Instantiates new loggers using configured factories and provides *args and **kwargs to the built objects.

Parameters
  • name – logger name

  • level – logging level. If a built object has no attribute level, it will be treated as callable.

  • args – logger args

  • kwargs – logger kwargs

Returns

None

enable()[source]

Enables logging.

Returns

None

error(name: str, *args, **kwargs)[source]

Shortcut for dump(name, "error", ...).

Parameters
  • name – logger name

  • args – logger args

  • kwargs – logger kwargs

Returns

None

property is_enabled

Returns True if logging is enabled.

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

Bases: object

Frozen dictionary that allows accessing elements by .

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 – dict to store

qval.utils.dummyfy(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 qval.framework_integration.DummyRequest with params of 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 query parameters of 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 qval.framework_integration.DummyRequest if request is a dictionary, and returns the request itself otherwise.

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 a parameter and return an object that implements the request interface.

For example, the following code adds print to each function call:

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

Then execute export QVAL_MAKE_REQUEST_WRAPPER=app.utils.my_wrapper in your console or simply add it to the config file.

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 supported frameworks.

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

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

property query_params

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 settings module. If none of the supported env variables are defined, returns _EnvironSettings() object.

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

Imports 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 hanlding 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