Skip to content

Commit

Permalink
Merge pull request #230 from napse-invest/feature/mkdocstring
Browse files Browse the repository at this point in the history
Feature/mkdocstring
  • Loading branch information
Xenepix authored Dec 2, 2023
2 parents 596d2f4 + 3a99a7a commit 479179d
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 9 deletions.
1 change: 1 addition & 0 deletions .github/workflows/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- dev

permissions:
contents: write
Expand Down
85 changes: 85 additions & 0 deletions docs/plugins/griffe_doclinks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import ast
import re
from functools import partial
from pathlib import Path
from typing import Tuple

from griffe.dataclasses import Object as GriffeObject
from griffe.extensions import VisitorExtension
from pymdownx.slugs import slugify

DOCS_PATH = Path(__file__).parent.parent
slugifier = slugify(case="lower")


def find_heading(content: str, slug: str, file_path: Path) -> Tuple[str, int]:
for m in re.finditer("^#+ (.+)", content, flags=re.M):
heading = m.group(1)
h_slug = slugifier(heading, "-")
if h_slug == slug:
return heading, m.end()
msg = f"heading with slug {slug!r} not found in {file_path}"
raise ValueError(msg)


def insert_at_top(path: str, api_link: str) -> str:
rel_file = path.rstrip("/") + ".md"
file_path = DOCS_PATH / rel_file
content = file_path.read_text()
second_heading = re.search("^#+ ", content, flags=re.M)
assert second_heading, "unable to find second heading in file" # noqa: S101
first_section = content[: second_heading.start()]

if f"[{api_link}]" not in first_section:
print(f'inserting API link "{api_link}" at the top of {file_path.relative_to(DOCS_PATH)}')
file_path.write_text('??? api "API Documentation"\n' f" [`{api_link}`][{api_link}]<br>\n\n{content}") # noqa: ISC001

heading = file_path.stem.replace("_", " ").title()
return f'!!! abstract "Usage Documentation"\n [{heading}](../{rel_file})\n'


def replace_links(m: re.Match, *, api_link: str) -> str:
path_group = m.group(1)
if "#" not in path_group:
# no heading id, put the content at the top of the page
return insert_at_top(path_group, api_link)

usage_path, slug = path_group.split("#", 1)
rel_file = usage_path.rstrip("/") + ".md"
file_path = DOCS_PATH / rel_file
content = file_path.read_text()
heading, heading_end = find_heading(content, slug, file_path)

next_heading = re.search("^#+ ", content[heading_end:], flags=re.M)
next_section = content[heading_end : heading_end + next_heading.start()] if next_heading else content[heading_end:]

if f"[{api_link}]" not in next_section:
print(f'inserting API link "{api_link}" into {file_path.relative_to(DOCS_PATH)}')
file_path.write_text(
f"{content[:heading_end]}\n\n" '??? api "API Documentation"\n' f" [`{api_link}`][{api_link}]<br>" f"{content[heading_end:]}", # noqa: ISC001
)

return f'!!! abstract "Usage Documentation"\n [{heading}](../{rel_file}#{slug})\n'


def update_docstring(obj: GriffeObject) -> str:
return re.sub(
r"usage[\- ]docs: ?https://docs\.pydantic\.dev/.+?/(\S+)",
partial(replace_links, api_link=obj.path),
obj.docstring.value,
flags=re.I,
)


def update_docstrings_recursively(obj: GriffeObject) -> None:
if obj.docstring:
obj.docstring.value = update_docstring(obj)
for member in obj.members.values():
if not member.is_alias:
update_docstrings_recursively(member)


class Extension(VisitorExtension):
def visit_module(self, node: ast.AST) -> None:
module = self.visitor.current.module
update_docstrings_recursively(module)
83 changes: 83 additions & 0 deletions docs/theme/assets/stylesheets/docstring.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
.tile {
display: flex;
text-align: center;
width: 120px;
height: 120px;
display: inline-block;
margin: 10px;
padding: 5px;
border-radius: .5rem;
}

.tile img {
width: 100px;
}

.md-typeset__table > table {
max-height: 60vh;
}

.md-typeset__table > table thead {
position: sticky;
top: 0;
background-color: var(--md-default-bg-color);
}

.md-typeset__table > table th {
border-bottom: .05rem solid var(--md-typeset-table-color);
}

.md-typeset__table > table tr:first-child td {
border-top: none;
}

/* API documentation link admonition */
:root {
--md-admonition-icon--api: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7H5a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2m0 4H5V9h2m7-2h-4v10h2v-4h2a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2m0 4h-2V9h2m6 0v6h1v2h-4v-2h1V9h-1V7h4v2Z"/></svg>')
}
.md-typeset .admonition.api, .md-typeset details.api {
border-color: #448aff;
}
.md-typeset .api > .admonition-title, .md-typeset .api > summary {
background-color: #448aff1a;
}
.md-typeset .api > .admonition-title::before, .md-typeset .api > summary::before {
background-color: #448aff;
-webkit-mask-image: var(--md-admonition-icon--api);
mask-image: var(--md-admonition-icon--api);
}

/* Revert hue value to that of pre mkdocs-material v9.4.0 */
[data-md-color-scheme="slate"] {
--md-hue: 230;
--md-default-bg-color: hsla(230, 15%, 21%, 1);
}


/* Indentation. */
div.doc-contents:not(.first) {
padding-left: 25px;
border-left: .05rem solid var(--md-typeset-table-color);
}

/* Mark external links as such. */
a.external::after,
a.autorefs-external::after {
/* https://primer.style/octicons/arrow-up-right-24 */
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.25 15.5a.75.75 0 00.75-.75v-9a.75.75 0 00-.75-.75h-9a.75.75 0 000 1.5h7.19L6.22 16.72a.75.75 0 101.06 1.06L17.5 7.56v7.19c0 .414.336.75.75.75z"></path></svg>');
-webkit-mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.25 15.5a.75.75 0 00.75-.75v-9a.75.75 0 00-.75-.75h-9a.75.75 0 000 1.5h7.19L6.22 16.72a.75.75 0 101.06 1.06L17.5 7.56v7.19c0 .414.336.75.75.75z"></path></svg>');
content: ' ';

display: inline-block;
vertical-align: middle;
position: relative;

height: 1em;
width: 1em;
background-color: var(--md-typeset-a-color);
}

a.external:hover::after,
a.autorefs-external:hover::after {
background-color: var(--md-accent-fg-color);
}
33 changes: 24 additions & 9 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ copyright: MIT Licence
docs_dir: "docs/"

extra_css:
- theme/assets/stylesheets/extra.css
- theme/assets/stylesheets/api.css
- assets/stylesheets/extra.css
- assets/stylesheets/api.css
- assets/stylesheets/docstring.css


extra:
social:
Expand Down Expand Up @@ -67,6 +69,12 @@ theme:
- navigation.top
- content.code.copy
- search.suggest
- content.tabs.link
- content.code.annotate

watch:
- django_napse
- docs

plugins:
- search
Expand All @@ -83,16 +91,21 @@ plugins:
python:
paths: [.]
options:
members_order: source
separate_signature: true
filters: ["!^_"]
docstring_options:
ignore_init_summary: true
merge_init_into_class: true
heading_level: 3
extensions:
- docs/plugins/griffe_doclinks.py
show_source: false
allow_inspection: false
show_bases: false
show_root_heading: false
docstring_style: google
docstring_section_style: list
docstring_options:
ignore_init_summary: false
trim_doctest_flags: true
heading_level: 3
# docstring_section_style: list
# Contributors
- neoteroi.contribs:
contributors:
Expand All @@ -102,7 +115,7 @@ plugins:

markdown_extensions:
- toc:
permalink: "#"
permalink: true
- pymdownx.snippets:
- pymdownx.magiclink:
- attr_list:
Expand All @@ -122,8 +135,10 @@ markdown_extensions:
alternate_style: true
- pymdownx.highlight:
anchor_linenums: true
pygments_lang_class: true
# line_spans: __span
# pygments_lang_class: true



nav:
- Home:
Expand Down
1 change: 1 addition & 0 deletions requirements/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ neoteroi-mkdocs==1.0.4 # https://github.com/Neoteroi/mkdocs-plugins
drf-spectacular==0.26.5 # https://github.com/tfranzel/drf-spectacular
mkdocstrings==0.24.0 # https://github.com/mkdocstrings/mkdocstrings
mkdocstrings-python==1.7.5 # https://github.com/mkdocstrings/python
watchfiles==0.21.0 # https://github.com/samuelcolvin/watchfiles

0 comments on commit 479179d

Please sign in to comment.