Skip to main content

Validate configuration and produce human readable error messages.

Project description

Build Status Azure DevOps coverage

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 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.

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 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cfgv-2.0.0.tar.gz (8.7 kB view details)

Uploaded Source

Built Distribution

cfgv-2.0.0-py2.py3-none-any.whl (7.4 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file cfgv-2.0.0.tar.gz.

File metadata

  • Download URL: cfgv-2.0.0.tar.gz
  • Upload date:
  • Size: 8.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.7

File hashes

Hashes for cfgv-2.0.0.tar.gz
Algorithm Hash digest
SHA256 32edbe09de6f4521224b87822103a8c16a614d31a894735f7a5b3bcf0eb3c37e
MD5 d542632243da5c422c5b9a92a1861e29
BLAKE2b-256 ac5aa43aaca1023c47d51e1ff954624be5863f213c0da8141f3e5d0612b82508

See more details on using hashes here.

Provenance

File details

Details for the file cfgv-2.0.0-py2.py3-none-any.whl.

File metadata

  • Download URL: cfgv-2.0.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 7.4 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.7

File hashes

Hashes for cfgv-2.0.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 3bd31385cd2bebddbba8012200aaf15aa208539f1b33973759b4d02fc2148da5
MD5 b11e78a921d52de72bc7429cb6b5403f
BLAKE2b-256 2fec3c0a56fbc00e6b649c1dc809dc3f12c5796fbfb7940d1167b9bddc67b818

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page