Skip to content

Commit

Permalink
v0.0.2 🌵
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertoPrevato committed Aug 11, 2021
1 parent 7058ffb commit 6e80e97
Show file tree
Hide file tree
Showing 20 changed files with 158 additions and 111 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,18 @@ jobs:
- name: Install distribution dependencies
run: pip install --upgrade twine setuptools wheel
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
if: matrix.python-version == 3.9

- name: Create distribution package
run: python setup.py sdist bdist_wheel
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
if: matrix.python-version == 3.9

- name: Upload distribution package
uses: actions/upload-artifact@master
with:
name: dist-package-${{ matrix.python-version }}
path: dist
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
if: matrix.python-version == 3.9

publish:
runs-on: ubuntu-18.04
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@ celerybeat-schedule
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
Expand All @@ -107,4 +104,4 @@ venv.bak/

test-cov.xml
test-output.xml
*.py,cover
*.py,cover
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2021-08-09 :cactus:
- Forks new project from [roconfiguration](https://github.com/Neoteroi/roconfiguration), with name `essentials-configuration`
- New code API to support extensions
## [0.0.2] - 2021-08-11 :cactus:
- Forks a new project from
[roconfiguration](https://github.com/Neoteroi/roconfiguration), with name
`essentials-configuration`
- Implements a new code API that better supports extensions
- Makes `PyYAML` an optional dependency, necessary only if the user desires to
use YAML files
- Applies `isort` and enforces `isort` and `black` checks in CI pipeline
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include README.md
include LICENSE
include LICENSE
recursive-include configuration py.typed
16 changes: 14 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@ artifacts: test
python setup.py sdist bdist_wheel


release: test
python setup.py sdist upload
clean:
rm -rf dist/


prepforbuild:
pip install --upgrade twine setuptools wheel


testrelease:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*


release: clean artifacts
twine upload --repository-url https://upload.pypi.org/legacy/ dist/*


test:
Expand Down
137 changes: 79 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![pypi](https://img.shields.io/pypi/v/essentials-configuration.svg)](https://pypi.python.org/pypi/essentials-configuration)
[![versions](https://img.shields.io/pypi/pyversions/essentials-configuration.svg)](https://github.com/Neoteroi/essentials-configuration)
[![codecov](https://codecov.io/gh/Neoteroi/essentials-configuration/branch/main/graph/badge.svg?token=VzAnusWIZt)](https://codecov.io/gh/Neoteroi/essentials-configuration)
[![license](https://img.shields.io/github/license/Neoteroi/essentials-configuration.svg)](https://github.com/Neoteroi/essentials-configuration/blob/master/LICENSE)
[![license](https://img.shields.io/github/license/Neoteroi/essentials-configuration.svg)](https://github.com/Neoteroi/essentials-configuration/blob/main/LICENSE)

# Python configuration utilities
Implementation of key-value pair based configuration for Python applications.
Expand All @@ -18,26 +18,42 @@ This library is freely inspired by .NET Core `Microsoft.Extensions.Configuration
The main class is influenced by Luciano Ramalho`s example of
JSON structure explorer using attribute notation, in his book [Fluent Python](http://shop.oreilly.com/product/0636920032519.do).

## Overview

`essentials-configuration` provides a way to handle configuration roots
composed of different layers, such as configuration files and environmental
variables. Layers are applied in order and can override each others' values,
enabling different scenarios like configuration by environment and system
instance.

## Supported sources:
* **yaml** files
* **json** files
* **ini** files
* environmental variables
* dictionaries
* keys and values
* [Azure Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/basic-concepts), using [essentials-configuration-keyvault](https://github.com/Neoteroi/essentials-configuration)
* custom sources, implementing the `ConfigurationSource` interface

## Installation

```bash
pip install essentials-configuration
```

Alternatively, to install it with support for `YAML` configuration files:
To install with support for `YAML` configuration files:

```
pip install essentials-configuration[yaml]
```

## Extensions

* Azure Key Vault secrets configuration source:
[essentials-configuration-keyvault](https://github.com/Neoteroi/essentials-configuration-keyvault)


# Examples

### JSON file and environmental variables
Expand All @@ -48,14 +64,14 @@ Settings are applied in order, so environmental variables with matching name
override values from the `json` file.

```python
from configuration import ConfigurationBuilder
from configuration.common import ConfigurationBuilder
from configuration.json import JSONFile
from configuration.env import EnvironmentalVariables

builder = ConfigurationBuilder()
from configuration.env import EnvironmentVariables

builder.add_source(JSONFile("settings.json"))
builder.add_source(EnvironmentalVariables(prefix="APP_"))
builder = ConfigurationBuilder(
JSONFile("settings.json"),
EnvironmentVariables(prefix="APP_")
)

config = builder.build()
```
Expand All @@ -76,7 +92,7 @@ And the environment has a variable named `APP_foo=AAA`:

```python
>>> config
<Configuration {'logging': {'level': 'INFO'}, 'example': 'Hello World', 'foo': 'AAA'}>
<Configuration {'logging': '...', 'example': '...', 'foo': '...'}>
>>> config.foo
'AAA'
>>> config.logging.level
Expand All @@ -91,14 +107,14 @@ environmental variables with matching name override values from the `yaml` file


```python
from configuration import ConfigurationBuilder
from configuration.env import EnvironmentalVariables
from configuration.common import ConfigurationBuilder
from configuration.env import EnvironmentVariables
from configuration.yaml import YAMLFile

builder = ConfigurationBuilder()

builder.add_source(YAMLFile("settings.yaml"))
builder.add_source(EnvironmentalVariables())
builder.add_source(EnvironmentVariables())

config = builder.build()
```
Expand All @@ -111,8 +127,8 @@ present, it is read to override values configured in `settings.yaml` file.
```python
import os

from configuration import ConfigurationBuilder
from configuration.env import EnvironmentalVariables
from configuration.common import ConfigurationBuilder
from configuration.env import EnvironmentVariables
from configuration.yaml import YAMLFile

environment_name = os.environ["APP_ENVIRONMENT"]
Expand All @@ -123,15 +139,15 @@ builder.add_source(YAMLFile("settings.yaml"))

builder.add_source(YAMLFile(f"settings.{environment_name}.yaml", optional=True))

builder.add_source(EnvironmentalVariables(prefix="APP_"))
builder.add_source(EnvironmentVariables(prefix="APP_"))

config = builder.build()
```

### Filtering environmental variables by prefix

```python
from configuration import Configuration
from configuration.common import Configuration

config = Configuration()

Expand All @@ -147,7 +163,7 @@ INI files are parsed using the built-in `configparser` module, therefore
support `[DEFAULT]` section; all values are kept as strings.

```python
from configuration import ConfigurationBuilder
from configuration.common import ConfigurationBuilder
from configuration.ini import INIFile

builder = ConfigurationBuilder()
Expand All @@ -160,7 +176,7 @@ config = builder.build()
### Dictionaries

```python
from configuration import ConfigurationBuilder
from configuration.common import ConfigurationBuilder

builder = ConfigurationBuilder()

Expand All @@ -180,7 +196,7 @@ assert config.example[1].id == 2
### Keys and values

```python
from configuration import ConfigurationBuilder
from configuration.common import ConfigurationBuilder

builder = ConfigurationBuilder()

Expand All @@ -203,24 +219,22 @@ dictionary keys using the following notation for sub properties:
* keys separated by "__", such as `a__d__e`

```python
from configuration import ConfigurationBuilder, MapSource
from configuration.common import ConfigurationBuilder, MapSource


builder = ConfigurationBuilder(
[
MapSource(
{
"a": {
"b": 1,
"c": 2,
"d": {
"e": 3,
"f": 4,
},
}
MapSource(
{
"a": {
"b": 1,
"c": 2,
"d": {
"e": 3,
"f": 4,
},
}
)
]
}
)
)

config = builder.build()
Expand All @@ -243,20 +257,18 @@ assert config.a.d.f == 4
import os

builder = ConfigurationBuilder(
[
MapSource(
{
"a": {
"b": 1,
"c": 2,
"d": {
"e": 3,
"f": 4,
},
}
MapSource(
{
"a": {
"b": 1,
"c": 2,
"d": {
"e": 3,
"f": 4,
},
}
)
]
}
)
)

config = builder.build()
Expand All @@ -274,7 +286,7 @@ assert config.a.d.f == 4

os.environ["a__d__e"] = "5"

builder.sources.append(EnvironmentalVariables())
builder.sources.append(EnvironmentVariables())

config = builder.build()

Expand All @@ -285,17 +297,15 @@ assert config.a.d.e == "5"

```python
builder = ConfigurationBuilder(
[
MapSource(
{
"b2c": [
{"tenant": "1"},
{"tenant": "2"},
{"tenant": "3"},
]
}
)
]
MapSource(
{
"b2c": [
{"tenant": "1"},
{"tenant": "2"},
{"tenant": "3"},
]
}
)
)

builder.add_value("b2c:1:tenant", "4")
Expand All @@ -307,3 +317,14 @@ assert config.b2c[1].tenant == "4"
assert config.b2c[2].tenant == "3"

```

### Goal and non-goals
The goal of this package is to provide a way to handle configuration roots,
fetching and composing settings from different sources, usually happening
once at application's start.

The library implements only a synchronous API and fetching of application
settings atomically (it doesn't support generators), like application settings
fetched from INI, JSON, or YAML files that are read once in memory entirely.
An asynchronous API is currently out of the scope of this library, since its
primary use case is to fetch configuration values once at application's start.
Loading

0 comments on commit 6e80e97

Please sign in to comment.