Validate configuration and produce human readable error messages.
Project description
cfgv
Validate configuration and produce human readable error messages.
Installation
pip install cfgv
Sample error messages
These are easier to see by example. Here's an example where I typo'd true
in a pre-commit configuration.
pre_commit.clientlib.InvalidConfigError:
==> File /home/asottile/workspace/pre-commit/.pre-commit-config.yaml
==> At Config()
==> At key: repos
==> At Repository(repo='https://github.com/pre-commit/pre-commit-hooks')
==> At key: hooks
==> At Hook(id='flake8')
==> At key: always_run
=====> Expected bool got str
API
cfgv.validate(value, schema)
Perform validation on the schema:
- raises
ValidationError
on failure - returns the value on success (for convenience)
cfgv.apply_defaults(value, schema)
Returns a new value which sets all missing optional values to their defaults.
cfgv.remove_defaults(value, schema)
Returns a new value which removes all optional values that are set to their defaults.
cfgv.load_from_filename(filename, schema, load_strategy, exc_tp=ValidationError)
Load a file given the load_strategy
. Reraise any errors as exc_tp
. All
defaults will be populated in the resulting value.
Most useful when used with functools.partial
as follows:
load_my_cfg = functools.partial(
cfgv.load_from_filename,
schema=MY_SCHEMA,
load_strategy=json.loads,
exc_tp=MyError,
)
Making a schema
A schema validates a container -- cfgv
provides Map
and Array
for
most normal cases.
writing your own schema container
If the built-in containers below don't quite satisfy your usecase, you can always write your own. Containers use the following interface:
class Container(object):
def check(self, v):
"""check the passed in value (do not modify `v`)"""
def apply_defaults(self, v):
"""return a new value with defaults applied (do not modify `v`)"""
def remove_defaults(self, v):
"""return a new value with defaults removed (do not modify `v`)"""
Map(object_name, id_key, *items)
The most basic building block for creating a schema is a Map
object_name
: will be displayed in error messagesid_key
: will be used to identify the object in error messages. Set toNone
if there is no identifying key for the object.items
: validator objects such asRequired
orOptional
Consider the following schema:
Map(
'Repo', 'url',
Required('url', check_any),
)
In an error message, the map may be displayed as:
Repo(url='https://github.com/pre-commit/pre-commit')
Repo(url=MISSING)
(if the key is not present)
Array(of, allow_empty=True)
Used to nest maps inside of arrays. For arrays of scalars, see check_array
.
of
: AMap
/Array
or other sub-schema.allow_empty
: whenFalse
,Array
will ensure at least one element.
When validated, this will check that each element adheres to the sub-schema.
Validator objects
Validator objects are used to validate key-value-pairs of a Map
.
writing your own validator
If the built-in validators below don't quite satisfy your usecase, you can always write your own. Validators use the following interface:
class Validator(object):
def check(self, dct):
"""check that your specific key has the appropriate value in `dct`"""
def apply_default(self, dct):
"""modify `dct` and set the default value if it is missing"""
def remove_default(self, dct):
"""modify `dct` and remove the default value if it is present"""
It may make sense to borrow functions from the built in validators. They additionally use the following interface(s):
self.key
: the key to checkself.check_fn
: the check functionself.default
: a default value to set.
Required(key, check_fn)
Ensure that a key is present in a Map
and adheres to the
check function.
RequiredRecurse(key, schema)
Similar to Required
, but uses a schema.
Optional(key, check_fn, default)
If a key is present, check that it adheres to the check function.
apply_defaults
will set thedefault
if it is not present.remove_defaults
will remove the value if it is equal todefault
.
OptionalRecurse(key, schema, default)
Similar to Optional
but uses a schema.
apply_defaults
will set thedefault
if it is not present and then validate it with the schema.remove_defaults
will remove defaults using the schema, and then remove the value it if it is equal todefault
.
OptionalNoDefault(key, check_fn)
Like Optional
, but does not apply_defaults
or remove_defaults
.
Conditional(key, check_fn, condition_key, condition_value, ensure_absent=False)
- If
condition_key
is equal to thecondition_value
, the specifickey
will be checked using the check function. - If
ensure_absent
isTrue
and the condition check fails, thekey
will be checked for absense.
Note that the condition_value
is checked for equality, so any object
implementing __eq__
may be used. A few are provided out of the box
for this purpose, see equality helpers.
ConditionalOptional(key, check_fn, default, condition_key, condition_value, ensure_absent=False)
Similar to Conditional
and Optional
.
ConditionalRecurse(key, schema, condition_key, condition_value, ensure_absent=True)
Similar to Conditional
, but uses a schema.
NoAdditionalKeys(keys)
Use in a mapping to ensure that only the keys
specified are present.
Equality helpers
Equality helpers at the very least implement __eq__
for their behaviour.
They may also implement def describe_opposite(self):
for use in the
ensure_absent=True
error message (otherwise, the __repr__
will be used).
Not(val)
Returns True
if the value is not equal to val
.
In(*values)
Returns True
if the value is contained in values
.
NotIn(*values)
Returns True
if the value is not contained in values
.
Check functions
A number of check functions are provided out of the box.
A check function takes a single parameter, the value
, and either raises a
ValidationError
or returns nothing.
check_any(_)
A noop check function.
check_type(tp, typename=None)
Returns a check function to check for a specific type. Setting typename
will replace the type's name in the error message.
For example:
Required('key', check_type(int))
# 'Expected bytes' in both python2 and python3.
Required('key', check_type(bytes, typename='bytes'))
Several type checking functions are provided out of the box:
check_bool
check_bytes
check_int
check_string
check_text
check_one_of(possible)
Returns a function that checks that the value is contained in possible
.
For example:
Required('language', check_one_of(('javascript', 'python', 'ruby')))
check_regex(v)
Ensures that v
is a valid python regular expression.
check_array(inner_check)
Returns a function that checks that a value is a sequence and that each
value in that sequence adheres to the inner_check
.
For example:
Required('args', check_array(check_string))
check_and(*fns)
Returns a function that performs multiple checks on a value.
For example:
Required('language', check_and(check_string, my_check_language))
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.