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-3.1.0.tar.gz (7.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cfgv-3.1.0-py2.py3-none-any.whl (7.3 kB view details)

Uploaded Python 2Python 3

File details

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

File metadata

  • Download URL: cfgv-3.1.0.tar.gz
  • Upload date:
  • Size: 7.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.6.9

File hashes

Hashes for cfgv-3.1.0.tar.gz
Algorithm Hash digest
SHA256 c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513
MD5 bb29b3a129e8a103630bd54fa1905b79
BLAKE2b-256 88e8159f95e454d6c35d2fc238ca056497672b8d93c3dab568a9b13e7a602331

See more details on using hashes here.

File details

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

File metadata

  • Download URL: cfgv-3.1.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 7.3 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.6.9

File hashes

Hashes for cfgv-3.1.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53
MD5 ff4552f5f211b363ab63ca318181949a
BLAKE2b-256 6d8249913e721128ff16d6b7cf304f513de7bba698583b045dfb9c4b3bb2f467

See more details on using hashes here.

Supported by

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