diff --git a/pagerite/__main__.py b/pagerite/__main__.py index f6d37a5..1019d49 100644 --- a/pagerite/__main__.py +++ b/pagerite/__main__.py @@ -55,6 +55,16 @@ def main() -> None: default_port=DEFAULT_PORT, server_header=False, reload=Path(__file__).parent if env.dev else False, + # Partial log config, merged over uvicorn's default by fastapi-vue: + # root prints at WARNING in production / INFO in dev. Keep our own + # loggers audible in production, and silence httpx's per-request INFO + # (tracking._schedule_favicon_fetch logs its own one-line summary). + log_config={ + "loggers": { + "pagerite": {"level": "INFO"}, + "httpx": {"level": "WARNING"}, + } + }, **run_args, ) diff --git a/pagerite/tracking.py b/pagerite/tracking.py index 0b652a8..9a2fc15 100644 --- a/pagerite/tracking.py +++ b/pagerite/tracking.py @@ -35,10 +35,6 @@ from pagerite.state import SITE_URL, _html_response, analytics_store, data logger = logging.getLogger(__name__) -# httpx logs every request at INFO (e.g. the favicon fetches below); our own -# one-line summary in _schedule_favicon_fetch replaces that noise. -logging.getLogger("httpx").setLevel(logging.WARNING) - router = APIRouter() # Live WebSocket clients for the analytics stream. diff --git a/pyproject.toml b/pyproject.toml index c1656a1..75543ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,15 +17,15 @@ readme = "README.md" requires-python = ">=3.14" dependencies = [ "blake3>=1.0.9", - "fastapi-vue~=1.6.1", + "fastapi-vue~=1.7.1", "fastapi[standard]>=0.141.1", "html5tagger>=2.0.0", "httpx>=0.28.1", - "kanta>=0.9.0", + "kanta>=0.9.2", "markdown-it-py>=4.2.0", "maxminddb>=3.1.1", "mdit-py-plugins>=0.6.1", - "mediapreview[standard]>=0.2.3", + "mediapreview[standard]>=0.2.5", "platformdirs>=4.11.5", "pygments>=2.20.0", "python-slugify>=8.0.4", diff --git a/scripts/fastapi-vue/buildhook.py b/scripts/fastapi-vue/buildhook.py index db91983..407e4bf 100644 --- a/scripts/fastapi-vue/buildhook.py +++ b/scripts/fastapi-vue/buildhook.py @@ -1,3 +1,4 @@ +# ruff: noqa: INP001 """Hatch build hook for building Vue frontend during package build.""" import sys diff --git a/scripts/fastapi-vue/buildutil.py b/scripts/fastapi-vue/buildutil.py index 9f6875b..5241b00 100644 --- a/scripts/fastapi-vue/buildutil.py +++ b/scripts/fastapi-vue/buildutil.py @@ -1,3 +1,4 @@ +# ruff: noqa: INP001 """Utilities used at build time and in devserver script. No dependencies.""" import logging @@ -9,21 +10,31 @@ from pathlib import Path MIN_NODE_VERSION = 20 +# Duplicated from fastapi_vue.logging because build environment is isolated +_LEVEL_EMOJI = { + logging.DEBUG: "🐛", + logging.INFO: "🔷", + logging.WARNING: "❗", + logging.ERROR: "🛑", + logging.CRITICAL: "🚨", +} -class _PrefixFormatter(logging.Formatter): - """Formatter that adds prefix based on log level.""" + +class _Formatter(logging.Formatter): + """Emoji level prefix formatter, mirroring fastapi_vue.logging.Formatter.""" def format(self, record: logging.LogRecord) -> str: - if record.levelno >= logging.WARNING: - return f"⚠️ {record.getMessage()}" - return record.getMessage() + emoji = _LEVEL_EMOJI.get(record.levelno) + prefix = f"{emoji} " if emoji else f"{record.levelname}: " + return prefix + record.getMessage() _handler = logging.StreamHandler() -_handler.setFormatter(_PrefixFormatter()) +_handler.setFormatter(_Formatter()) logger = logging.getLogger("fastapi-vue") logger.addHandler(_handler) logger.setLevel(logging.INFO) +logger.propagate = False # own handler; do not double-print via a configured root def _check_node_version(node_path: str) -> None: @@ -32,7 +43,7 @@ def _check_node_version(node_path: str) -> None: Raises RuntimeError if version is too old or cannot be determined. """ try: - result = subprocess.run( + result = subprocess.run( # noqa: S603 [node_path, "--version"], capture_output=True, text=True, @@ -220,7 +231,7 @@ def build(folder: str = "frontend") -> None: def run(cmd: list[str]) -> None: display_cmd = [Path(cmd[0]).stem, *cmd[1:]] logger.info("### %s", " ".join(display_cmd)) - subprocess.run(cmd, check=True, cwd=folder) + subprocess.run(cmd, check=True, cwd=folder) # noqa: S603 try: run(install_cmd) diff --git a/scripts/fastapi-vue/devutil.py b/scripts/fastapi-vue/devutil.py index bcfe0b8..eeb2587 100644 --- a/scripts/fastapi-vue/devutil.py +++ b/scripts/fastapi-vue/devutil.py @@ -1,3 +1,4 @@ +# ruff: noqa: INP001 """Utilities meant for devserver script, used only in source repository with dev deps.""" from __future__ import annotations @@ -11,9 +12,8 @@ from subprocess import CalledProcessError from typing import TYPE_CHECKING, Any from urllib.parse import urlsplit -from fastapi_vue.hostutil import parse_endpoint - from buildutil import find_dev_tool, find_install_tool, logger +from fastapi_vue.hostutil import parse_endpoint if TYPE_CHECKING: from collections.abc import Awaitable @@ -67,7 +67,7 @@ class ProcessGroup(asyncio.TaskGroup): async def wait(self, *waitables: Process | Awaitable) -> tuple[Any, ...]: """Wait concurrently and return results in argument order.""" - async def task(w: Process | Awaitable) -> Any: + async def task(w: Process | Awaitable) -> Any: # noqa: ANN401 if not isinstance(w, Process): return await w if retcode := await w.wait(): @@ -84,7 +84,7 @@ class ProcessGroup(asyncio.TaskGroup): return tuple(task.result() for task in tasks) -async def http_get_server(url: str, timeout: float) -> str | None: +async def http_get_server(url: str, timeout: float) -> str | None: # noqa: ASYNC109 """GET url with plain asyncio streams, return the response Server header. Returns an empty string when the server responds without a Server header,