Skip to content

Commit 859ae1f

Browse files
committed
fix: blueprint correct member path, handles flat children option
1 parent 1930e6e commit 859ae1f

File tree

4 files changed

+105
-38
lines changed

4 files changed

+105
-38
lines changed

quartodoc/builder/blueprint.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ def __init__(self, get_object=None, parser="numpy"):
4444

4545
self.crnt_package = None
4646

47+
@staticmethod
48+
def _append_member_path(path: str, new: str):
49+
if ":" in path:
50+
return f"{path}.{new}"
51+
52+
return f"{path}:{new}"
53+
4754
def get_object_fixed(self, *args, **kwargs):
4855
try:
4956
return self.get_object(*args, **kwargs)
@@ -79,7 +86,7 @@ def exit(self, el: Section):
7986
# otherwise, replace all contents with pages.
8087
new = el.copy()
8188
contents = [
82-
Page(contents=[el], path=el.name) if isinstance(el, Doc) else el
89+
Page(contents=[el], path=el.name) if not isinstance(el, Page) else el
8390
for el in new.contents
8491
]
8592

@@ -112,7 +119,8 @@ def enter(self, el: Auto):
112119
# but the actual objects on the target.
113120
# On the other hand, we've wired get_object up to make sure getting
114121
# the member of an Alias also returns an Alias.
115-
obj_member = self.get_object_fixed(f"{path}.{entry}", dynamic=el.dynamic)
122+
member_path = self._append_member_path(path, entry)
123+
obj_member = self.get_object_fixed(member_path, dynamic=el.dynamic)
116124

117125
# do no document submodules
118126
if obj_member.kind.value == "module":
@@ -126,7 +134,7 @@ def enter(self, el: Auto):
126134
res = MemberPage(path=obj_member.path, contents=[doc])
127135
# Case2: use just the Doc element, so it gets embedded directly
128136
# into the class being documented
129-
elif el.children == ChoicesChildren.embedded:
137+
elif el.children in {ChoicesChildren.embedded, ChoicesChildren.flat}:
130138
res = doc
131139
# Case 3: make each member just a link in a summary table.
132140
# if the page for the member is not created somewhere else, then it
@@ -139,7 +147,8 @@ def enter(self, el: Auto):
139147

140148
children.append(res)
141149

142-
return Doc.from_griffe(el.name, obj, members=children)
150+
is_flat = el.children == ChoicesChildren.flat
151+
return Doc.from_griffe(el.name, obj, members=children, flat=is_flat)
143152

144153
@staticmethod
145154
def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
@@ -182,6 +191,17 @@ def exit(self, el: Page):
182191
return el
183192

184193

194+
def blueprint(el: _Base, package: str = None):
195+
"""Create a blueprint of a layout element, that is ready to render."""
196+
197+
trans = BlueprintTransformer()
198+
199+
if package is not None:
200+
trans.crnt_package = package
201+
202+
return trans.visit(el)
203+
204+
185205
def strip_package_name(el: _Base, package: str):
186206
"""Removes leading package name from layout Pages."""
187207

quartodoc/layout.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ class Config:
180180

181181
@classmethod
182182
def from_griffe(
183-
cls, name, obj: Union[dc.Object, dc.Alias], members=None, anchor: str = None
183+
cls,
184+
name,
185+
obj: Union[dc.Object, dc.Alias],
186+
members=None,
187+
anchor: str = None,
188+
flat: bool = False,
184189
):
185190
if members is None:
186191
members = []
@@ -195,9 +200,9 @@ def from_griffe(
195200
elif kind == "attribute":
196201
return DocAttribute(**kwargs)
197202
elif kind == "class":
198-
return DocClass(members=members, **kwargs)
203+
return DocClass(members=members, flat=flat, **kwargs)
199204
elif kind == "module":
200-
return DocModule(members=members, **kwargs)
205+
return DocModule(members=members, flat=flat, **kwargs)
201206

202207
raise TypeError(f"Cannot handle auto for object kind: {obj.kind}")
203208

@@ -209,6 +214,7 @@ class DocFunction(Doc):
209214
class DocClass(Doc):
210215
kind: Literal["class"] = "class"
211216
members: list[Union[MemberPage, Doc, Link]] = tuple()
217+
flat: bool
212218

213219

214220
class DocAttribute(Doc):

quartodoc/tests/test_builder.py

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from quartodoc import get_object
2+
from quartodoc import layout as lo
3+
from quartodoc.builder.blueprint import BlueprintTransformer
4+
import pytest
5+
6+
TEST_MOD = "quartodoc.tests.example"
7+
8+
9+
@pytest.fixture
10+
def lay():
11+
return lo.Layout(
12+
sections=[
13+
lo.Section(
14+
title="",
15+
desc="",
16+
contents=[f"{TEST_MOD}.a_func", f"{TEST_MOD}.a_attr"],
17+
),
18+
lo.Section(
19+
title="",
20+
desc="",
21+
contents=[
22+
f"{TEST_MOD}.AClass",
23+
f"{TEST_MOD}.AClass.a_attr",
24+
f"{TEST_MOD}.AClass.a_method",
25+
],
26+
),
27+
]
28+
)
29+
30+
31+
@pytest.fixture
32+
def bp():
33+
return BlueprintTransformer()
34+
35+
36+
@pytest.mark.parametrize("path", ["quartodoc.get_object", "quartodoc:get_object"])
37+
def test_blueprint_basic(bp, path):
38+
auto = lo.Auto(name=path)
39+
res = bp.visit(auto)
40+
41+
obj = get_object(path)
42+
dst = lo.DocFunction(
43+
name=path, obj=obj, anchor="quartodoc.get_object", kind="function"
44+
)
45+
46+
# TODO: it's hard to compare pydantic models with griffe objects in them
47+
# we likely need to define how to serialize them to json.
48+
assert str(res) == str(dst)
49+
50+
51+
@pytest.mark.parametrize("dynamic", [False, True])
52+
def test_blueprint_visit_class(bp, dynamic):
53+
path = "quartodoc.tests.example:AClass"
54+
auto = lo.Auto(name=path, members=["a_method"], dynamic=dynamic)
55+
res = bp.visit(auto)
56+
57+
assert isinstance(res, lo.DocClass)
58+
assert len(res.members) == 1
59+
assert res.members[0].name == "a_method"
60+
assert res.members[0].obj.path == path.replace(":", ".") + ".a_method"
61+
62+
63+
@pytest.mark.parametrize("dynamic", [False, True])
64+
def test_blueprint_visit_module(bp, dynamic):
65+
path = "quartodoc.tests.example"
66+
auto = lo.Auto(name=path, members=["a_func"], dynamic=dynamic)
67+
res = bp.visit(auto)
68+
69+
assert isinstance(res, lo.DocModule)
70+
assert len(res.members) == 1
71+
assert res.members[0].name == "a_func"
72+
assert res.members[0].obj.path == path.replace(":", ".") + ".a_func"

0 commit comments

Comments
 (0)