Configuration: settings and options

fresco.core.FrescoApp.options is a dict-like Options object that can be used for your application’s configuration.

Basic usage

In the simplest case, fresco.core.FrescoApp.options can be used like a regular dict:

from fresco import FrescoApp
app = FrescoApp()

app.options["SMTP_HOST"] = "smtp://localhost"
app.options.update({"DATABASE": "sqlite://"})

Settings are made available globally via context.app.options:

from fresco import context

db = connect(context.app.options.DATABASE)

Using configuration files

Use load() to load options from configuration files:

app = FrescoApp()

# Load options from all matching files
app.options.load("conf.d/*")

# A fully loaded example
app.options.load(
    "conf.d/*",

    # Search for files in this directory
    dir="/path/to/config",

    # Allow configuration files to add keys not present in the first file
    strict=False,

    # Use environment variables in preference to values loaded from files
    use_environ=True
)

The dir argument specifies the directory to be searched for configuration files (defaults to the current directory).

The default behaviour is that the first loaded file must specify all available configuration keys. A subsequent file that introduces a new key will raise an exception. Specify strict=False to turn off this check.

If use_environ=True is specified, any environment variable with the same name as a loaded option key will be used in preference to the value loaded from the file. Environment variables are subject to the same parsing and interpolation rules as described in key-value properties files

Selecting configuration files based on environment

load() can load files selectively based on ‘tags’ embedded in the file names (tags are embedded in the filename, separated by dots, eg settings.tag1.tag2.conf).

For example with these configuration files:

settings.conf                 # 1
settings.dev.conf             # 2
settings.staging.conf         # 3
settings.staging.local.conf   # 4
settings.local.conf           # 5

This is how to load subsets of the available files:

# Load all files
options.load("settings*")

# Load base config file plus those
# tagged 'dev' or 'local' (files #1, #2 and #5)
options.load("settings*", ["dev", "local"])

Files are loaded in the order:

  • Files not matching any tags, ordered by filename.

  • Files matching one or more supplied tag, in the order of tags provided to load().

The string {hostname} in a tag name will be interpolated with the current hostname (as returned by socket.gethostname), but with dots replaced by underscores (eg myserver_example_org). A common pattern is:

options.load(
    "settings*",
    [
        os.environ.get("FRESCO_PROFILE", "prod"),
        "host-{hostname}",
        "local"
    ]
)

This will load:

  • a base config file (eg settings.conf);

  • then any config files tagged with the profile specified in the FRESCO_PROFILE environment variable (eg settings.dev.conf or settings.prod.conf)

  • then any host-specific config files (eg settings.host-myserver_example_org.conf);

  • finally, any local config files (eg settings.local.conf).

Configuration file formats

The following formats are supported by load():

key-value properties files

These are simple newline separated lists of key-value pairs. The format is designed to be easy to parse, and as such does not support features such as multi-line strings or escaping special characters. For more flexibility use one of the other formats.

Example:

# This is a comment
database = postgresql://myserver/mydb

# There are two special variables available:
config_path = $__FILE__
data_dir = $__DIR__/data

# Environment variables may also be interpolated
cache_dir = $HOME/.cache/myapp-cache

# As can configuration keys that have already been loaded
sessions_dir = $data_dir/sessions

# Boolean values: 'true' and 'false' are case-insensitive here
apples_are_green = true
horses_are_green = false

# Integers
x = 1
y = 2

# Values with decimal points are parsed as `decimal.Decimal` objects
z = 11.3

# Everything else is a string
hello = world

# Strings may be quoted if required to disambiguate
# or to preserve whitespace
apikey = "123456"
message = "       HELLO $hello       "

JSON (.json)

JSON files are loaded using python’s builtin JSON module.

Toml (.toml)

Loading from Toml files is only supported if the toml package is available.

Python files (.py)

The file will be loaded and parsed as a Python module. Module-level variables are loaded into the options dict unless they start with an underscore.

The __all__ variable may be used to specify a list of keys to load.

Example:

# Use leading underscores to avoid creating
# unnecessary entries in the options dict
from os.path import join as _pathjoin
from os import environ as _env

DATABASE = f"sqlite:///{_env['HOME']}/my.db"

Loading settings from environment variables

If you also want to load your default settings from files and then update this from environment variables, use load():

app = FrescoApp()
app.options.load("myapp.conf", use_environ=True)

If you only want to load values from the environment, use update():

app = FrescoApp()
settings_keys = ['DATABASE']
app.options.update({k: os.environ[k] for k in settings_keys})

Onload hooks

Register callbacks to be run once load() has completed by using onload():

options = fresco.options.Options()

@options.onload
def do_something_with(options):
    print("Howdy!")

options.load(".env*")

API reference: fresco.options

class fresco.options.Options(*a, **kw)[source]

Options dictionary. An instance of this is attached to each fresco.core.FrescoApp instance, as a central store for configuration options.

copy() a shallow copy of D[source]
load(sources: str, tags: Sequence[str] = [], use_environ=False, strict=True, dir=None)[source]

Find all files matching glob pattern sources and populates the options object from those with matching filenames containing tags.

Parameters:
  • sources – glob pattern or glob patterns separated by “;”

  • tags – a list of tags to look for in file names. If a filename contains multiple tags, all the tags in the filename must match for it to be loaded.

  • use_environ – if true, environment variables matching previously loaded keys will be loaded into the options object. This happens after all files have been processed.

  • strict – if true, the first file loaded is assumed to contain all available option keys. Any new key found in a later file will raise an error.

Files may be in python (.py), json (.json), TOML (.toml) format. Any other files will be interpreted as simple lists of `key=value pairs.

Tags in filenames should be delimited with periods, eg “.env.production.py”. For For example the filename setttings.dev.local.ini would be considered to have the tags, ('dev', 'local')

The string ‘{hostname}’ can be included as part of a tag name: it will be substituted for the current host’s name with dots replaced by underscores.

Files with the suffix “.sample” are unconditionally excluded.

Files are loaded in the order specified by tags, then in filename order. Environment variables, if requested, are loaded last.

Example:

opts = Options()
opts.load(Options(), ".env*", ["dev", "host-{hostname}", "local"])

Would load options from files named .env, .env.json, .env.dev.py and .env.local.py.

onload(fn: Callable) Callable[source]

Register a function to be called once load has finished populating the options object.

update_from_dict(d, load_all=False)[source]

Update from the given list of key-value pairs.

If load_all is True, all key-value pairs will be loaded.

Otherwise, if the special key ‘__all__’ is present, only those keys listed in __all__ will be loaded (same semantics as from … import *)

Otherwise only those NOT beginning with _ will be loaded.

update_from_file(path, load_all=False)[source]

Update the instance with any symbols found in the python source file at path.

Parameters:
  • path – The path to a python source file

  • load_all – If true private symbols will also be loaded into the options object.

update_from_object(ob, load_all=False)[source]

Update the instance with any symbols found in object ob.

Parameters:

load_all – If true private symbols will also be loaded into the options object.