Module gvalidate.generic
Includes the following generic decorator functions:
- validate
View Source
"""
Includes the following *generic* decorator functions:
- validate
"""
from functools import wraps
from inspect import signature
from typing import Tuple, Union
from warnings import warn
def validate(
validator: callable,
argument_names: Union[Tuple[str], str] = (),
message: str = "",
error_type: type = ValueError,
enable_warnings=True,
) -> None:
"""
Generic validator function that raises an exception if any positional
arguments specified by an integer in the tuple `argument_names`
or any keyword arguments
specified by a string are not valid.
- validator: A function that must return `False` if the argument is
invalid.
- argument_names: A tuple containing the names of
all arguments that need to be validated or a string.
- message: Optional string that will be appended to the error message.
- error_type: Optional parameter used to specify the type of error raised.
- enable_warnings: Set to `True` to generate a warning if any entry
of `argument_names` is not a valid argument name.
Note: Skips arguments that are absent (since an error will be
thrown implicitly when the decorated function is called.)
If the user supplied validator function raises an exception,
validation will fail and relevant information is appended
to the error message before raising the error using a `from` construct.
---
Usage: In the example below the decorator checks if the argument
`width` is larger than zero.
``` python
@validate(
validator=lambda input: input > 0,
argument_names = ('width',),
message='Must be larger than zero.',
)
def my_func(length, width):
pass
my_func(10, -2)
#Stacktrace will be printed ...
ValueError: Invalid argument: width = -2. Must be larger than zero.
```
"""
if isinstance(argument_names, str):
argument_names = (argument_names,)
def _validate(func):
@wraps(func)
def __validate(*args, **kwargs):
# Map args onto kwargs:
all_argument_names = tuple(signature(func).parameters.keys())
mapped_kwargs = kwargs.copy()
for index, arg_value in enumerate(args):
mapped_kwargs[all_argument_names[index]] = arg_value
def _argument_validation(current, arg_name: str):
"""
Calls the validator, generates info, raises exception
on validation failure.
"""
validation_error = None
try:
valid = validator(current)
info = (
f"Invalid argument in function {func.__name__}: "
+ f"{arg_name} = {current}."
)
if not valid:
# Exception raised after validation failed.
validation_error = error_type(
info + " " + str(message)
)
raise validation_error
except Exception as error:
# Check if exception was raised already
if error == validation_error:
raise
# Attach message and raise again.
info = (
"Exception raised while validating argument in "
+ f"{func.__name__}: {arg_name} = {current}."
)
error.args = (info + " " + str(message), *error.args)
raise error_type from error
# Validate all entries if argument_names is empty.
if not argument_names:
for arg_name in mapped_kwargs.keys():
current = mapped_kwargs[arg_name]
_argument_validation(current, arg_name)
# Validation
for arg_name in argument_names:
if arg_name in mapped_kwargs:
current = mapped_kwargs[arg_name]
_argument_validation(current, arg_name)
else:
if enable_warnings:
warn(
"Warning: {} is not a valid argument name. "
"Check the decorators of {}.".format(
arg_name, func.__name__
)
)
return func(*args, **kwargs)
return __validate
return _validate
Functions
validate
def validate(
validator: <built-in function callable>,
argument_names: Union[Tuple[str], str] = (),
message: str = '',
error_type: type = <class 'ValueError'>,
enable_warnings=True
) -> None
Generic validator function that raises an exception if any positional
arguments specified by an integer in the tuple argument_names
or any keyword arguments
specified by a string are not valid.
- validator: A function that must return
False
if the argument is invalid. - argument_names: A tuple containing the names of all arguments that need to be validated or a string.
- message: Optional string that will be appended to the error message.
- error_type: Optional parameter used to specify the type of error raised.
- enable_warnings: Set to
True
to generate a warning if any entry ofargument_names
is not a valid argument name.
Note: Skips arguments that are absent (since an error will be thrown implicitly when the decorated function is called.)
If the user supplied validator function raises an exception,
validation will fail and relevant information is appended
to the error message before raising the error using a from
construct.
Usage: In the example below the decorator checks if the argument
width
is larger than zero.
@validate(
validator=lambda input: input > 0,
argument_names = ('width',),
message='Must be larger than zero.',
)
def my_func(length, width):
pass
my_func(10, -2)
#Stacktrace will be printed ...
ValueError: Invalid argument: width = -2. Must be larger than zero.
View Source
def validate(
validator: callable,
argument_names: Union[Tuple[str], str] = (),
message: str = "",
error_type: type = ValueError,
enable_warnings=True,
) -> None:
"""
Generic validator function that raises an exception if any positional
arguments specified by an integer in the tuple `argument_names`
or any keyword arguments
specified by a string are not valid.
- validator: A function that must return `False` if the argument is
invalid.
- argument_names: A tuple containing the names of
all arguments that need to be validated or a string.
- message: Optional string that will be appended to the error message.
- error_type: Optional parameter used to specify the type of error raised.
- enable_warnings: Set to `True` to generate a warning if any entry
of `argument_names` is not a valid argument name.
Note: Skips arguments that are absent (since an error will be
thrown implicitly when the decorated function is called.)
If the user supplied validator function raises an exception,
validation will fail and relevant information is appended
to the error message before raising the error using a `from` construct.
---
Usage: In the example below the decorator checks if the argument
`width` is larger than zero.
``` python
@validate(
validator=lambda input: input > 0,
argument_names = ('width',),
message='Must be larger than zero.',
)
def my_func(length, width):
pass
my_func(10, -2)
#Stacktrace will be printed ...
ValueError: Invalid argument: width = -2. Must be larger than zero.
```
"""
if isinstance(argument_names, str):
argument_names = (argument_names,)
def _validate(func):
@wraps(func)
def __validate(*args, **kwargs):
# Map args onto kwargs:
all_argument_names = tuple(signature(func).parameters.keys())
mapped_kwargs = kwargs.copy()
for index, arg_value in enumerate(args):
mapped_kwargs[all_argument_names[index]] = arg_value
def _argument_validation(current, arg_name: str):
"""
Calls the validator, generates info, raises exception
on validation failure.
"""
validation_error = None
try:
valid = validator(current)
info = (
f"Invalid argument in function {func.__name__}: "
+ f"{arg_name} = {current}."
)
if not valid:
# Exception raised after validation failed.
validation_error = error_type(
info + " " + str(message)
)
raise validation_error
except Exception as error:
# Check if exception was raised already
if error == validation_error:
raise
# Attach message and raise again.
info = (
"Exception raised while validating argument in "
+ f"{func.__name__}: {arg_name} = {current}."
)
error.args = (info + " " + str(message), *error.args)
raise error_type from error
# Validate all entries if argument_names is empty.
if not argument_names:
for arg_name in mapped_kwargs.keys():
current = mapped_kwargs[arg_name]
_argument_validation(current, arg_name)
# Validation
for arg_name in argument_names:
if arg_name in mapped_kwargs:
current = mapped_kwargs[arg_name]
_argument_validation(current, arg_name)
else:
if enable_warnings:
warn(
"Warning: {} is not a valid argument name. "
"Check the decorators of {}.".format(
arg_name, func.__name__
)
)
return func(*args, **kwargs)
return __validate
return _validate