Skip to main content

Python configuration library that provides pleasant configuration definition and access interface, and it reads unrestricted python configuration file.

Project description

Confect
=======

``confect`` is a Python configuration library with the following features.

- A unified and pleasant configuration definition and accessing interface
- Immutable conf object for reducing the possibility of making errors
- Configuration file written in Python. This makes it possible to
dynamically handle complicated logic in the configuration file, read
other TOML/YMAL/JSON files or even environment variables in it.

Install
-------

``confect`` is a Python package hosted on PyPI and works only on Python 3.6 up.

Just like other Python package, install it by `pip
<https://pip.pypa.io/en/stable/>`_ into a `virtualenv <https://hynek.me/articles/virtualenv-lives/>`_, or use `poetry <https://poetry.eustace.io/>`_ to
automatically create and manage the virtualenv.

.. code:: console

$ pip install confect


Basic Usage
-----------

Calling ``conf = confect.Conf()`` creates a new configuration manager object.
All configuration properties reside in it and are accessable through get
attribute like this ``conf.group_name.prop_name``. It is possible to create
multiple ``Conf`` objects, but normally we don't need it. In most cases,
initialize only one ``Conf`` object in some module, then import and use it
anywhere in your application.

Put following lines in your application package. For example, in
``your_package.__init__.py`` or ``your_package.core.py``.

.. code:: python

from confect import conf
conf = Conf()

Configuration properties should be declared before using it. Use
``Conf.declare_group(group_name)`` context manager to declare a configuration
group and all properties under it. It's nessasery to provide a default value for
each properties. Default values can be any type as long as the value can be
deepcopy. Group names shuold be valid attribute names.

Put your configuration group declaration code in modules where you need those
properties. And make sure that the declaration is before all the lines that
access these properties. Normally, the group name is your class name, module
name or subpackage name.


.. code:: python

from your_package import conf
with conf.declare_group('yummy') as yummy:
yummy.kind = 'seafood'
yummy.name = 'fish'
yummy.weight = 10

def print_yummy():
# get some configuration through `conf.group_name.prop_name`
print(f'{conf.yummy.kind} {conf.yummy.name} {conf.yummy.weight}')

class Yummy:
# keep a configuration group in a variable
yummy_conf = conf.yummy

def get_yummy(self):
if self.yummy_conf.kind == 'seafood':
fishing_on_the_sea()

Configuration properties and groups are immutable. They can only be globally
changed by loading configuration files. Otherwise, they are always default
values.

>>> conf.yummy.name = 'octopus'
Traceback (most recent call last):
...
confect.error.FrozenConfPropError: Configuration properties are frozen.

Configuration File
------------------

Use ``Conf.load_conf_file(path)`` or ``Conf.load_conf_module(module_name)`` to
load configuration files. No matter it is loaded before or after
groups/properties declaration, property values in configuration file always
override default values. Loading multiple files is possible, the latter one
would replace old values.

Be aware, you should access your configuration properties after load
configuration files. If not, you might get wrong/default value. Therefore, we
usually load configure file right after initilize the ``Conf``, and before
loading all other modules that might access this ``Conf`` object.

For instance, have the following code in ``your_package/core.py``, and ``from
your_package.core import conf`` in other module.

.. code:: python

import sys
from confect import Conf
conf = Conf()

if len(sys.argv) == 2:
conf.load_conf_file(sys.argv[1])
else:
conf.load_conf_file('default/path/to/conf.py')

The default configuration file is in Python. That makes your configuration file
programmable and unrestricted. In configuration file, import ``confect.c``
object and set all properties on it. Here's an example of configuration file.

.. code-block:: python

from confect import c

c.yummy.kind = 'poultry'
c.yummy.name = 'chicken'
c.yummy.weight = 25

import os
c.cache.expire = 60 * 60 # one hour
c.cache.key = os.environ['CACHE_KEY']

DEBUG = True
if DEBUG:
c.cache.disable = True

import json
with open('secret.json') as f:
secret = json.load(f)

c.secret.key = secret['key']
c.secret.token = secret['token']

The ``c`` object only exits when loading a python configuration file, it's not
possible to import it in your source code. You can set any property in any
configuration group onto the ``c`` object. However, they are only accessable if
you declared it in the source code with ``Conf.declare_group(group_name)``.

If it's hard for you to specify the path of configuration file. You can load it
through the import system of Python. Put your configuration file somewhere under
your package or make ``PYTHONPATH`` pointing to the directory it resides. Then
load it with ``Conf.load_conf_module(module_name)``.

.. code:: console

$ edit my_conf.py
$ export PYTHONPATH=.
$ python your_application.py


.. code:: python

from confect import Conf
conf = Conf()
conf.load_conf_module('my_conf')

Load Environment Variables
---------------------------

``Conf.load_envvars(prefix: str)`` automatically searches environment variables
in ``<prefix>__<group>__<prop>`` format. All of these three identifier are case
sensitive. If you have a configuration property ``conf.cache.expire_time`` and
you call ``Conf.load_envvars('proj_X')``. It will set that ``expire_time``
property to the parsed value of ``proj_X__cache__expire_time`` environment
variable.

>>> import os
>>> os.environ['proj_X.cache.expire'] = '3600'

>>> conf = confect.new_conf()
>>> conf.load_envvars('proj_X') # doctest: +SKIP

Confect includes predefined parsers of these primitive types.

- ``str``
- ``int``
- ``float``
- ``bytes``
- ``datetime.datetime``
- ``datetime.date``
- ``tuple``
- ``dict``
- ``list``

Mutable Environment
-----------------

``Conf.mutate_locally()`` context manager creates an environment that makes ``Conf``
object temporarily mutable. All changes would be restored when it leaves the
block. It is usaful on writing test case or testing configuration properties in Python REPL.

>>> conf = Conf()
>>> conf.declare_group( # declare group through keyword arguments
... 'dummy',
... prop1=3,
... prop2='some string')
...
>>> with conf.mutate_locally():
... conf.dummy.prop1 = 5
... print(conf.dummy.prop1)
5
... call_some_function_use_this_property()
>>> print(conf.dummy.prop1) # all configuration restored
3


To-Dos
======

- A function for loading dictionary into ``conflect.c``.
- A function that loads command line arguments and overrides configuration properties.
- Copy-on-write mechenism in ``conf.mutate_locally()`` for better performance and memory usage.
- API reference page

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

confect-0.2.5.tar.gz (10.3 kB view hashes)

Uploaded Source

Built Distribution

confect-0.2.5-py3-none-any.whl (23.1 kB view hashes)

Uploaded Python 3

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