Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using configparser instead of dynaconf to manage the settings #39

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down Expand Up @@ -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).
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = "MIT"

[tool.poetry.dependencies]
python = "^3.11"
dynaconf = "3.2.3"

[tool.poetry.dev-dependencies]
pytest = "7.4.3"
Expand All @@ -16,6 +15,7 @@ ruff = "^0.0.291"

[tool.pytest.ini_options]
testpaths = ["tests",]
pythonpath = ["src",]

[tool.coverage.run]
branch = true
Expand Down
1 change: 1 addition & 0 deletions settings.toml → settings.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[default]
app_name=python-boilerplate-project

[development]

Expand Down
32 changes: 24 additions & 8 deletions src/config/settings.py
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 3 additions & 1 deletion src/main.py
Original file line number Diff line number Diff line change
@@ -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"


Expand Down
17 changes: 17 additions & 0 deletions tests/config/settings_to_test.conf
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions tests/config/test_settings.py
Original file line number Diff line number Diff line change
@@ -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')
Loading