"""
Validate a configuration value by converting it to a specific type.
These functions are used by :mod:`staticconf.readers` and
:mod:`staticconf.schema` to coerce config values to a type.
"""
import datetime
import logging
import re
import time
import six
from staticconf.errors import ValidationError
[docs]def validate_string(value):
return None if value is None else six.text_type(value)
[docs]def validate_bool(value):
return None if value is None else bool(value)
[docs]def validate_numeric(type_func, value):
try:
return type_func(value)
except ValueError:
raise ValidationError("Invalid %s: %s" % (type_func.__name__, value))
[docs]def validate_int(value):
return validate_numeric(int, value)
[docs]def validate_float(value):
return validate_numeric(float, value)
date_formats = [
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %I:%M:%S %p",
"%Y-%m-%d",
"%y-%m-%d",
"%m/%d/%y",
"%m/%d/%Y",
]
[docs]def validate_datetime(value):
if isinstance(value, datetime.datetime):
return value
for format_ in date_formats:
try:
return datetime.datetime.strptime(value, format_)
except ValueError:
pass
raise ValidationError("Invalid date format: %s" % value)
[docs]def validate_date(value):
if isinstance(value, datetime.date):
return value
return validate_datetime(value).date()
time_formats = [
"%I %p",
"%H:%M",
"%I:%M %p",
"%H:%M:%S",
"%I:%M:%S %p"
]
[docs]def validate_time(value):
if isinstance(value, datetime.time):
return value
for format_ in time_formats:
try:
return datetime.time(*time.strptime(value, format_)[3:6])
except ValueError:
pass
raise ValidationError("Invalid time format: %s" % value)
def _validate_iterable(iterable_type, value):
"""Convert the iterable to iterable_type, or raise a Configuration
exception.
"""
if isinstance(value, six.string_types):
msg = "Invalid iterable of type(%s): %s"
raise ValidationError(msg % (type(value), value))
try:
return iterable_type(value)
except TypeError:
raise ValidationError("Invalid iterable: %s" % (value))
[docs]def validate_list(value):
return _validate_iterable(list, value)
[docs]def validate_set(value):
return _validate_iterable(set, value)
[docs]def validate_tuple(value):
return _validate_iterable(tuple, value)
[docs]def validate_regex(value):
try:
return re.compile(value)
except (re.error, TypeError) as e:
raise ValidationError("Invalid regex: %s, %s" % (e, value))
[docs]def build_list_type_validator(item_validator):
"""Return a function which validates that the value is a list of items
which are validated using item_validator.
"""
def validate_list_of_type(value):
return [item_validator(item) for item in validate_list(value)]
return validate_list_of_type
[docs]def build_map_type_validator(item_validator):
"""Return a function which validates that the value is a mapping of
items. The function should return pairs of items that will be
passed to the `dict` constructor.
"""
def validate_mapping(value):
return dict(item_validator(item) for item in validate_list(value))
return validate_mapping
[docs]def validate_log_level(value):
"""Validate a log level from a string value. Returns a constant from
the :mod:`logging` module.
"""
try:
return getattr(logging, value)
except AttributeError:
raise ValidationError("Unknown log level: %s" % value)
[docs]def validate_any(value):
return value
validators = {
'': validate_any,
'bool': validate_bool,
'date': validate_date,
'datetime': validate_datetime,
'float': validate_float,
'int': validate_int,
'list': validate_list,
'set': validate_set,
'string': validate_string,
'time': validate_time,
'tuple': validate_tuple,
'regex': validate_regex,
'log_level': validate_log_level,
}
[docs]def get_validators():
"""Return an iterator of (validator_name, validator) pairs."""
return six.iteritems(validators)