Skip to content

Commit

Permalink
feat: Implement handler and add templates
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Nov 22, 2021
1 parent c94142e commit dbb580a
Show file tree
Hide file tree
Showing 57 changed files with 1,623 additions and 8 deletions.
2 changes: 2 additions & 0 deletions config/flake8.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ ignore =
D105
# multi-line docstring summary should start at the first line
D212
# does not support Parameters sections
D417
# whitespace before ':' (incompatible with Black)
E203
# redundant with E0602 (undefined variable)
Expand Down
3 changes: 3 additions & 0 deletions config/mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ exclude = tests/fixtures/

[mypy-toml.*]
ignore_missing_imports = true

[mypy-markdown.*]
ignore_missing_imports = true
19 changes: 12 additions & 7 deletions docs/gen_ref_nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,27 @@
nav = mkdocs_gen_files.Nav()

for path in sorted(Path("src").glob("**/*.py")):
# if str(path) in exclude:
# continue
module_path = path.relative_to("src").with_suffix("")
doc_path = path.relative_to("src", "mkdocstrings").with_suffix(".md")
doc_path = path.relative_to("src").with_suffix(".md")
full_doc_path = Path("reference", doc_path)

parts = list(module_path.parts)
parts[-1] = f"{parts[-1]}.py"
nav[parts] = doc_path
if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")
elif parts[-1] == "__main__":
continue
nav_parts = list(parts)
nav[nav_parts] = doc_path

with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(module_path.parts)
ident = ".".join(parts)
print("::: " + ident, file=fd)

mkdocs_gen_files.set_edit_path(full_doc_path, path)

# add pages manually:
# nav["package", "module"] = "path/to/file.md"

with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())
1 change: 0 additions & 1 deletion docs/reference/cli.md

This file was deleted.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ classifiers = [
]
dependencies = [
"griffe~=0.3",
"-e ../griffe",
]

[project.urls]
Expand Down
69 changes: 69 additions & 0 deletions src/mkdocstrings/handlers/python/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""This module implements a handler for the Python language."""

import posixpath
from typing import Any, BinaryIO, Iterator, Optional, Tuple

from mkdocstrings.handlers.base import BaseHandler
from mkdocstrings.handlers.python.collector import PythonCollector
from mkdocstrings.handlers.python.renderer import PythonRenderer
from mkdocstrings.inventory import Inventory
from mkdocstrings.loggers import get_logger

log = get_logger(__name__)


class PythonHandler(BaseHandler):
"""The Python handler class.
Attributes:
domain: The cross-documentation domain/language for this handler.
enable_inventory: Whether this handler is interested in enabling the creation
of the `objects.inv` Sphinx inventory file.
"""

domain: str = "py" # to match Sphinx's default domain
enable_inventory: bool = True

@classmethod
def load_inventory(
cls, in_file: BinaryIO, url: str, base_url: Optional[str] = None, **kwargs
) -> Iterator[Tuple[str, str]]:
"""Yield items and their URLs from an inventory file streamed from `in_file`.
This implements mkdocstrings' `load_inventory` "protocol" (see plugin.py).
Arguments:
in_file: The binary file-like object to read the inventory from.
url: The URL that this file is being streamed from (used to guess `base_url`).
base_url: The URL that this inventory's sub-paths are relative to.
**kwargs: Ignore additional arguments passed from the config.
Yields:
Tuples of (item identifier, item URL).
"""
if base_url is None:
base_url = posixpath.dirname(url)

for item in Inventory.parse_sphinx(in_file, domain_filter=("py",)).values(): # noqa: WPS526
yield item.name, posixpath.join(base_url, item.uri)


def get_handler(
theme: str, # noqa: W0613 (unused argument config)
custom_templates: Optional[str] = None,
**config: Any,
) -> PythonHandler:
"""Simply return an instance of `PythonHandler`.
Arguments:
theme: The theme to use when rendering contents.
custom_templates: Directory containing custom templates.
config: Configuration passed to the handler.
Returns:
An instance of `PythonHandler`.
"""
return PythonHandler(
collector=PythonCollector(),
renderer=PythonRenderer("python", theme, custom_templates),
)
81 changes: 81 additions & 0 deletions src/mkdocstrings/handlers/python/collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""This module implements a collector for the Python language.
It collects data with [Griffe](https://github.com/pawamoy/griffe).
"""

from collections import ChainMap

from griffe import logger as griffe_logger

from mkdocstrings.handlers.base import BaseCollector, CollectionError, CollectorItem
from mkdocstrings.loggers import get_logger

griffe_logger.get_logger = get_logger # patch logger to blend in MkDocs logs
from griffe.collections import LinesCollection, ModulesCollection # noqa: E402
from griffe.docstrings.parsers import Parser # noqa: E402
from griffe.extensions import load_extensions # noqa: E402
from griffe.loader import GriffeLoader # noqa: E402

logger = get_logger(__name__)


class PythonCollector(BaseCollector):
"""The class responsible for loading Jinja templates and rendering them.
It defines some configuration options, implements the `render` method,
and overrides the `update_env` method of the [`BaseRenderer` class][mkdocstrings.handlers.base.BaseRenderer].
"""

default_config: dict = {"docstring_style": "google", "docstring_options": {}}
"""The default selection options.
Option | Type | Description | Default
------ | ---- | ----------- | -------
**`docstring_style`** | `"google" | "numpy" | "rst" | None` | The docstring style to use. | `"google"`
**`docstring_options`** | dict[str, Any] | The options for the docstring parser. | `{}`
"""

def __init__(self) -> None:
"""Initialize the object."""
self._modules_collection: ModulesCollection = ModulesCollection()
self._lines_collection: LinesCollection = LinesCollection()

def collect(self, identifier: str, config: dict) -> CollectorItem: # noqa: WPS231
"""Collect the documentation tree given an identifier and selection options.
Arguments:
identifier: The dotted-path of a Python object available in the Python path.
config: Selection options, used to alter the data collection done by `pytkdocs`.
Raises:
CollectionError: When there was a problem collecting the object documentation.
Returns:
The collected object-tree.
"""
final_config = ChainMap(config, self.default_config)

module_name = identifier.split(".", 1)[0]
if module_name not in self._modules_collection:
loader = GriffeLoader(
extensions=load_extensions(final_config.get("extensions", [])),
docstring_parser=Parser(final_config["docstring_style"]),
docstring_options=final_config["docstring_options"],
modules_collection=self._modules_collection,
lines_collection=self._lines_collection,
)
try:
module = loader.load_module(module_name)
except ModuleNotFoundError as error:
raise CollectionError from error

for _ in range(5):
if loader.follow_aliases(module):
break
else:
logger.warning("some aliases could not be resolved")

try:
return self._modules_collection[identifier]
except KeyError as error: # noqa: WPS440
raise CollectionError from error
Loading

0 comments on commit dbb580a

Please sign in to comment.