PyCOM 0.6.0 documentation

Source code for pycom.ext.checks

#   PyCOM - Distributed Component Model for Python
#   Copyright (c) 2011-2012, Dmitry "Divius" Tantsur
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are
#   met:
#
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above
#     copyright notice, this list of conditions and the following disclaimer
#     in the documentation and/or other materials provided with the
#     distribution.
#
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Checks for services."""

import pycom


try:
    callable
except NameError:  # pragma: no cover
    # py3k < 3.2 sucks
    callable = lambda x: hasattr(x, '__call__')  # pylint: disable-msg=W0622


[docs]def check_argument(name, valid_types=None, validator=None, error_message=None): """A decorator for checking types of incoming arguments. *name* is a name of argument to check. *valid_types* is a tuple with allowed types or just a single type. *validator* is a callable that takes 2 arguments (argument name and argument value), positive result designate valid value, negative result or raising `TypeError` or `ValueError` designate invalid value (error message is taken from the exception). *error_message* overrides default error message. Example:: import pycom from pycom.ext import checks @pycom.interface("com.example.interface.foo") class SimpleInterface(pycom.Service): @checks.check_argument("arg1", valid_types=str, validator=lambda name, value: len(value) > 0) @checks.check_argument("arg2", valid_types=(int, type(None)), validator=(lambda name, value: value is None or value > 0), error_mesage="Required positive integer or None for arg2") @pycom.method def my_method(self, request, arg1, arg2): pass This decorator can be used any number of times, but should be placed before :func:`pycom.method`. :exc:`pycom.BadRequest` is raised on failed checks. """ assert valid_types is None or isinstance(valid_types, (tuple, type)) assert validator is None or callable(validator) def _checker(iface, method, request): """This prehook checks incoming data.""" if name not in request.args: # We do NOT check presence! return request value = request.args[name] try: if valid_types is not None: if not isinstance(value, valid_types): raise TypeError( "Invalid type for argument '%s' of '%s.%s', " "expected '%s', got '%s'" % ( name, iface.name, method.name, valid_types, type(value) )) if validator is not None: if not validator(name, value): raise ValueError( "Validation failed for argument '%s' of '%s.%s'" % ( name, iface.name, method.name )) except (ValueError, TypeError) as err: if error_message is not None: raise pycom.BadRequest(error_message) else: raise pycom.BadRequest(str(err)) return request def _wrapper(wrapped): """This wrapper is called on method creation by decorator.""" wrapped.__method__.prehooks.append(_checker) return wrapped return _wrapper