Skip to content
Permalink
Browse files

refactor: Simplify logging

  • Loading branch information
pawamoy committed Sep 29, 2020
1 parent 791c111 commit 409f93ed26d7d8292a8bc7a6c32cb270b3769409
Showing with 80 additions and 38 deletions.
  1. +11 −14 src/mkdocstrings/extension.py
  2. +13 −15 src/mkdocstrings/handlers/python.py
  3. +48 −0 src/mkdocstrings/logging.py
  4. +8 −9 src/mkdocstrings/plugin.py
@@ -22,7 +22,6 @@
option_x: etc
```
"""
import logging
import re
from typing import Any, Tuple
from xml.etree.ElementTree import XML, Element, ParseError # noqa: S405 (we choose to trust the XML input)
@@ -34,12 +33,11 @@
from markdown.blockprocessors import BlockProcessor
from markdown.extensions import Extension
from markdown.util import AtomicString
from mkdocs.utils import warning_filter

from mkdocstrings.handlers import CollectionError, get_handler
from mkdocstrings.logging import get_logger

log = logging.getLogger(f"mkdocs.plugins.{__name__}")
log.addFilter(warning_filter)
log = get_logger(__name__)

ENTITIES = """
<!DOCTYPE html [
@@ -151,7 +149,7 @@ def run(self, parent: Element, blocks: Element) -> None:

if match:
identifier = match.group(1)
log.debug(f"mkdocstrings.extension: Matched '::: {identifier}'")
log.debug(f"Matched '::: {identifier}'")
xml_element = self.process_block(identifier, str(block))
parent.append(xml_element)

@@ -178,7 +176,7 @@ def process_block(self, identifier: str, yaml_block: str) -> Element:
config = yaml.safe_load(yaml_block) or {}
handler_name = self.get_handler_name(config)

log.debug(f"mkdocstrings.extension: Using handler '{handler_name}'")
log.debug(f"Using handler '{handler_name}'")
handler_config = self.get_handler_config(handler_name)
handler = get_handler(
handler_name,
@@ -189,28 +187,27 @@ def process_block(self, identifier: str, yaml_block: str) -> Element:

selection, rendering = get_item_configs(handler_config, config)

log.debug("mkdocstrings.extension: Collecting data")
log.debug("Collecting data")
try:
data: Any = handler.collector.collect(identifier, selection)
except CollectionError:
log.error(f"mkdocstrings.extension: Could not collect '{identifier}'")
log.error(f"Could not collect '{identifier}'")
raise

log.debug("mkdocstrings.extension: Updating renderer's env")
log.debug("Updating renderer's env")
handler.renderer.update_env(self.md, self._config)

log.debug("mkdocstrings.extension: Rendering templates")
log.debug("Rendering templates")
try:
rendered = handler.renderer.render(data, rendering)
except TemplateNotFound as exc:
theme_name = self._config["theme_name"]
log.error(
f"mkdocstrings.extension: Template '{exc.name}' not found "
f"for '{handler_name}' handler and theme '{theme_name}'.",
f"Template '{exc.name}' not found for '{handler_name}' handler and theme '{theme_name}'.",
)
raise

log.debug("mkdocstrings.extension: Loading HTML back into XML tree")
log.debug("Loading HTML back into XML tree")
try:
xml_contents = XML(ENTITIES + rendered)
except ParseError as error:
@@ -277,7 +274,7 @@ def log_xml_parse_error(error: str, xml_text: str) -> None:
error: The error message (no traceback).
xml_text: The XML text that generated the parsing error.
"""
message = f"mkdocstrings.extension: {error}"
message = error
if "mismatched tag" in error:
line_column = error[error.rfind(":") + 1 :]
line, column = line_column.split(", ")
@@ -5,19 +5,17 @@
"""

import json
import logging
import os
import sys
from subprocess import PIPE, Popen # noqa: S404 (what other option, more secure that PIPE do we have? sockets?)
from typing import Any, List, Optional

from markdown import Markdown
from mkdocs.utils import warning_filter

from mkdocstrings.handlers import BaseCollector, BaseHandler, BaseRenderer, CollectionError
from mkdocstrings.logging import get_logger

log = logging.getLogger(f"mkdocs.plugins.{__name__}")
log.addFilter(warning_filter)
log = get_logger(__name__)


class PythonRenderer(BaseRenderer):
@@ -132,7 +130,7 @@ def __init__(self, setup_commands: Optional[List[str]] = None) -> None:
Arguments:
setup_commands: A list of python commands as strings to be executed in the subprocess before `pytkdocs`.
"""
log.debug("mkdocstrings.handlers.python: Opening 'pytkdocs' subprocess")
log.debug("Opening 'pytkdocs' subprocess")
env = os.environ.copy()
env["PYTHONUNBUFFERED"] = "1"

@@ -197,49 +195,49 @@ def collect(self, identifier: str, config: dict) -> Any:
final_config = dict(self.default_config)
final_config.update(config)

log.debug("mkdocstrings.handlers.python: Preparing input")
log.debug("Preparing input")
json_input = json.dumps({"objects": [{"path": identifier, **final_config}]})

log.debug("mkdocstrings.handlers.python: Writing to process' stdin")
log.debug("Writing to process' stdin")
self.process.stdin.write(json_input + "\n") # type: ignore
self.process.stdin.flush() # type: ignore

log.debug("mkdocstrings.handlers.python: Reading process' stdout")
log.debug("Reading process' stdout")
stdout = self.process.stdout.readline() # type: ignore

log.debug("mkdocstrings.handlers.python: Loading JSON output as Python object")
log.debug("Loading JSON output as Python object")
try:
result = json.loads(stdout)
except json.decoder.JSONDecodeError as exception:
log.error(f"mkdocstrings.handlers.python: Error while loading JSON: {stdout}")
log.error(f"Error while loading JSON: {stdout}")
raise CollectionError(str(exception)) from exception

error = result.get("error")
if error:
message = f"mkdocstrings.handlers.python: Collection failed: {error}"
message = f"Collection failed: {error}"
if "traceback" in result:
message += f"\n{result['traceback']}"
log.error(message)
raise CollectionError(error)

for loading_error in result["loading_errors"]:
log.warning(f"mkdocstrings.handlers.python: {loading_error}")
log.warning(loading_error)

for errors in result["parsing_errors"].values():
for parsing_error in errors:
log.warning(f"mkdocstrings.handlers.python: {parsing_error}")
log.warning(parsing_error)

# We always collect only one object at a time
result = result["objects"][0]

log.debug("mkdocstrings.handlers.python: Rebuilding categories and children lists")
log.debug("Rebuilding categories and children lists")
rebuild_category_lists(result)

return result

def teardown(self) -> None:
"""Terminate the opened subprocess, set it to `None`."""
log.debug("mkdocstrings.handlers.python: Tearing process down")
log.debug("Tearing process down")
self.process.terminate()


@@ -0,0 +1,48 @@
"""Utility functions."""

import logging

from mkdocs.utils import warning_filter


class LoggerAdapter(logging.LoggerAdapter):
"""A logger adapter to prefix messages."""

def __init__(self, prefix, logger):
"""
Initialize the object.
Arguments:
prefix: The string to insert in front of every message.
logger: The logger instance.
"""
super().__init__(logger, {})
self.prefix = prefix

def process(self, msg, kwargs):
"""
Process the message.
Arguments:
msg: The message:
kwargs: Remaining arguments.
Returns:
The processed message.
"""
return f"{self.prefix}: {msg}", kwargs


def get_logger(name):
"""
Return a pre-configured logger.
Arguments:
name: The name to use with `logging.getLogger`.
Returns:
A logger configured to work well in MkDocs.
"""
logger = logging.getLogger(f"mkdocs.plugins.{name}")
logger.addFilter(warning_filter)
return LoggerAdapter(name, logger)
@@ -32,14 +32,13 @@
from mkdocs.plugins import BasePlugin
from mkdocs.structure.pages import Page
from mkdocs.structure.toc import AnchorLink
from mkdocs.utils import warning_filter

from mkdocstrings.extension import MkdocstringsExtension
from mkdocstrings.handlers import teardown
from mkdocstrings.logging import get_logger
from mkdocstrings.references import fix_refs

log = logging.getLogger(f"mkdocs.plugins.{__name__}")
log.addFilter(warning_filter)
log = get_logger(__name__)

SELECTION_OPTS_KEY: str = "selection"
"""The name of the selection parameter in YAML configuration blocks."""
@@ -128,7 +127,7 @@ def on_serve(self, server: Server, builder: Callable = None, **kwargs) -> Server
# See issue https://github.com/mkdocs/mkdocs/issues/1952.
builder = list(server.watcher._tasks.values())[0]["func"] # noqa: WPS437 (protected attribute)
for element in self.config["watch"]:
log.debug(f"mkdocstrings.plugin: Adding directory '{element}' to watcher")
log.debug(f"Adding directory '{element}' to watcher")
server.watch(element, builder)
return server

@@ -150,7 +149,7 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: W0613 (unused
Returns:
The modified config.
"""
log.debug("mkdocstrings.plugin: Adding extension to the list")
log.debug("Adding extension to the list")

theme_name = None
if config["theme"].name is None:
@@ -186,7 +185,7 @@ def on_page_content(self, html: str, page: Page, **kwargs) -> str: # noqa: W061
Returns:
The same HTML. We only use this hook to map anchors to URLs.
"""
log.debug(f"mkdocstrings.plugin: Mapping identifiers to URLs for page {page.file.src_path}")
log.debug(f"Mapping identifiers to URLs for page {page.file.src_path}")
for item in page.toc.items:
self.map_urls(page.url, item)
return html
@@ -227,14 +226,14 @@ def on_post_page(self, output: str, page: Page, **kwargs) -> str: # noqa: W0613
Returns:
Modified HTML.
"""
log.debug(f"mkdocstrings.plugin: Fixing references in page {page.file.src_path}")
log.debug(f"Fixing references in page {page.file.src_path}")

fixed_output, unmapped = fix_refs(output, page.url, self.url_map)

if unmapped and log.isEnabledFor(logging.WARNING):
for ref in unmapped:
log.warning(
f"mkdocstrings.plugin: {page.file.src_path}: Could not find cross-reference target '[{ref}]'",
f"{page.file.src_path}: Could not find cross-reference target '[{ref}]'",
)

return fixed_output
@@ -255,5 +254,5 @@ def on_post_build(self, **kwargs) -> None: # noqa: W0613,R0201 (unused argument
Arguments:
kwargs: Additional arguments passed by MkDocs.
"""
log.debug("mkdocstrings.plugin: Tearing handlers down")
log.debug("Tearing handlers down")
teardown()

0 comments on commit 409f93e

Please sign in to comment.