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, namespace=None, default=NO_VALUE, default_if_empty=True, alternate_keys=None, doc='', parser=<class 'str'>, raise_error=True, raw_value=False)
Return a parsed value from the environment.
- Parameters:
key (str) – the key to look up
namespace (List[str] | str | None) – the namespace for the key–different environments use this differently
default (str | NoValue) –
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 ofraise_error
If this ConfigManager is bound to a component, the default will be the default of the option in the bound component configuration.
default_if_empty (bool) – if True, treat empty string values as a non-value and return the specified default
alternate_keys (List[str] | None) –
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 namespaceIf 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 (str) –
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 (Callable) –
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 (bool) – True if you want a lack of value to raise a
everett.ConfigurationError
raw_value (bool) – 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)
- Return type:
Any
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, namespace, key, parser)
Error that indicates that the value is not valid.
- Parameters:
msg (str) –
namespace (List[str] | None) –
key (str) –
parser (Callable) –
- class everett.ConfigurationMissingError(msg, namespace, key, parser)
Error that indicates that required configuration is missing.
- Parameters:
msg (str) –
namespace (List[str] | None) –
key (str) –
parser (Callable) –
- 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-shooting 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.