Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from axelfahy/feat/config
feat(config): add class `FancyConfig` to handle configurations
- Loading branch information
Showing
8 changed files
with
207 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
""" | ||
FancyConfig, configuration loader. | ||
Tool to load the configuration from a configuration file (`config.yml`). | ||
""" | ||
from collections.abc import Mapping | ||
import logging | ||
from pathlib import Path | ||
import pprint | ||
import yaml | ||
|
||
LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class FancyConfig(Mapping): | ||
""" | ||
Class to load the configuration file. | ||
This class behaves like a dictionary that loads a | ||
configuration file in yaml format. | ||
If the configuration file does not exist, creates it from template. | ||
Examples | ||
-------- | ||
>>> config = FancyConfig() | ||
>>> print(config) | ||
{ 'database': { 'host': '127.0.0.1', | ||
'name': 'porgs', | ||
'port': 3306, | ||
'pwd': 'bacca', | ||
'user': 'Chew'}, | ||
'env': 'prod', | ||
'imports': {'star_wars': ['ewok', 'bantha']}} | ||
""" | ||
|
||
def __init__(self, path_config_to_load: Path = Path.home().joinpath('.config/fancyconfig.yml'), | ||
default_config_path: Path = (Path(__file__).resolve() | ||
.parent.joinpath('config.yml'))): | ||
""" | ||
Initialization of configuration. | ||
If the folder to store the configuration does not exist, create it. | ||
If configuration file does not exist, copy it from default one. | ||
Parameters | ||
---------- | ||
path_config_to_load : Path, default '~/.config/' | ||
Directory to store the configuration file and load the configuration from. | ||
default_config_path: Path, default 'config.yml' current directory. | ||
Name of the configuration file. | ||
""" | ||
# Create config file if does not exist. | ||
if not path_config_to_load.exists(): | ||
LOGGER.info((f'Configuration file does not exist, ' | ||
f'creating it from {default_config_path}')) | ||
# Creating folder of configuration (parent of file). | ||
path_config_to_load.parent.mkdir(parents=True, exist_ok=True) | ||
# Copy the configuration file. | ||
path_config_to_load.write_bytes(default_config_path.read_bytes()) | ||
|
||
with path_config_to_load.open(mode='r', encoding='utf-8') as yaml_config_file: | ||
self._config = yaml.load(yaml_config_file, Loader=yaml.FullLoader) | ||
|
||
def __getitem__(self, item): | ||
"""Getter of the class.""" | ||
try: | ||
return self._config[item] | ||
except KeyError: | ||
LOGGER.error(f'Configuration for {item} does not exist.') | ||
|
||
def __iter__(self): | ||
"""Iterator of the class.""" | ||
return iter(self._config) | ||
|
||
def __len__(self): | ||
"""Lenght of the config.""" | ||
return len(self._config) | ||
|
||
def __str__(self): | ||
""" | ||
__str__ method. | ||
Pretty representation of the config. | ||
""" | ||
pretty = pprint.PrettyPrinter(indent=2) | ||
return pretty.pformat(self._config) | ||
|
||
def __repr__(self): | ||
"""Representation of the config.""" | ||
return f'{super().__repr__}\n{str(self._config)}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Configuration file example. | ||
|
||
# Database information | ||
database: | ||
user: Chew | ||
pwd: bacca | ||
host: 127.0.0.1 | ||
port: 3306 | ||
name: porgs | ||
|
||
imports: | ||
star_wars: | ||
- ewok | ||
- bantha | ||
|
||
env: prod |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FancyConfig | ||
=========== | ||
|
||
.. automodule:: bff.FancyConfig | ||
:members: __init__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,3 +29,5 @@ Contents | |
|
||
fancy | ||
|
||
config | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ exclude = | |
versioneer.py, | ||
bff/_version.py, | ||
setup.py | ||
doc | ||
|
||
[tool:pytest] | ||
testpaths = bff | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Test of config module | ||
This module test the configuration loader | ||
""" | ||
import filecmp | ||
from pathlib import Path | ||
import unittest | ||
|
||
from bff.config import FancyConfig | ||
|
||
|
||
class TestiFancyConfig(unittest.TestCase): | ||
""" | ||
Unittest of config module. | ||
""" | ||
|
||
def test_create_config(self): | ||
""" | ||
Test of loading the config when it does not exists. | ||
""" | ||
# Default configuration | ||
config_default = FancyConfig() | ||
|
||
default_path_ok = Path(__file__).resolve().parent.parent.joinpath('bff/config.yml') | ||
default_path_ko = Path.home().resolve().joinpath('config.yml') | ||
|
||
dest = Path(__file__).resolve().parent.joinpath('config.yml') | ||
config_a = FancyConfig(dest, default_path_ok) | ||
|
||
# Check if the file is correctly copied. | ||
self.assertTrue(filecmp.cmp(dest, default_path_ok)) | ||
|
||
# Check if the file is not overridden. | ||
path_conf_b = Path(__file__).resolve().parent.joinpath('conf_b.yml') | ||
with path_conf_b.open(mode='w', encoding='utf-8') as f: | ||
f.write('testfile: True') | ||
config_b = FancyConfig(dest, path_conf_b) | ||
|
||
self.assertFalse(filecmp.cmp(dest, path_conf_b)) | ||
# Check that the configurations are the same. | ||
self.assertEqual(config_a, config_b) | ||
|
||
# Removes the configs. | ||
dest.unlink() | ||
path_conf_b.unlink() | ||
|
||
# Create a config that is not called config. | ||
config_c = FancyConfig(path_conf_b, default_path_ok) | ||
self.assertEqual(config_a, config_c) | ||
|
||
# Remove the files. | ||
path_conf_b.unlink() | ||
|
||
# If default config path is wrong, should fail. | ||
with self.assertRaises(FileNotFoundError): | ||
FancyConfig(dest, default_path_ko) | ||
|
||
def test_access_config(self): | ||
""" | ||
Test the access of the configuration once loaded. | ||
Should be accessible as a property. | ||
""" | ||
default_path_ok = Path(__file__).resolve().parent.parent.joinpath('bff/config.yml') | ||
dest = Path(__file__).resolve().parent.joinpath('config.yml') | ||
config = FancyConfig(dest, default_path_ok) | ||
|
||
self.assertEqual(config['env'], 'prod') | ||
self.assertEqual(config['database']['user'], 'Chew') | ||
self.assertEqual(config['imports']['star_wars'], ['ewok', 'bantha']) | ||
|
||
# Check the error message using a mock. | ||
with unittest.mock.patch('logging.Logger.error') as mock_logging: | ||
config['error'] | ||
mock_logging.assert_called_with('Configuration for error does not exist.') | ||
|
||
# Remove the file. | ||
dest.unlink() |