# allow: no-sut-import — guardian; sweeps src/ to forbid printf-style placeholders in loguru calls import ast import importlib.util from pathlib import Path HOOKS_DIR = Path(__file__).resolve().parents[2] / ".pre-commit-hooks" HOOK_PATH = HOOKS_DIR / "check-loguru-formatting.py" # Reuse the pre-commit hook's logic directly so the guardian and the hook # cannot diverge (they previously drifted: both gated on a top-level raw # loguru import and were blind to the security.secure_logging wrapper). spec = importlib.util.spec_from_file_location( "check_loguru_formatting", HOOK_PATH ) hook = importlib.util.module_from_spec(spec) assert spec.loader is not None spec.loader.exec_module(hook) def test_loguru_calls_do_not_use_printf_style_placeholders(): """Guard project logger calls against stdlib printf formatting. Covers both raw ``from loguru import logger`` files and files using the diagnose-gated ``security.secure_logging`` wrapper, including ``logger.bind(...)``/``logger.patch(...)`` chains and function-local imports. """ src_root = Path(__file__).resolve().parents[2] / "src" failures = [] for path in src_root.rglob("*.py"): source = path.read_text() tree = ast.parse(source) if not hook.imports_project_logger(tree): continue for lineno, message in hook.find_printf_violations(tree): failures.append( f"{path.relative_to(src_root.parent)}:{lineno} -> {message!r}" ) assert not failures, ( "found loguru calls using printf-style placeholders:\n" + "\n".join(sorted(failures)) ) def test_hook_gate_sees_wrapper_and_local_imports(): """Regression: the import gate must accept wrapper + local imports.""" gated = [ "from loguru import logger\n", "from ...security.secure_logging import logger\n", "from local_deep_research.security.secure_logging import logger\n", "def f():\n from ..security.secure_logging import logger\n", ] for code in gated: assert hook.imports_project_logger(ast.parse(code)), code skipped = [ "import os\n", # suffix lookalike must not count as the wrapper "from notsecurity.secure_logging import logger\n", ] for code in skipped: assert not hook.imports_project_logger(ast.parse(code)), code def test_hook_flags_printf_in_direct_and_chained_calls(): flagged = [ 'logger.info("value: %s", x)\n', 'logger.bind(a=1).warning("value: %s", x)\n', 'logger.patch(p).bind(a=1).error("count: %d", n)\n', 'logger.log("INFO", "value: %s", x)\n', ] for code in flagged: assert hook.find_printf_violations(ast.parse(code)), code clean = [ 'logger.info("value: {}", x)\n', 'logger.bind(a=1).warning("value: {}", x)\n', # message without extra args is loguru-legal even with a % in it 'logger.info("100%s")\n', # non-logger receivers are out of scope 'other.info("value: %s", x)\n', ] for code in clean: assert not hook.find_printf_violations(ast.parse(code)), code