Skip to content

Usage

We will cover the various usages of configuror in the following guide.

The most important thing to know is the Config class. It is an extension of a regular dict object. There are two main ways to instantiate it.

Using mapping files

from configuror import Config

mapping_files = {
    'ini': ['foo.ini', 'bar.ini'],
    'toml': ['foo.toml', 'bar.toml'],
    'python': ['/path/to/python/file']
}
config = Config(mapping_files=mapping_files, ignore_file_absence=True)

You can define a mapping of file_type: <files> where the file_type is the type of configuration file and <files> is the list of files from the lowest to the highest priority where values will be loaded. To know all the keys that you can use in your dictionary, you can print the EXTENSIONS keys. The constant EXTENSIONS is directly importable from the package.

Since dictionaries are ordered starting from python3.6, the order of the keys is important as it will become the order of importance of your files. For example in the example above, configuror will load values from files in the following order:

  • foo.ini
  • bar.ini
  • foo.toml
  • bar.toml
  • /path/to/python/file

Note

For python files, only uppercase variables will be loaded. This is considered a good practice for parameter variables.

Note

For ini files, only basic interpolation will be used, if you want extended interpolation, you will need to call load_from_ini after Config initialization with interpolation_method parameter set to extended.

You will notice the keyword argument ignore_file_absence in Config class initialization. If it is set to True, all files that does not exist will not raised FileNotFoundError. It comes in handy when you want to retrieve variables from files that may or may not potentially exist. By default this parameter is set to False.

File extension is not necessary when you use mapping files since the key is already telling which files we work with. This is not the case with the second way to initialize Config class.

Using a list of files

from configuror import Config

files = [
    'foo.yml',
    'bar.toml',
    'foobar.json',
    '/path/to/python/file'
    '.env'
]
config = Config(files=files)

In this second form of initialization, you pass a list of files you want to retrieve values from the lowest to the highest priority.

Warning

File extension is mandatory here to help configuror to load the files properly.

To know file extensions supported by configuror, you can use the variable EXTENSIONS. it is a mapping file_type: <extensions> where file_type is a type of file supported like yaml and extensions is a list of recognized extensions for this type of file, e.g: [yml, yaml]

Today the file types supported are toml, yaml, dotenv, ini, python and json.

Other usages

Since Config object is a dict-like object, you can pass arbitrary keyword arguments to initialize default values.

from configuror import Config

config = Config(FOO=2, BAR='a')
print(config)  # will print {'FOO': 2, 'BAR': 'a'}

You can combine keyword arguments, mapping files and list of files at initialization. The order in which values will be initialized is the following:

  • values from keyword arguments
  • values from mapping files
  • values from list of files

You can also add values from files after initialization. There are several practical methods for this:

  • load_from_mapping_files: This is in fact the method used under the hood when you initialized Config object by passing the parameter mapping_files.

  • load_from_files: It is the method used under the hood when you initialized Config objects by passing the parameter files.

  • load_from_object: It loads values from a python object or a string corresponding to a path of a module (dotted notation). Only uppercase attributes of the corresponding object will be loaded.

Example 1

# Imagine you have a module settings.py in your project with the following variables
FOO = 2
bar = 'hello'
FOOBAR = 'world'

# in your main module
from configuror import Config

c = Config()
c.load_from_object('your_project.settings')
# this will give an object with two items: {'FOO': 2, 'FOOBAR': 'world'}

Example 2:

from enum import Enum
from configuror import Config

class Fruit:
    BANANA = 2
    TOMATOES = 4

class Color(Enum):
    RED = 1
    BLUE = 2
    GREEN = 3

c = Config()
c.load_from_object(Fruit)
c.load_from_object(Color)
# The result will be this object:
# {'BLUE': <Color.BLUE: 2>, 'GREEN': <Color.GREEN: 3>, 'RED': <Color.RED: 1>, 'BANANA': 2, 'TOMATOES': 4}

Warning

a dict object will not work since we cannot access attributes via the getattr builtin function. Since a Config object is an extension of a dict, you can take advantage of the update method ;)

Warning

If you use a property in a class and want to load it, you must first instantiate the class.

  • load_from_python_file: It loads values from a python file. The file must ideally be outside the project. Only uppercase attributes of the module will be loaded.

  • load_from_json: It loads values from a json file. Uppercase and lowercase attributes will be loaded.

  • load_from_yaml: It loads values from a yaml file. Uppercase and lowercase attributes will be loaded. Note that even if yaml allows to define multiple documents in the same file, this option is not supported in configuror because it is considered irrelevant.

  • load_from_ini: It loads values from ini files. Uppercase and lowercase attributes will be loaded. Since the Configparser can accept a list of files at initialization, this method also accept a list of files to take advantage of this feature. You will notice in the signature of this method a parameter interpolation_method. This helps you to choose the type of interpolation you want to be performed on the ini files. The two values allowed are basic and extended. For more information, please read this documentation.

Note

In fact, a string other than extended will be considered basic for the interpolation_method parameter.

  • load_from_toml: It loads values from toml files. Uppercase and lowercase attributes will be loaded. Since toml can accept a list of files to load, this method also accept a list of files to take advantage of this feature.

  • load_from_dotenv: It loads values from dotenv files. It allows you to start a line in your dotenv file with the word export or set so you can reuse the dotenv file in a PowerShell or bash script. It also have the ability to expand environment variables.

Example: Imagine you have a .env file like the following

# comments are ignored
# we will assume the HOME environment variable equals to "/home/kevin"
export PROJECT_DIR=${HOME}/my_project
export SETTINGS=${PROJECT_DIR}/settings.ini
FOO=BAR
from configuror import Config

config = Config()
# we assume the .env file is in the same directory
config.load_from_dotenv('.env')
# The result will be this object =>
# {'PERSONAL_DIR': '/home/kevin/my_project', 'SETTINGS': '/home/kevin/my_project/settings.ini', 'FOO': 'BAR'}

Note

Using .env files is a considered a good practice following the twelve-factor pattern. It helps to decouple code from configuration. If you use this pattern, make sure your .env file is not committed to your source control.

Bonus

  • If in your project you have some environment variables you want to handle, you can get them through the convenient getenv method. It can help to convert their value to a suitable one given a converter callback.

  • Sometimes you may have options starting with a common prefix and you may want to use the suffixes of these options as function or class constructors keyword arguments. There is a convenient method get_dict_from_namespace for this purpose.

from configuror import Config

# imagine you have a config like the following
config = Config()
config.update({
    'IMAGE_STORE_TYPE': 'fs',
    'IMAGE_STORE_PATH': '/var/app/images',
    'IMAGE_STORE_BASE_URL': 'http://img.website.com'
})

# To extract the type, path and base_url from the configuration above, you will do this:
image_store_info = config.get_dict_from_namespace('IMAGE_STORE_')
# image_store_info will be this => {'type': 'fs', 'path': '/var/app/images', 'base_url': 'http://img.website.com'}

Advice

You should load your configuration as soon as possible before running your project and avoid as much as possible to modify the configuration during the runtime of the project.