Extensions
==========

It is possible to extend the validation of each of the three basic types, ``map`` & ``seq`` & ``scalar``.

Extensions can be used to do more complex validation that is not natively supported by the core pykwalify lib.



Loading extensions
------------------

There are 2 ways to load extensions into a schema.

First you can specify any ``*.py`` file via the cli via the ``-e FILE`` or ``--extension FILE`` flag. If you would do this when using pykwalify as a library you should pass in a list of files to the ``extensions`` variable to the ``Core`` class.

The second way is to specify a list of files in the keyword ``extensions`` that can only be specified at the top level of the schema. The files can be either relative or absolute.



How custom validation works
---------------------------

Each function defined inside the extension must be defined with a globally unique method name and the following variables

.. code-block:: python

    def method_name(value, rule_obj, path):
        pass

To raise a validation error you can either raise any exception and it will propegate up to the caller or you can return ``True`` or ``False``. Any value/object that will be interpreted as ``False`` inside a if check will cause a ``CoreError`` validation error to be raised.

When using a validation function on a ``sequence``, the method will be called with the entire list content as the value.

When using a validation function on a ``mapping``, the method will be called with the entire dict content as the value.

When using a validation function on any ``scalar`` type value, the method will be called with the scalar value.

This is a example of how to use extensions inside a simple schema

.. code-block:: yaml

    # Schema
    extensions:
      - e.py
    type: map
    func: ext_map
    mapping:
      foo:
        type: seq
        func: ext_list
        sequence:
          - type: str
            func: ext_str

.. code-block:: yaml

    # Data
    foo:
      - foo
      - bar

This is the extension file named ``ext.py`` that is located in the same directory as the schema file.

.. code-block:: python

    # -*- coding: utf-8 -*-
    import logging
    log = logging.getLogger(__name__)


    def ext_str(value, rule_obj, path):
        log.debug("value: %s", value)
        log.debug("rule_obj: %s", rule_obj)
        log.debug("path: %s", path)

        # Either raise some exception that you have defined your self
        # raise AssertionError('Custom assertion error in jinja_function()')

        # Or you should return True/False that will tell if it validated
        return True


    def ext_list(value, rule_obj, path):
        log.debug("value: %s", value)
        log.debug("rule_obj: %s", rule_obj)
        log.debug("path: %s", path)

        # Either raise some exception that you have defined your self
        # raise AssertionError('Custom assertion error in jinja_function()')

        # Or you should return True/False that will tell if it validated
        return True


    def ext_map(value, rule_obj, path):
        log.debug("value: %s", value)
        log.debug("rule_obj: %s", rule_obj)
        log.debug("path: %s", path)

        # Either raise some exception that you have defined your self
        # raise AssertionError('Custom assertion error in jinja_function()')

        # Or you should return True/False that will tell if it validated
        return True
