Skip to content

Commit

Permalink
Merge pull request #212 from lsst-sqre/tickets/DM-23878
Browse files Browse the repository at this point in the history
DM-23878: Allow log level enum with any case
  • Loading branch information
rra authored Oct 3, 2023
2 parents 15ed2c0 + 8521968 commit 859d7ab
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 2 deletions.
3 changes: 3 additions & 0 deletions changelog.d/20231003_135049_rra_DM_23878.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### New features

- Allow the `safir.logging.LogLevel` enum to be created from strings of any case, which will allow the logging level to be specified with any case for Safir applications that use Pydantic to validate the field.
19 changes: 17 additions & 2 deletions src/safir/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import re
import sys
from enum import Enum
from typing import Any
from typing import Any, Self

import structlog
from structlog.stdlib import add_log_level
Expand Down Expand Up @@ -48,14 +48,29 @@ class Profile(Enum):


class LogLevel(Enum):
"""Python logging level."""
"""Python logging level.
Any case variation is accepted when converting a string to an enum value
via the class constructor.
"""

DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"

@classmethod
def _missing_(cls, value: Any) -> Self | None:
"""Allow strings in any case to be used to create the enum."""
if not isinstance(value, str):
return None
value = value.upper()
for member in cls:
if member.value == value:
return member
return None


def add_log_severity(
logger: logging.Logger, method_name: str, event_dict: EventDict
Expand Down
20 changes: 20 additions & 0 deletions tests/logging_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from _pytest.capture import CaptureFixture
from _pytest.logging import LogCaptureFixture
from httpx import AsyncClient
from pydantic import BaseModel, ValidationError

from safir import logging as safir_logging
from safir.logging import LogLevel, Profile, configure_logging
Expand Down Expand Up @@ -48,6 +49,25 @@ def _strip_color(string: str) -> str:
return re.sub(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]", "", string)


def test_log_level_enum() -> None:
"""Test that any case is allowed when initializing the enum."""
assert LogLevel("warning") == LogLevel.WARNING
assert LogLevel("WARNING") == LogLevel.WARNING
assert LogLevel("Error") == LogLevel.ERROR

# Check that this also works when going through Pydantic.
class Model(BaseModel):
log_level: LogLevel

model = Model.model_validate({"log_level": "warning"})
assert model.log_level == LogLevel.WARNING
model = Model.model_validate({"log_level": "inFO"})
assert model.log_level == LogLevel.INFO

with pytest.raises(ValidationError):
Model.model_validate({"log_level": "unknown"})


def test_configure_logging_development(caplog: LogCaptureFixture) -> None:
"""Test that development-mode logging is key-value formatted."""
caplog.set_level(logging.INFO)
Expand Down

0 comments on commit 859d7ab

Please sign in to comment.