Skip to content

Commit d3edd60

Browse files
authored
Add tests and better file structure (#2)
* Update pre-commit * Update settings * Update logger and add more tests
1 parent 144032e commit d3edd60

14 files changed

+161
-1204
lines changed

.env.sample

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
ENV="dev"
2-
OTHER_VAR="OIHER VAR"
2+
LOGGER_NAME="project_logger"
3+
LOGGING_LEVEL="INFO"

.pre-commit-config.yaml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ repos:
1111
- id: end-of-file-fixer
1212
- id: trailing-whitespace
1313
- id: check-case-conflict
14-
- id: check-docstring-first
1514
- id: name-tests-test
1615
- id: detect-private-key
1716

@@ -47,14 +46,14 @@ repos:
4746
args: ["--ignore-missing-imports"]
4847
require_serial: true
4948

50-
- repo: local
51-
hooks:
52-
- id: pytest
53-
name: run tests
54-
description: ""
55-
entry: poetry run pytest tests
56-
pass_filenames: false
57-
language: python
58-
types_or: [python, pyi]
59-
args: []
60-
require_serial: true
49+
# - repo: local
50+
# hooks:
51+
# - id: pytest
52+
# name: run tests
53+
# description: ""
54+
# entry: poetry run pytest tests
55+
# pass_filenames: false
56+
# language: python
57+
# types_or: [python, pyi]
58+
# args: []
59+
# require_serial: true

main.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from src.logger import logger
2+
from src.settings import settings
3+
4+
5+
def main() -> None:
6+
logger.info(settings)
7+
8+
9+
if __name__ == "__main__":
10+
main()

poetry.lock

Lines changed: 0 additions & 1167 deletions
This file was deleted.

pyproject.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ build-backend = "poetry.core.masonry.api"
4747
[tool.ruff]
4848
line-length = 122
4949
target-version = "py310"
50-
# All options here: https://github.com/charliermarsh/ruff#supported-rules
5150
select = [
5251
"A",
5352
"B",
@@ -71,24 +70,24 @@ select = [
7170
]
7271

7372

74-
[tool.ruff.per-file-ignores]
75-
"**/tests/*" = ["S101"]
73+
[tool.ruff.lint.per-file-ignores]
74+
"**/tests/*" = ["S101", "SIM117"]
7675
"*init*.py" = ["F401"]
7776

7877

79-
[tool.ruff.isort]
78+
[tool.ruff.lint.isort]
8079
combine-as-imports = true
8180
force-wrap-aliases = true
8281

8382

84-
[tool.ruff.pylint]
83+
[tool.ruff.lint.pylint]
8584
max-args = 8
8685
max-branches = 25
8786
max-returns = 9
8887
max-statements = 30
8988

9089

91-
[tool.isort]
90+
[tool.lint.isort]
9291
profile = "black"
9392
line_length=122
9493
multi_line_output=3

src/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def get_version_from_pyproject(filename: str) -> str:
2+
"""Loads version from .toml file."""
3+
with open(filename) as file:
4+
for line in file:
5+
if not line.strip().startswith("version"):
6+
continue
7+
version = line.split("=")[1].strip().strip('"').strip("'")
8+
return version
9+
10+
return ""
11+
12+
13+
__version__ = get_version_from_pyproject("./pyproject.toml")

src/logger.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import logging
2+
3+
from .settings import settings
4+
5+
__all__ = [
6+
"logger",
7+
]
8+
9+
10+
def _create_logger() -> logging.Logger:
11+
"""Creates generic logger with the given `logger_name` and `logging_level`"""
12+
13+
logger = logging.getLogger(settings.logger_name)
14+
logger.setLevel(settings.logging_level)
15+
16+
console_handler = logging.StreamHandler()
17+
console_handler.setLevel(settings.logging_level)
18+
19+
formatter = logging.Formatter(
20+
fmt="%(levelname)s [%(asctime)s %(name)s]: %(message)s",
21+
datefmt="%m/%d %H:%M:%S",
22+
)
23+
console_handler.setFormatter(formatter)
24+
25+
logger.addHandler(console_handler)
26+
return logger
27+
28+
29+
logger = _create_logger()
30+
"""Logger that can be imported and be used globally."""

src/main.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/settings.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
1-
from enum import StrEnum
1+
from enum import StrEnum, auto
2+
from typing import Literal
23

34
from dotenv import load_dotenv
45
from pydantic_settings import BaseSettings
56

7+
__all__ = [
8+
"Environment",
9+
"settings",
10+
]
11+
612

713
class Environment(StrEnum):
8-
DEV = "dev"
9-
PROD = "prod"
14+
DEV = auto()
15+
PROD = auto()
16+
17+
18+
LoggingLevel = Literal[
19+
"CRITICAL",
20+
"FATAL",
21+
"ERROR",
22+
"WARNING",
23+
"WARN",
24+
"INFO",
25+
"DEBUG",
26+
"NOTSET",
27+
]
1028

1129

1230
class Settings(BaseSettings):
1331
"""Global application settings loaded from the .env file."""
1432

1533
env: Environment = Environment.DEV
16-
other_var: str = "other"
34+
logger_name: str = "project_logger"
35+
logging_level: LoggingLevel = "INFO"
1736

1837

1938
load_dotenv(override=True)

tests/core/__init__.py

Whitespace-only changes.

tests/core/logger_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def test_logger_logging() -> None:
2+
"""Test if the logger correctly logs a message."""
3+
from src.logger import logger
4+
5+
test_message = "This is a test message"
6+
logger.info(test_message)
7+
logger.setLevel("DEBUG")

tests/core/settings_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import os
2+
from unittest.mock import patch
3+
4+
import pytest
5+
from pydantic_core import ValidationError
6+
7+
from src.settings import Environment, Settings
8+
9+
10+
def test_environment_override() -> None:
11+
"""Test overriding settings through environment variables."""
12+
with patch.dict(os.environ, {"ENV": "prod", "LOGGER_NAME": "test_logger", "LOGGING_LEVEL": "DEBUG"}):
13+
settings = Settings()
14+
assert settings.env == Environment.PROD
15+
assert settings.logger_name == "test_logger"
16+
assert settings.logging_level == "DEBUG"
17+
18+
19+
def test_invalid_environment() -> None:
20+
"""Test setting an invalid environment variable."""
21+
with patch.dict(os.environ, {"ENV": "INVALID_ENV"}):
22+
with pytest.raises(ValidationError):
23+
Settings()
24+
25+
26+
def test_invalid_logging_level() -> None:
27+
"""Test setting an invalid logging level."""
28+
with patch.dict(os.environ, {"LOGGING_LEVEL": "INVALID_LEVEL"}):
29+
with pytest.raises(ValidationError):
30+
Settings()

tests/core/version_reader_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
from src import get_version_from_pyproject
6+
7+
8+
def path_to_pyproject(tmp_path: Path) -> Path:
9+
temp_dir = tmp_path / "sub"
10+
temp_dir.mkdir()
11+
file = temp_dir / "pyproject.toml"
12+
return file
13+
14+
15+
def test_get_version_from_pyproject(path_to_pyproject: Path) -> None:
16+
version = "1.2.3"
17+
path_to_pyproject.write_text(f"version = '{version}'")
18+
result = get_version_from_pyproject(str(path_to_pyproject))
19+
assert version == result
20+
21+
22+
def test_get_version_from_empty_pyproject(path_to_pyproject: Path) -> None:
23+
path_to_pyproject.write_text("")
24+
version = get_version_from_pyproject(str(path_to_pyproject))
25+
assert version == ""
26+
27+
28+
def test_get_version_from_nonexistent_file() -> None:
29+
with pytest.raises(FileNotFoundError):
30+
get_version_from_pyproject("nonexistent_file.toml")

tests/settings_test.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)