Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/schireson/configly
Simplify and centralize the loading of configuration, such as environment variables.
https://github.com/schireson/configly
config configuration json schiresonip tidepod yaml
Last synced: 3 months ago
JSON representation
Simplify and centralize the loading of configuration, such as environment variables.
- Host: GitHub
- URL: https://github.com/schireson/configly
- Owner: schireson
- License: mit
- Created: 2019-09-18T19:02:27.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2023-08-04T19:38:03.000Z (over 1 year ago)
- Last Synced: 2024-10-11T20:48:29.481Z (4 months ago)
- Topics: config, configuration, json, schiresonip, tidepod, yaml
- Language: Python
- Homepage:
- Size: 99.6 KB
- Stars: 8
- Watchers: 9
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![Github Actions](https://github.com/schireson/configly/actions/workflows/build.yml/badge.svg) [![codecov](https://codecov.io/gh/schireson/configly/branch/main/graph/badge.svg)](https://codecov.io/gh/schireson/configly) [![Documentation Status](https://readthedocs.org/projects/configly/badge/?version=latest)](https://configly.readthedocs.io/en/latest/?badge=latest)
## TL;DR
```yaml
# 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] %>!
``````python
# app.py
from configly import Configconfig = Config.from_yaml('config.yml')
print(config.foo.bar)
print(config.foo['baz'])
for item in config.list_of_stuff:
print(item)
``````bash
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:
```python
# 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
```python
# config.py
import osclass 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
```python
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.```python
from configly import Config# 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:
```yaml
# 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:
```python
# app.py
from configly import Configconfig = 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
```bash
# 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]
```