# coding=utf-8
"""Collection of assertions for validation.
.. moduleauthor:: Torbjörn Klatt <[email protected]>
Examples
--------
>>> a = 3
>>> b = 2
>>> assert_condition(3 > 2, ValueError, "3 should be larger 2")
>>> func = lambda x: x
>>> assert_is_callable(func, "A lambda expression is callable.")
>>> l = [1, 2, 4]
>>> assert_is_in(2, l, "2 is in the list")
>>> m = {'hello': 'world'}
>>> assert_is_key('hello', m, "'hello' is a key")
>>> a = 1
>>> assert_is_instance(a, int, "'a' is an integer")
"""
import inspect
from collections import Callable
from pypint.utilities.tracing import checking_obj_name, class_name, func_name
from pypint.utilities.logging import LOG
[docs]def assert_condition(condition, exception_type, message, checking_obj=None):
"""Asserts trueness of arbitrary condition
Parameters
----------
condition : :py:class:`bool` or ``boolean expression``
expression to be asserted
exception_type : :py:class:`Exception`
type of exception to be raised if ``condition`` evaluates to :py:class:`False`
message : :py:class:`str`
message content of exception raised
checking_obj : :py:class:`object` or :py:class:`None`
The exception will be raised in the scope of the given object.
If :py:class:`None` (default) no scope will be displayed.
Raises
------
exception_type
if ``condition`` evaluates to :py:class:`False`
"""
if not condition:
LOG.critical(func_name(checking_obj) + message)
raise exception_type("{:s}.{:s}(): {:s}"
.format(checking_obj_name(checking_obj), inspect.stack()[1][3], message))
[docs]def assert_is_callable(obj, message=None, descriptor=None, checking_obj=None):
"""Asserts callability of given object
Parameters
----------
obj : :py:class:`object`
object to be asserted callable
message : :py:class:`str` or :py:class:`None`
*(optional)*
checking_obj : :py:class:`object` or :py:class:`None`
*(optional)*
The exception will be raised in the scope of the given object.
If :py:class:`None` (default) no scope will be displayed.
Raises
------
ValueError
if ``obj`` is not :py:class:`Callable`
"""
if not isinstance(obj, Callable):
if not message:
if descriptor:
message = "{:s} must be callable.".format(descriptor)
else:
message = "Required a callable: NOT {:s}.".format(class_name(obj))
LOG.critical(func_name(checking_obj) + message)
raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
[docs]def assert_is_in(element, test_list, message=None, elem_desc=None, list_desc=None, checking_obj=None):
"""Asserts element in list or sequence
Parameters
----------
element : :py:class:`object`
element to check membership
test_list : ``Sequence``
sequence to check
message : :py:class:`str`
*(optional)*
message content of exception raised
checking_obj : :py:class:`object` or :py:class:`None`
*(optional)*
The exception will be raised in the scope of the given object.
If :py:class:`None` (default) no scope will be displayed.
Raises
------
ValueError
if ``element`` is not in ``test_list``
Examples
--------
assert_is_in(True, bool_list, elem_desc='', list_desc='', checking_obj=self)
"""
if element not in test_list:
if not message:
if not list_desc:
list_desc = class_name(test_list)
if not elem_desc:
elem_desc = "Element {:r}".format(element)
message = "{:s} is not in {:s}.".format(elem_desc, list_desc)
LOG.critical(func_name(checking_obj) + message)
LOG.debug(func_name(checking_obj) +
"Elements in {:s}: {:s}".format(class_name(test_list), ', '.join(test_list)))
raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
[docs]def assert_is_instance(obj, instances, message=None, descriptor=None, checking_obj=None):
"""Asserts element is of certain type
Parameters
----------
obj : :py:class:`object`
object to check type
instances : :py:class:`type` of ``classes`` or :py:class:`class`
types to check
message : :py:class:`str`
*(optional)*
message content of exception raised
checking_obj : :py:class:`object` or :py:class:`None`
*(optional)*
The exception will be raised in the scope of the given object.
If :py:class:`None` (default) no scope will be displayed.
Raises
------
ValueError
if ``obj`` is not of type ``instances``
"""
if not isinstance(obj, instances):
if not message:
_instances_str = set()
if isinstance(instances, (tuple, set, frozenset)):
for _i in instances:
_instances_str.add("'%s'" % _i.__name__)
else:
_instances_str.add("'%s'" % instances.__name__)
if descriptor:
if len(_instances_str) > 1:
message = "{:s} must be one of {:s}: NOT {:s}"\
.format(descriptor, ', '.join(_instances_str), class_name(obj))
else:
message = "{:s} must be a {:s}: NOT {:s}"\
.format(descriptor, ', '.join(_instances_str), class_name(obj))
else:
if len(_instances_str) > 1:
message = "Required one of {:s}: NOT {:s}.".format(', '.join(_instances_str), class_name(obj))
else:
message = "Required a {:s}: NOT {:s}.".format(', '.join(_instances_str), class_name(obj))
LOG.critical(func_name(checking_obj) + message)
raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
[docs]def assert_is_key(key, dictionary, message=None, key_desc=None, dict_desc=None, checking_obj=None):
"""Asserts dictionary has a certain key
Parameters
----------
key : ``key``
key to check
dictionary : :py:class:`dict`
dictionary to check
message : :py:class:`str`
*(optional)*
message content of exception raised
checking_obj : :py:class:`object` or :py:class:`None`
*(optional)*
The exception will be raised in the scope of the given object.
If :py:class:`None` (default) no scope will be displayed.
Raises
------
ValueError
if ``key`` is not of a key in ``dictionary``
"""
if key not in dictionary:
if not message:
if not key_desc:
key_desc = "'{:s}'".format(key)
if not dict_desc:
dict_desc = "given dict"
message = "{:s} is not a key in {:s}.".format(key_desc, dict_desc)
LOG.critical(func_name(checking_obj) + message)
LOG.debug(func_name(checking_obj) + "Keys in {:s}: {:s}".format(id(dictionary), ', '.join(dictionary.keys())))
raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
def assert_named_argument(name, kwargs, types=None, message=None, descriptor=None, checking_obj=None):
if name not in kwargs:
if not message:
if descriptor:
message = "%s ('%s') is a required argument." % (descriptor, name)
else:
message = "'%s' is a required argument." % name
LOG.critical(func_name(checking_obj) + message)
LOG.debug(func_name(checking_obj) + "Named arguments were: %s" % ', '.join(kwargs.keys()))
raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
if types:
assert_is_instance(kwargs[name], types, descriptor=descriptor, checking_obj=checking_obj)
__all__ = [
'assert_condition',
'assert_is_in',
'assert_is_instance',
'assert_is_callable',
'assert_is_key',
'assert_named_argument'
]