Skip to main content

Github Actions codecov Documentation Status

TL;DR

# config.yml
foo:
  bar: <% ENV[REQUIRED] %>
  baz: <% ENV[OPTIONAL, true] %>
list_of_stuff:
  - fun<% ENV[NICE, dament] %>al
  - fun<% ENV[AGH, er] %>al
  - more/<% ENV[THAN, er] %>/one/<% ENV[interpolation, er] %>!
# app.py
config = Config.from_yaml('config.yml')

print(config.foo.bar)
print(config.foo['baz'])
for item in config.list_of_stuff:
    print(item)
pip install configly[yaml]

Introduction

Loading configuration is done in every (application) project, and yet it is often overlooked and condidered too easy or straightforward to bother using a library to manage doing it.

Therefore, we often see code like this:

# config.py
import os

# Maybe it's following 12factor and loading all the config from the environment.
config = {
    'log_level': os.getenv('LOG_LEVEL'),
    'database': {
        # At least here, I can nest values if I want to organize things.
        'password': os.environ['DATABASE_PASSWORD'],
        'port': int(os.environ['DATABASE_PORT']),
    }
}

or this

# config.py
import os

class Config:
    log_level = os.getenv('LOG_LEVEL')

    # Here it's not so easy to namespace
    database_password = os.environ['DATABASE_PASSWORD']
    database_port = int(os.environ['DATABASE_PORT'])


# Oh goodness!
class DevConfig(Config):
    environment = 'dev'

or this

import configparser
# ...🤢... Okay I dont even want to get into this one.

And this is all assuming that everyone is loading configuration at the outermost entrypoint! The two worst possible outcomes in configuration are:

  • You are loading configuration lazily and/or deeply within your application, such that it hits a critical failure after having seemingly successfully started up.
  • There is not a singular location at which you can go to see all configuration your app might possibly be reading from.

The pitch

Configly asserts configuration should:

  • Be centralized
    • One should be able to look at one file to see all (env vars, files, etc) which must exist for the application to function.
  • Be comprehensive
    • One should not find configuration being loaded secretly elsewhere
  • Be declarative/static
    • code-execution (e.g. the class above) in the definition of the config inevitably makes it hard to interpret, as the config becomes more complex.
  • Be namespacable
    • One should not have to prepend foo_ namespaces to all foo related config names
  • Be loaded, once, at app startup
    • (At least the definition of the configuration you're loading)
  • (Ideally) have structured output
    • If something is an int, ideally it would be read as an int.

To that end, the configly.Config class exposes a series of classmethods from which your config can be loaded. It's largely unimportant what the input format is, but we started with formats that deserialize into at least str, float, int, bool and None types.

# Currently supported input formats.
config = Config.from_yaml('config.yml')
config = Config.from_json('config.json')
config = Config.from_toml('config.toml')

Given an input config.yml file:

# config.yml
foo:
  bar: <% ENV[REQUIRED] %>
  baz: <% ENV[OPTIONAL, true] %>
list_of_stuff:
  - fun<% ENV[NICE, dament] %>al
  - fun<% ENV[AGH, er] %>al
  - more/<% ENV[THAN, er] %>/one/<% ENV[interpolation, er] %>!

A number of things are exemplified in the example above:

  • Each <% ... %> section indicates an interpolated value, the interpolation can be a fragment of the overall value, and multiple values can be interpolated within a single value.

  • ENV is an "interpolator" which knows how to obtain environment variables

  • [VAR] Will raise an error if that piece of config is not found, whereas [VAR, true] will default VAR to the value after the comma

  • Whatever the final value is, it's interpreted as a literal value in the format of the file which loads it. I.E. true -> python True, 1 -> python 1, and null -> python None.

Now that you've loaded the above configuration:

# app.py
config = Config.from_yaml('config.yml')

# You can access namespaced config using dot access
print(config.foo.bar)

# You have use index syntax for dynamic, or non-attribute-safe key values.
print(config.foo['baz'])

# You can iterate over lists
for item in config.list_of_stuff:
    print(item)

# You can *generally* treat key-value maps as dicts
for key, value in config.foo.items():
    print(key, value)

# You can *actually* turn key-value maps into dicts
dict(config.foo) == config.foo.to_dict()

Installing

# Basic installation
pip install configly

# To use the yaml config loader
pip install configly[yaml]

# To use the toml config loader
pip install configly[toml]

# To use the vault config loader
pip install configly[vault]

Metadata

Release files for configly 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for configly 1.0.0
File Size Uploaded
configly-1.0.0.tar.gz 12.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for configly 1.0.0
File Interpreter ABI Platform
configly-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.0 kB

Release files / configly-1.0.0.tar.gz

Download URL configly-1.0.0.tar.gz
Size 12.1 kB
Tags Source
SHA-256 checksum
How to use checksums
dd2b3d8ed608596a23e97cc10279b5e044ae597491fc9affc5e383a48f5fb4a9
BLAKE2b-256 checksum
How to use checksums
6b437e95749c96d10a1884bb4239d0618293ffeb4b67f5bef4e2310f86203e93
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.9.6 Darwin/21.4.0

Release files / configly-1.0.0-py3-none-any.whl

Download URL configly-1.0.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
025d4d2fc6e6ab7f4032d5c8c9185c73a1a8630fa70e6e228a8cb70d7314c777
BLAKE2b-256 checksum
How to use checksums
c1533a2abb0ded2c778650868b0cb53cd25466f7359db42a8cddc08712689217
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.9.6 Darwin/21.4.0

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page