Validate configuration and produce human readable error messages.

# 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(
schema=MY_SCHEMA,
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 messages
• id_key: will be used to identify the object in error messages. Set to None if there is no identifying key for the object.
• items: validator objects such as Required or Optional

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: A Map / Array or other sub-schema.
• allow_empty: when False, 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.

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 check
• self.check_fn: the check function
• self.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 the default if it is not present.
• remove_defaults will remove the value if it is equal to default.

### OptionalRecurse(key, schema, default)

Similar to Optional but uses a schema.

• apply_defaults will set the default 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 to default.

### 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 the condition_value, the specific key will be checked using the check function.
• If ensure_absent is True and the condition check fails, the key 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

Uploaded source
Uploaded py2 py3