Coverage for src/mkdocstrings/loggers.py: 78.33%
50 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-14 19:41 +0100
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-14 19:41 +0100
1"""Logging functions."""
3from __future__ import annotations
5import logging
6from contextlib import suppress
7from pathlib import Path
8from typing import TYPE_CHECKING, Any, Callable, MutableMapping, Sequence
10try:
11 from jinja2 import pass_context
12except ImportError: # TODO: remove once Jinja2 < 3.1 is dropped
13 from jinja2 import contextfunction as pass_context # type: ignore[attr-defined,no-redef]
15try:
16 import mkdocstrings_handlers
17except ImportError:
18 TEMPLATES_DIRS: Sequence[Path] = ()
19else:
20 TEMPLATES_DIRS = tuple(mkdocstrings_handlers.__path__) # type: ignore[arg-type]
23if TYPE_CHECKING:
24 from jinja2.runtime import Context
27class LoggerAdapter(logging.LoggerAdapter):
28 """A logger adapter to prefix messages."""
30 def __init__(self, prefix: str, logger: logging.Logger):
31 """Initialize the object.
33 Arguments:
34 prefix: The string to insert in front of every message.
35 logger: The logger instance.
36 """
37 super().__init__(logger, {})
38 self.prefix = prefix
40 def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]:
41 """Process the message.
43 Arguments:
44 msg: The message:
45 kwargs: Remaining arguments.
47 Returns:
48 The processed message.
49 """
50 return f"{self.prefix}: {msg}", kwargs
53class TemplateLogger:
54 """A wrapper class to allow logging in templates.
56 Attributes:
57 debug: Function to log a DEBUG message.
58 info: Function to log an INFO message.
59 warning: Function to log a WARNING message.
60 error: Function to log an ERROR message.
61 critical: Function to log a CRITICAL message.
62 """
64 def __init__(self, logger: LoggerAdapter):
65 """Initialize the object.
67 Arguments:
68 logger: A logger adapter.
69 """
70 self.debug = get_template_logger_function(logger.debug)
71 self.info = get_template_logger_function(logger.info)
72 self.warning = get_template_logger_function(logger.warning)
73 self.error = get_template_logger_function(logger.error)
74 self.critical = get_template_logger_function(logger.critical)
77def get_template_logger_function(logger_func: Callable) -> Callable:
78 """Create a wrapper function that automatically receives the Jinja template context.
80 Arguments:
81 logger_func: The logger function to use within the wrapper.
83 Returns:
84 A function.
85 """
87 @pass_context
88 def wrapper(context: Context, msg: str | None = None) -> str:
89 """Log a message.
91 Arguments:
92 context: The template context, automatically provided by Jinja.
93 msg: The message to log.
95 Returns:
96 An empty string.
97 """
98 template_path = get_template_path(context)
99 logger_func(f"{template_path}: {msg or 'Rendering'}")
100 return ""
102 return wrapper
105def get_template_path(context: Context) -> str:
106 """Return the path to the template currently using the given context.
108 Arguments:
109 context: The template context.
111 Returns:
112 The relative path to the template.
113 """
114 context_name: str = str(context.name)
115 filename = context.environment.get_template(context_name).filename
116 if filename: 116 ↛ 123line 116 didn't jump to line 123, because the condition on line 116 was never false
117 for template_dir in TEMPLATES_DIRS: 117 ↛ 120line 117 didn't jump to line 120, because the loop on line 117 didn't complete
118 with suppress(ValueError):
119 return str(Path(filename).relative_to(template_dir))
120 with suppress(ValueError):
121 return str(Path(filename).relative_to(Path.cwd()))
122 return filename
123 return context_name
126def get_logger(name: str) -> LoggerAdapter:
127 """Return a pre-configured logger.
129 Arguments:
130 name: The name to use with `logging.getLogger`.
132 Returns:
133 A logger configured to work well in MkDocs.
134 """
135 logger = logging.getLogger(f"mkdocs.plugins.{name}")
136 return LoggerAdapter(name.split(".", 1)[0], logger)
139def get_template_logger() -> TemplateLogger:
140 """Return a logger usable in templates.
142 Returns:
143 A template logger.
144 """
145 return TemplateLogger(get_logger("mkdocstrings.templates"))