Configuration

Extracting values

Once you have a configuration manager set up with sources, you can pull configuration values from it.

Configuration must have a key. Everything else is optional.

Examples:

config("password")

The key is “password”.

The value is parsed as a string.

There is no default value provided so if “password” isn’t provided in any of the configuration sources, then this will raise a everett.ConfigurationError.

This is what you want to do to require that a configuration value exist.

config("name", raise_error=False)

The key is “name”.

The value is parsed as a string.

There is no default value provided and raise_error is set to False, so if this configuration variable isn’t set anywhere, the result of this will be everett.NO_VALUE.

Note

everett.NO_VALUE is a falsy value so you can use it in comparative contexts:

debug = config("DEBUG", parser=bool, raise_error=False)
if not debug:
    pass
config("port", parser=int, default="5432")

The key is “port”.

The value is parsed using int.

There is a default provided, so if this configuration variable isn’t set in the specified sources, the default will be false.

Note that the default value is always a string that’s parseable by the parser.

config("username", namespace="db")

The key is “username”.

The namespace is “db”.

There’s no default, so if there’s no “username” in namespace “db” configuration variable set in the sources, this will raise a everett.ConfigurationError.

If you’re looking up values in the process environment, then the full key would be DB_USERNAME.

config("password", namespace="postgres", alternate_keys=["db_password", "root:postgres_password"])

The key is “password”.

The namespace is “postgres”.

If there is no key “password” in namespace “postgres”, then it looks for “db_password” in namespace “postgres”. This makes it possible to deprecate old key names, but still support them.

If there is no key “password” or “db_password” in namespace “postgres”, then it looks at “postgres_password” in the root namespace. This allows you to have multiple components that share configuration like credentials and hostnames.

config(
    "debug", default="false", parser=bool,
    doc="Set to True for debugmode; False for regular mode",
)

You can provide a doc argument which will give users users who are trying to configure your software a more helpful error message when they hit a configuration error.

Example of error message for an option that specifies doc when trying to set DEBUG to foo:

<traceback>
everett.InvalidValueError: ValueError: 'foo' is not a valid bool value
DEBUG requires a value parseable by everett.manager.parse_bool
DEBUG docs: Set to True for debugmode; False for regular mode
Project docs: Check https://example.com/configuration for documentation.

That last line comes directly from the doc argument you provide.

ConfigManager.__call__(key: str, namespace: Union[List[str], str, None] = None, default: Union[str, everett.NoValue] = NO_VALUE, alternate_keys: Optional[List[str]] = None, doc: str = '', parser: Callable = <class 'str'>, raise_error: bool = True, raw_value: bool = False) → Any

Return a parsed value from the environment.

Parameters:
  • key – the key to look up
  • namespace – the namespace for the key–different environments use this differently
  • default

    the default value (if any); this must be a string that is parseable by the specified parser; if no default is provided, this will raise an error or return everett.NO_VALUE depending on the value of raise_error

    If this ConfigManager is bound to a component, the default will be the default of the option in the bound component configuration.

  • alternate_keys

    the list of alternate keys to look up; supports a root: key prefix which will cause this to look at the configuration root rather than the current namespace

    If this ConfigManager is bound to a component, the alternate_keys will be the alternate_keys of the option in the bound component configuration.

    New in version 0.3.

  • doc

    documentation for this config option

    If this ConfigManager is bound to a component, the doc will be the doc of the option in the bound component configuration.

    New in version 0.6.

  • parser

    the parser for converting this value to a Python object

    If this ConfigManager is bound to a component, the parser will be the parser of the option in the bound component configuration.

  • raise_error – True if you want a lack of value to raise a everett.ConfigurationError
  • raw_value – True if you want the raw unparsed value, False otherwise
Raises:
  • everett.ConfigurationMissingError – if the required bit of configuration is missing from all the environments
  • everett.InvalidKeyError – if the configuration key doesn’t exist for that component
  • everett.InvalidValueError – if the configuration value is invalid in some way (not an integer, not a bool, etc)

Note

The default value should always be a string that is parseable by the parser. This simplifies thinking about values since all values are strings that are parsed by the parser rather than default values do one thing and non-default values doa nother. Further, it simplifies documentation for the user since the default value is an example value.

The parser can be any callable that takes a string value and returns a parsed value.

class everett.ConfigurationError

Configuration error base class.

class everett.InvalidValueError(msg: str, namespace: Optional[List[str]], key: str, parser: Callable)

Error that indicates that the value is not valid.

class everett.ConfigurationMissingError(msg: str, namespace: Optional[List[str]], key: str, parser: Callable)

Error that indicates that required configuration is missing.

class everett.InvalidKeyError

Error that indicates the key is not valid for this component.

Namespaces

Everett has namespaces for grouping related configuration values.

For example, this uses “username”, “password”, and “port” configuration keys in the “db” namespace:

# namespaces.py

from everett.manager import ConfigManager


def open_connection(config):
    username = config("username")
    password = config("password")
    port = config("port", default="5432", parser=int)

    print(f"Opened database with {username}/{password} on port {port}")


config = ConfigManager.from_dict(
    {
        "DB_USERNAME": "admin",
        "DB_PASSWORD": "ou812",
    }
)

# Database configuration keys are all prefixed with "db", so we want to
# retrieve database configuration keys with the "db" namespace
db_config = config.with_namespace("db")

open_connection(db_config)

These variables in the environment would be DB_USERNAME, DB_PASSWORD and DB_PORT.

$ python namespaces.py
Opened database with admin/ou812 on port 5432

This is helpful when you need to create two of the same thing, but using separate configuration.

What if we had source and destination databases and needed to have the configuration keys separated?

# namespaces2.py

from everett.manager import ConfigManager


def open_connection(config):
    username = config("username")
    password = config("password")
    port = config("port", default="5432", parser=int)

    print(f"Opened database with {username}/{password} on port {port}")


config = ConfigManager.from_dict(
    {
        "SOURCE_DB_USERNAME": "admin",
        "SOURCE_DB_PASSWORD": "ou812",
        "DEST_DB_USERNAME": "admin",
        "DEST_DB_PASSWORD": "P9rwvnnj8CidECMb",
    }
)

# Database configuration keys are all prefixed with "db", so we want to
# retrieve database configuration keys with the "db" namespace
source_db_config = config.with_namespace("source_db")
dest_db_config = config.with_namespace("dest_db")

source_conn = open_connection(source_db_config)
dest_conn = open_connection(dest_db_config)
$ python namespaces2.py
Opened database with admin/ou812 on port 5432
Opened database with admin/P9rwvnnj8CidECMb on port 5432

Handling exceptions when extracting values

When the namespaced key isn’t found in any of the sources, then Everett will raise an exception that is a subclass of everett.ConfigurationError. This makes it easier to programmatically figure out what happened.

If you don’t like what Everett prints by default, you can catch the errors and print something different.

For example:

# handling_exceptions.py

import logging

from everett import InvalidValueError
from everett.manager import ConfigManager

logging.basicConfig()

config = ConfigManager.from_dict({"debug_mode": "monkey"})

try:
    some_val = config("debug_mode", parser=bool, doc="set debug mode")
except InvalidValueError:
    print("I'm sorry dear user, but DEBUG_MODE must be 'true' or 'false'.")

Also, you can change the structure of the error message by passing in a msg_builder argument to the everett.manager.ConfigManager.

For example, say your project is entirely done with INI configuration. Then you’d want to tailor the message accordingly.

# msg_builder.py

from everett.manager import ConfigManager, ConfigOSEnv


def build_msg_for_ini(namespace, key, parser, msg="", option_doc="", config_doc=""):
    namespace = namespace or ["main"]
    namespace = "_".join(namespace)

    return f"Dear user. {key} in section [{namespace}] is not correct. Please fix it."


config = ConfigManager(
    environments=[ConfigOSEnv()],
    msg_builder=build_msg_for_ini,
)

config("debug", default="false", parser=bool)

That prints this:

$ DEBUG=lizard python msg_builder.py
<traceback>
everett.InvalidValueError: Dear user. debug in section [main] is not correct. Please fix it.

Trouble-shootinging and logging what happened

If you have a non-trivial Everett configuration, it might be difficult to figure out exactly why a key lookup failed.

Everett logs to the everett logger at the logging.DEBUG level. You can enable this logging and get a clearer idea of what’s going on.

See Python logging documentation for details on enabling logging.