From 84e1744d6f1182578de664bd31f456642a618cc3 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Sat, 27 Jun 2026 21:49:20 -0400 Subject: [PATCH] fix: never crash on a bad level= string (v0.1.1) _level_value used logging.getLevelName(name), which returns the string 'Level XXX' for an unknown name; that string then reached setLevel() and raised ValueError, violating the 'never crashes the app over logging' contract. validate the result is an int and fall back to INFO otherwise. verified: level='BOGUS' -> INFO (no crash); 'DEBUG' and int levels still honored. Signed-off-by: disqualifier --- pyproject.toml | 2 +- src/log_setup/__init__.py | 2 +- src/log_setup/setup.py | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ac30a7b..894ec37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "log_setup" -version = "0.1.0" +version = "0.1.1" description = "stdlib app-entry-point logging setup: live run.log, rotation, gzip, retention, consistent format" requires-python = ">=3.10" dependencies = [] diff --git a/src/log_setup/__init__.py b/src/log_setup/__init__.py index ed1dbc7..9717112 100644 --- a/src/log_setup/__init__.py +++ b/src/log_setup/__init__.py @@ -19,4 +19,4 @@ from .setup import setup_logging __all__ = ["setup_logging"] -__version__ = "0.1.0" +__version__ = "0.1.1" diff --git a/src/log_setup/setup.py b/src/log_setup/setup.py index f9f922b..1dcbb12 100644 --- a/src/log_setup/setup.py +++ b/src/log_setup/setup.py @@ -28,7 +28,12 @@ def _level_value(level: Union[int, str]) -> int: """coerce a level name or int to a logging level int (defaults to INFO)""" if isinstance(level, int): return level - return logging.getLevelName(str(level).upper()) if isinstance(level, str) else logging.INFO + if not isinstance(level, str): + return logging.INFO + resolved = logging.getLevelName(level.upper()) + # getLevelName returns the string "Level XXX" for an unknown name, which + # setLevel then rejects — never crash the app over a bad level, fall back to INFO + return resolved if isinstance(resolved, int) else logging.INFO def _clear_owned(root: logging.Logger) -> None: