.. -*- coding: utf-8; indent-tabs-mode: nil; -*-

.. _introduction:

Introduction
============

The configurations can be read from different types of files:

- :ref:`YAML files <yaml-files>`
- :ref:`INI files <ini-files>`
- :ref:`executable Python scripts <executable-python-scripts>`


.. _yaml-files:

YAML Files
----------

Need the :mod:`yaml` package (e.g. ``pip install pyyaml``)

.. note:: All strings are returned as Unicode text strings.


An example is:

.. literalinclude:: ../tests/data/conf10.yml
   :language: yaml


.. _ini-files:

INI Files
---------

Read the file and all sections named in parameter `extract` are flattened
into the resulting dictionary. By default the section named ``config`` is
used.

Normally all values are returned as Unicode text strings.
But values can be annotated and therefore interpreted as other types:

  ``:int:``
      The value is handled in the same way as a Python :class:`int`
      literal

  ``:float:``
      The value is interpreted as :class:`float`

  ``:bool:``
      The resulting value is a :class:`bool` where

        ``1``, ``true``, ``yes``, ``on``
           yield a Python ``True``

        ``0``, ``false``, ``no``, ``off``
           yield a Python ``False``

      The evaluation is done *case-insensitively*.

.. note:: All strings are returned as Unicode text strings.

.. note:: Contrary to the behaviour of the standard Python :mod:`configparser`
          module the INI file reader is *case-sensitive*.

The example INI style configuration below yields an equivalent
configuration to the YAML configuration above:

.. literalinclude:: ../tests/data/conf10.ini
   :language: ini

As can be seen in thie example -- INI file internal value interpolation
is done as in Python's standard :mod:`configparser` module.

This example also illustrates how INI sections are used to build a
tree-ish configuration dictionary.


.. _executable-python-scripts:

Executable Python Scripts
-------------------------

What will be exported:

1. If loading is done with the `extract` parameter only the given keys are
   extracted from the script.

2. Otherwise it is checked if the scripts defines an ``__all__``
   sequence. If there is one it's contents are the keys to be
   extracted.

3. If there is no ``__all__`` object all names not starting with an
   underscore ``_`` are found.

This is analogous to as Python modules behave when importing them with
``from module import *``.

.. note:: The Python configuration files are evaluated with ``exec`` and not
          imported.

The example configuration by Python script below yields an equivalent
configuration to the YAML configuration above:

.. literalinclude:: ../tests/data/conf10.py
   :language: python


.. _loading-and-merging:

Loading and Merging
-------------------

Basic usage of the API is as follows in this example::

    import configmix

    #
    # Note: With conf10 merging is rather pointless because the tree
    # files # are really the same configuration. But is doesn't harm
    # also here.
    #
    config = configmix.load("conf10.yml", "conf10.ini", "conf10.py")

    # Get a -- possibly interpolated -- configuration variable's value
    value1 = config.getvar_s("key1")

    # Get a -- possibly interpolated -- variable from within the tree
    value2 = config.getvar_s("tree1.tree2.key4")


The filenames of the configuration files must have the extensions
(case-insensitively):

  ``.py``
    for Python configuration files

  ``.yml`` or ``.yaml``
    for YAML configuration files

  ``.ini``
    for INI configuration files


.. _getting-values:

Getting configuration variables
-------------------------------

Get a -- possibly expanded -- configuration variable's value with::

    value1 = config.getvar_s("key1")

Get a raw configuration variable's value with::

    value1_raw = config.getvar("key1")

Because the configuration is not only a plain list of but a tree of
key-value pairs you will want to fetch them by separating the individual
level keys with a point ``.``.

Looking at the example in chapter :ref:`yaml-files` -- when calling
``config.getvar_s("tree1.tree2.key4")`` you will get the value
``get this as `tree1.tree2.key4'``.

This is true for both methods :py:meth:`.Configuration.getvar` and
:py:meth:`.Configuration.getvar_s`.

Both methods also perform :ref:`variable-expansion` and handle
:ref:`variable-namespaces`. Filtering is not supported. So -- the
variable name arguments of :py:meth:`.Configuration.getvar` and
:py:meth:`.Configuration.getvar_s` are of the form
``[namespace:]variable``.


.. _variable-namespaces:

Variable Namespaces
-------------------

Currently there are 4 namespaces:

1. The unnamed namespace (which is also default).

   All the configuration variables are part of this namespace.

2. The namespace ``OS``

   Available functions:

     ``cwd``
         Contains the current working directory of the process

3. The namespace ``ENV``

   This namespace contains all the environment variables as they are
   available from :py:data:`os.environ`.

4. The namespace ``PY``

   Contains selected values from the running Python:

     ``version``
         The return value of :py:func:`platform.python_version`

     ``version_maj_min``
         Just the major and minor version of the running Python
         (``.`` separated)

     ``version_maj``
         Just the major version of the running Python

     ``implementation``
         The return value of :py:func:`platform.python_implementation`


Examples
~~~~~~~~

::

     config.getvar("OS:cwd")

yields the current working directory as :py:func:`os.getcwd` does.


.. _variable-expansion:

Variable Expansion
------------------

Configuration variable values that are read with
:py:meth:`.Configuration.getvar_s` are subject to variable
expansion. The general syntactic pattern for this is::

    {{[namespace:]variable[|filter[|filter...]]}}

I.e.: between double curly braces an optional `namespace` name followed by
a colon ``:``, the `variable` and then zero or more filters, each one
introduced by a pipe symbol ``|``.

Variables are expanded lately at runtime -- exactly when calling
:py:meth:`.Configuration.getvar_s` or :py:meth:`.Configuration.getvar`.


Filter functions
~~~~~~~~~~~~~~~~

Interpolated values can be processed through a series of filter functions::

    {{my.variable|filter1|filter2}}

Available filter functions are:

  ``urlquote``

  ``saslprep``

  ``normpath``

  ``abspath``

  ``posixpath``

  ``lower``

  ``upper``


Examples
~~~~~~~~

::

    {{OS:cwd|posixpath}}

expands to the current working directory as POSIX path: on Windows all
backslashes are replaced by forward slashes.

::

    {{ENV:PATH}}

expands to the current search path from the process environment.

::

    {{PY:version}}

expands to the current running Python version (e.g. ``3.6.4``).

::

    {{PY::implementation|upper}}

expands to something like ``CPYTHON`` when using the standard Python
interpreter written in C.
