Advanced Usage

Most of the time you can use the prettyconf.config function to get your settings and use the prettyconf’s standard behaviour. But some times you need to change this behaviour.

To make this changes possible you can always create your own Configuration() instance and change it’s default behaviour:

from prettyconf import Configuration

config = Configuration()

Warning

prettyconf will skip configuration files inside .zip, .egg or wheel packages.

Customizing the configuration discovery

By default the library will use the envrionment and the directory of the file where config() was called as the start directory to look for a .env configuration file. Consider the following file structure:

project/
  app/
    .env
    config.ini
    settings.py

If you call config() from project/app/settings.py the library will inspect the envrionment and then look for configuration files at project/app.

You can change that behaviour, by customizing configuration loaders to look at a different path when instantiating your Configuration():

# Code example in project/app/settings.py
import os

from prettyconf import Configuration
from prettyconf.loaders import Environment, EnvFile

project_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
env_file = f"{project_path}/.env"
config = Configuration(loaders=[Environment(), EnvFile(filename=env_file)])

The example above will start looking for configuration in the environment and then in a .env file at project/ instead of project/app.

Because config is nothing but an already instantiated Configuration object, you can also alter this loaders attribute in prettyconf.config before use it:

# Code example in project/app/settings.py
import os

from prettyconf import config
from prettyconf.loaders import Environment, EnvFile

project_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
env_file = f"{project_path}/.env"
config.loaders = [Environment(), EnvFile(filename=env_file)]

Read more about how loaders can be configured in the loaders section.

Naming conventions for variables

There happen to be some formating conventions for configuration paramenters based on where they are set. For example, it is common to name environment variables in uppercase:

$ DEBUG=yes OTHER_CONFIG=10 ./app.py

but if you were to set this config in an .ini file, it should probably be in lower case:

[settings]
debug=yes
other_config=10

command line argments have yet another conventions:

$ ./app.py --debug=yes --another-config=10

Prettyconf let’s you follow these aesthetics patterns by setting a var_format function when instantiating the loaders.

By default, the Environment is instantiated with var_format=str.upper so that lookups play nice with the env variables.

from prettyconf import Configuration
from prettyconf.loaders import Environment

config = Configuration(loaders=[Environment(var_format=str.upper)])
debug = config('debug', default=False, cast=config.boolean)  # lookups for DEBUG=[yes|no]

Writing your own loader

If you need a custom loader, you should just extend the AbstractConfigurationLoader.

For example, say you want to write a Yaml loader. It is important to note that by raising a KeyError exception from the loader, prettyconf knows that it has to keep looking down the loaders chain for a specific config.

import yaml
from prettyconf.loaders import AbstractConfigurationLoader

class YamlFile(AbstractConfigurationLoader):
    def __init__(self, filename):
        self.filename = filename
        self.config = None

    def _parse(self):
        if self.config is not None:
            return
        with open(self.filename, 'r') as f:
            self.config = yaml.load(f)

    def __contains__(self, item):
        try:
            self._parse()
        except:
            return False

        return item in self.config

    def __getitem__(self, item):
        try:
            self._parse()
        except:
            # KeyError tells prettyconf to keep looking elsewhere!
            raise KeyError("{!r}".format(item))

        return self.config[item]

Then configure prettyconf to use it.

from prettyconf import config
config.loaders = [YamlFile('config.yml')]