Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: draft griffe extension loading #376

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ ci = "https://github.com/machow/quartodoc/actions"

[project.optional-dependencies]
dev = [
"griffe-pydantic",
"pytest<8.0.0",
"pytest-cov",
"jupyterlab",
Expand Down
4 changes: 4 additions & 0 deletions quartodoc/_griffe_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from griffe import Parser, parse, parse_numpy
from griffe import AliasResolutionError

from griffe import load_extensions
except ImportError:
from griffe.loader import GriffeLoader
from griffe.collections import ModulesCollection, LinesCollection
Expand All @@ -20,3 +22,5 @@

from griffe.docstrings.parsers import Parser, parse
from griffe.exceptions import AliasResolutionError

from griffe import load_extensions
33 changes: 31 additions & 2 deletions quartodoc/autosummary.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from ._griffe_compat import GriffeLoader, ModulesCollection, LinesCollection
from ._griffe_compat import dataclasses as dc
from ._griffe_compat import Parser, parse
from ._griffe_compat import load_extensions

from fnmatch import fnmatchcase
from functools import partial
from plum import dispatch # noqa
from pathlib import Path
from types import ModuleType
Expand All @@ -24,7 +26,7 @@
from .pandoc.components import Attr


from typing import Any
from typing import Any, Callable


_log = logging.getLogger(__name__)
Expand Down Expand Up @@ -468,6 +470,9 @@ class Builder:
items: list[layout.Item]
"""Documented items by this builder"""

_get_object: "Callable[[str], dc.Object | dc.Alias]"
"""Internal function called to load each item."""

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)

Expand All @@ -494,6 +499,7 @@ def __init__(
dynamic: bool | None = None,
parser="numpy",
render_interlinks: bool = False,
griffe_extensions: "list[str | dict[str, dict[str, Any]]] | None" = None,
_fast_inventory=False,
):
self.layout = self.load_layout(
Expand All @@ -507,6 +513,9 @@ def __init__(
self.sidebar = sidebar
self.css = css
self.parser = parser
self.griffe_extensions = (
load_extensions(*griffe_extensions) if griffe_extensions else None
)

self.renderer = Renderer.from_config(renderer)
if render_interlinks:
Expand All @@ -517,12 +526,26 @@ def __init__(
if out_index is not None:
self.out_index = out_index

self._get_object = partial(
get_object,
loader=self._create_griffe_loader(self.griffe_extensions),
)
self.rewrite_all_pages = rewrite_all_pages
self.source_dir = str(Path(source_dir).absolute()) if source_dir else None
self.dynamic = dynamic

self._fast_inventory = _fast_inventory

# TODO: annotation
def _create_griffe_loader(self, extensions):
return GriffeLoader(
docstring_parser=Parser(self.parser),
docstring_options=get_parser_defaults(self.parser),
modules_collection=ModulesCollection(),
lines_collection=LinesCollection(),
extensions=self.griffe_extensions,
)

def load_layout(self, sections: dict, package: str, options=None):
# TODO: currently returning the list of sections, to make work with
# previous code. We should make Layout a first-class citizen of the
Expand Down Expand Up @@ -556,7 +579,13 @@ def build(self, filter: str = "*"):
# shaping and collection ----

_log.info("Generating blueprint.")
blueprint = blueprint(self.layout, dynamic=self.dynamic, parser=self.parser)
# TODO: set loader
blueprint = blueprint(
self.layout,
dynamic=self.dynamic,
parser=self.parser,
get_object=self._get_object,
)

_log.info("Collecting pages and inventory items.")
pages, self.items = collect(blueprint, base_dir=self.dir)
Expand Down
12 changes: 9 additions & 3 deletions quartodoc/builder/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

from .utils import PydanticTransformer, ctx_node, WorkaroundKeyError

from typing import overload, TYPE_CHECKING
from typing import Callable, overload, TYPE_CHECKING


_log = logging.getLogger(__name__)
Expand Down Expand Up @@ -437,7 +437,11 @@ def blueprint(el: Auto, package: str) -> Doc:


def blueprint(
el: _Base, package: str = None, dynamic: None | bool = None, parser="numpy"
el: _Base,
package: str = None,
dynamic: None | bool = None,
parser="numpy",
get_object: "None | Callable" = None,
) -> _Base:
"""Convert a configuration element to something that is ready to render.

Expand All @@ -449,6 +453,8 @@ def blueprint(
A base package name. If specified, this is prepended to the names of any objects.
dynamic:
Whether to dynamically load objects. Defaults to using static analysis.
get_object:
The function to call to load the item. If specified, parser is ignored.

Examples
--------
Expand All @@ -463,7 +469,7 @@ def blueprint(

"""

trans = BlueprintTransformer(parser=parser)
trans = BlueprintTransformer(get_object=get_object, parser=parser)

if package is not None:
trans.crnt_package = package
Expand Down
6 changes: 6 additions & 0 deletions quartodoc/tests/example_pydantic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel


class AModel(BaseModel):
a: int
"""The a attribute."""
19 changes: 19 additions & 0 deletions quartodoc/tests/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,22 @@ def test_builder_generate_sidebar(tmp_path, snapshot):
d_sidebar = builder._generate_sidebar(bp)

assert yaml.dump(d_sidebar) == snapshot


def test_builder_griffe_extensions():
cfg = yaml.safe_load(
"""
quartodoc:
package: quartodoc.tests.example_pydantic
griffe_extensions:
- griffe_pydantic:
schema: true
"""
)
builder = Builder.from_quarto_config(cfg)
obj = builder._get_object("quartodoc.tests.example_pydantic.AModel")

# TODO: what to check here?
assert False

builder.build()
Loading