From 389881574856e04733550eb389e49b1121a7d69b Mon Sep 17 00:00:00 2001 From: Marciel Torres Date: Fri, 24 Nov 2023 12:19:08 -0300 Subject: [PATCH] Using configparser instead of dynaconf to manage the settings (#39) --- Makefile | 7 +++-- README.md | 5 +++- pyproject.toml | 2 +- settings.toml => settings.conf | 1 + src/config/settings.py | 32 +++++++++++++++++------ src/main.py | 4 ++- tests/config/settings_to_test.conf | 17 +++++++++++++ tests/config/test_settings.py | 41 ++++++++++++++++++++++++++++++ 8 files changed, 94 insertions(+), 15 deletions(-) rename settings.toml => settings.conf (56%) create mode 100644 tests/config/settings_to_test.conf create mode 100644 tests/config/test_settings.py diff --git a/Makefile b/Makefile index 5c90c54..8a3daac 100644 --- a/Makefile +++ b/Makefile @@ -8,14 +8,13 @@ local/install: generate-default-env-file poetry install local/tests: - poetry run pytest --cov-report=html --cov-report=term --cov . + poetry run pytest -s --cov-report=html --cov-report=term --cov . local/lint: poetry run ruff check . - poetry run ruff . --fix --exit-non-zero-on-fix - + local/lint/fix: - poetry run black . + poetry run ruff . --fix --exit-non-zero-on-fix local/run: poetry run python src/main.py diff --git a/README.md b/README.md index bd04f5f..5f05211 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ A python boilerplate project using poetry - [Docker Compose](https://docs.docker.com/compose/) - **pre-requisite** - [Poetry](https://python-poetry.org/) - **pre-requisite** - [Ruff](https://github.com/astral-sh/ruff) -- [Dynaconf](https://www.dynaconf.com/) *Please pay attention on **pre-requisites** resources that you must install/configure.* @@ -40,3 +39,7 @@ run | `make docker/run` | `make local/run` | to run the project ## Logging This project uses a simple way to configure the log with [logging.conf](logging.conf) to show the logs on the container output console. + +## Settings + +This project uses a simple way to manage the settings with [settings.conf](settings.conf) and [ConfigParser](https://docs.python.org/3/library/configparser.html) using a [config class](./src/config/settings.py). diff --git a/pyproject.toml b/pyproject.toml index 9c35489..d4a0cdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.11" -dynaconf = "3.2.3" [tool.poetry.dev-dependencies] pytest = "7.4.3" @@ -16,6 +15,7 @@ ruff = "^0.0.291" [tool.pytest.ini_options] testpaths = ["tests",] +pythonpath = ["src",] [tool.coverage.run] branch = true diff --git a/settings.toml b/settings.conf similarity index 56% rename from settings.toml rename to settings.conf index 1120f51..537bb00 100644 --- a/settings.toml +++ b/settings.conf @@ -1,4 +1,5 @@ [default] +app_name=python-boilerplate-project [development] diff --git a/src/config/settings.py b/src/config/settings.py index 456d7a1..0ab4f1f 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -1,8 +1,24 @@ -from dynaconf import Dynaconf - -settings = Dynaconf( - settings_files=["./settings.toml"], - environments=True, - load_dotenv=True, - env_switcher="ENV", -) +from configparser import ConfigParser +from os import getenv +from typing import Any + + +class Settings: + __slots__ = ['config_parser', 'env'] + config_parser: ConfigParser + env: str + + def __init__(self, file: str = 'settings.conf'): + self.config_parser = ConfigParser() + self.config_parser.read(file) + self.env = getenv('ENV', 'development') + + def get(self, name: str, default_value: Any = None) -> Any: + return self._get_from_section(self.env, name) or self._get_from_section('default', name) or default_value + + def _get_from_section(self, section: str, var: str) -> Any: + if section in self.config_parser and var in self.config_parser[section]: + return self.config_parser[section][var] + return None + +settings = Settings() diff --git a/src/main.py b/src/main.py index 0362527..73d958d 100755 --- a/src/main.py +++ b/src/main.py @@ -1,12 +1,14 @@ from logging import getLogger from logging.config import fileConfig as logConfig +from config.settings import settings + logConfig("./logging.conf", disable_existing_loggers=False) logger = getLogger(__name__) def hello() -> str: - logger.info("Hello") + logger.info(f"Hello from {settings.get('app_name')}") return "Hello" diff --git a/tests/config/settings_to_test.conf b/tests/config/settings_to_test.conf new file mode 100644 index 0000000..3dd910a --- /dev/null +++ b/tests/config/settings_to_test.conf @@ -0,0 +1,17 @@ +[default] +app_name=app-name +app_var=default-app-var +sample_of_int_var=10 +sample_of_float_var=10.10 + +[development] +app_var=development-app-var + +[test] +app_var=test-app-var + +[qa] +app_var=qa-app-var + +[prod] +app_var=prod-app-var diff --git a/tests/config/test_settings.py b/tests/config/test_settings.py new file mode 100644 index 0000000..3428ea4 --- /dev/null +++ b/tests/config/test_settings.py @@ -0,0 +1,41 @@ +import os +from unittest import TestCase, mock + +from src.config.settings import Settings + + +class SettingsTest(TestCase): + def setUp(self) -> None: + self.settings = Settings(file='./tests/config/settings_to_test.conf') + + def test_get_setting_value_with_success(self): + self.assertEqual(self.settings.get('app_name'), 'app-name') + + def test_get_default_value_when_setting_not_found_with_success(self): + self.assertEqual(self.settings.get('not_found_var', 'default_value'), 'default_value') + + def test_get_setting_int_value_with_success(self): + self.assertEqual(int(self.settings.get('sample_of_int_var')), 10) + + def test_get_setting_float_value_with_success(self): + self.assertEqual(float(self.settings.get('sample_of_float_var')), 10.10) + + @mock.patch.dict(os.environ, {'ENV': 'prod'}, clear=True) + def test_get_setting_value_from_production_env_with_success(self): + prod_settings = Settings(file='./tests/config/settings_to_test.conf') + self.assertEqual(prod_settings.get('app_var'), 'prod-app-var') + + @mock.patch.dict(os.environ, {'ENV': 'qa'}, clear=True) + def test_get_setting_value_from_qa_env_with_success(self): + qa_settings = Settings(file='./tests/config/settings_to_test.conf') + self.assertEqual(qa_settings.get('app_var'), 'qa-app-var') + + @mock.patch.dict(os.environ, {'ENV': 'test'}, clear=True) + def test_get_setting_value_from_test_env_with_success(self): + test_settings = Settings(file='./tests/config/settings_to_test.conf') + self.assertEqual(test_settings.get('app_var'), 'test-app-var') + + @mock.patch.dict(os.environ, {'ENV': 'development'}, clear=True) + def test_get_setting_value_from_development_env_with_success(self): + dev_settings = Settings(file='./tests/config/settings_to_test.conf') + self.assertEqual(dev_settings.get('app_var'), 'development-app-var')