Skip to content

Commit 6ee9064

Browse files
committed
feat: add Auto signature_path and member_options fields
1 parent dc59f28 commit 6ee9064

File tree

6 files changed

+101
-13
lines changed

6 files changed

+101
-13
lines changed

quartodoc/builder/blueprint.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,10 @@ def _to_simple_dict(el: "BaseModel"):
116116
return json.loads(el.json(exclude_unset=True))
117117

118118

119-
def _non_default_entries(el: "BaseModel"):
120-
field_defaults = {mf.name: mf.default for mf in el.__fields__.values()}
121-
set_fields = [
122-
k for k, v in el if field_defaults[k] is not v if not isinstance(v, MISSING)
123-
]
124-
119+
def _non_default_entries(el: Auto):
125120
d = el.dict()
126121

127-
return {k: d[k] for k in set_fields}
122+
return {k: d[k] for k in el._fields_specified}
128123

129124

130125
class BlueprintTransformer(PydanticTransformer):
@@ -280,6 +275,12 @@ def enter(self, el: Auto):
280275

281276
# Three cases for structuring child methods ----
282277

278+
_defaults = {"dynamic": dynamic, "package": path}
279+
if el.member_options is not None:
280+
member_options = {**_defaults, **_non_default_entries(el.member_options)}
281+
else:
282+
member_options = _defaults
283+
283284
children = []
284285
for entry in raw_members:
285286
# Note that we could have iterated over obj.members, but currently
@@ -293,7 +294,7 @@ def enter(self, el: Auto):
293294
# create Doc element for member ----
294295
# TODO: when a member is a Class, it is currently created using
295296
# defaults, and there is no way to override those.
296-
doc = self.visit(Auto(name=relative_path, dynamic=dynamic, package=path))
297+
doc = self.visit(Auto(name=relative_path, **member_options))
297298

298299
# do no document submodules
299300
if (
@@ -325,7 +326,9 @@ def enter(self, el: Auto):
325326
children.append(res)
326327

327328
is_flat = el.children == ChoicesChildren.flat
328-
return Doc.from_griffe(el.name, obj, members=children, flat=is_flat)
329+
return Doc.from_griffe(
330+
el.name, obj, children, flat=is_flat, signature_path=el.signature_path
331+
)
329332

330333
@staticmethod
331334
def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):

quartodoc/layout.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55

66
from enum import Enum
7-
from pydantic import BaseModel, Field, Extra
7+
from pydantic import BaseModel, Field, Extra, PrivateAttr
88

99
from typing_extensions import Annotated
1010
from typing import Literal, Union, Optional
@@ -201,9 +201,13 @@ class ChoicesChildren(Enum):
201201
linked = "linked"
202202

203203

204+
SignatureOptions = Literal["full", "short", "relative"]
205+
206+
204207
class AutoOptions(_Base):
205208
"""Options available for Auto content layout element."""
206209

210+
signature_path: SignatureOptions = "relative"
207211
members: Optional[list[str]] = None
208212
include_private: bool = False
209213
include_imports: bool = False
@@ -221,6 +225,15 @@ class AutoOptions(_Base):
221225
dynamic: Union[None, bool, str] = None
222226
children: ChoicesChildren = ChoicesChildren.embedded
223227
package: Union[str, None, MISSING] = MISSING()
228+
member_options: Optional["AutoOptions"] = None
229+
230+
# for tracking fields users manually specify
231+
# so we can tell them apart from defaults
232+
_fields_specified: list[str] = PrivateAttr(default=())
233+
234+
def __init__(self, **kwargs):
235+
super().__init__(**kwargs)
236+
self._fields_specified = tuple(kwargs)
224237

225238

226239
class Auto(AutoOptions):
@@ -259,6 +272,8 @@ class Auto(AutoOptions):
259272
Style for presenting members. Either separate, embedded, or flat.
260273
package:
261274
If specified, object lookup will be relative to this path.
275+
member_options:
276+
Options to apply to members. These can include any of the options above.
262277
263278
264279
"""
@@ -320,6 +335,7 @@ class Doc(_Docable):
320335
name: str
321336
obj: Union[dc.Object, dc.Alias]
322337
anchor: str
338+
signature_path: SignatureOptions = "relative"
323339

324340
class Config:
325341
arbitrary_types_allowed = True
@@ -333,14 +349,20 @@ def from_griffe(
333349
members=None,
334350
anchor: str = None,
335351
flat: bool = False,
352+
signature_path: str = "relative",
336353
):
337354
if members is None:
338355
members = []
339356

340357
kind = obj.kind.value
341358
anchor = obj.path if anchor is None else anchor
342359

343-
kwargs = {"name": name, "obj": obj, "anchor": anchor}
360+
kwargs = {
361+
"name": name,
362+
"obj": obj,
363+
"anchor": anchor,
364+
"signature_path": signature_path,
365+
}
344366

345367
if kind == "function":
346368
return DocFunction(**kwargs)
@@ -431,6 +453,7 @@ class Config:
431453
Layout.update_forward_refs()
432454
Section.update_forward_refs()
433455
Page.update_forward_refs()
456+
AutoOptions.update_forward_refs()
434457
Auto.update_forward_refs()
435458
MemberPage.update_forward_refs()
436459
Interlaced.update_forward_refs()

quartodoc/renderers/md_renderer.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import quartodoc.ast as qast
44

55
from contextlib import contextmanager
6+
from functools import wraps
67
from griffe.docstrings import dataclasses as ds
78
from griffe import dataclasses as dc
89
from tabulate import tabulate
@@ -27,6 +28,29 @@ def _has_attr_section(el: dc.Docstring | None):
2728
return any([isinstance(x, ds.DocstringSectionAttributes) for x in el.parsed])
2829

2930

31+
def with_doc_options(f):
32+
"""Decorator to add docstring options to a renderer.
33+
34+
Currently, the renderer renders signatures directly from griffe objects,
35+
so this decorator is used, to temporarily set some things from layout.Doc
36+
as renderer settings.
37+
38+
(Not ideal but works for now.)
39+
"""
40+
41+
@wraps(f)
42+
def wrapper(self, el, *args, **kwargs):
43+
orig = self.display_name
44+
self.display_name = el.signature_path
45+
res = f(self, el, *args, **kwargs)
46+
47+
self.display_name = orig
48+
49+
return res
50+
51+
return wrapper
52+
53+
3054
class MdRenderer(Renderer):
3155
"""Render docstrings to markdown.
3256
@@ -86,7 +110,7 @@ def _increment_header(self, n=1):
86110

87111
def _fetch_object_dispname(self, el: "dc.Alias | dc.Object"):
88112
# TODO: copied from Builder, should move into util function
89-
if self.display_name == "name":
113+
if self.display_name in {"name", "short"}:
90114
return el.name
91115
elif self.display_name == "relative":
92116
return ".".join(el.path.split(".")[1:])
@@ -246,6 +270,7 @@ def render(self, el: layout.Doc):
246270
raise NotImplementedError(f"Unsupported Doc type: {type(el)}")
247271

248272
@dispatch
273+
@with_doc_options
249274
def render(self, el: Union[layout.DocClass, layout.DocModule]):
250275
title = self.render_header(el)
251276

@@ -314,12 +339,14 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]):
314339
return "\n\n".join([title, body, *attr_docs, *class_docs, *meth_docs])
315340

316341
@dispatch
342+
@with_doc_options
317343
def render(self, el: layout.DocFunction):
318344
title = self.render_header(el)
319345

320346
return "\n\n".join([title, self.render(el.obj)])
321347

322348
@dispatch
349+
@with_doc_options
323350
def render(self, el: layout.DocAttribute):
324351
title = self.render_header(el)
325352
return "\n\n".join([title, self.render(el.obj)])

quartodoc/tests/__snapshots__/test_renderers.ambr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,15 @@
232232
A function
233233
'''
234234
# ---
235+
# name: test_render_doc_signature_path
236+
'''
237+
# example.a_func { #quartodoc.tests.example.a_func }
238+
239+
`a_func()`
240+
241+
A function
242+
'''
243+
# ---
235244
# name: test_render_docstring_numpy_linebreaks
236245
'''
237246
# f_numpy_with_linebreaks { #quartodoc.tests.example_docstring_styles.f_numpy_with_linebreaks }

quartodoc/tests/test_builder_blueprint.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def test_blueprint_default_dynamic(bp):
8787
assert NOTE in res.obj.docstring.value
8888

8989

90-
def test_blueprint_auto_package(bp):
90+
def test_blueprint_auto_anchor(bp):
9191
auto = lo.Auto(name="a_func", package="quartodoc.tests.example")
9292
res = bp.visit(auto)
9393

@@ -183,3 +183,19 @@ def test_blueprint_fetch_members_include_inherited():
183183

184184
member_names = set([entry.name for entry in bp.members])
185185
assert "some_method" in member_names
186+
187+
188+
def test_blueprint_member_options():
189+
auto = lo.Auto(
190+
name="quartodoc.tests.example",
191+
member_options={"signature_path": "short"},
192+
members=["AClass"],
193+
)
194+
bp = blueprint(auto)
195+
doc_a_class = bp.members[0]
196+
197+
# member has option set
198+
assert doc_a_class.signature_path == "short"
199+
200+
# this currently does not apply to members of members
201+
assert doc_a_class.members[0].signature_path == "relative"

quartodoc/tests/test_renderers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,13 @@ def test_render_docstring_numpy_linebreaks(snapshot, renderer):
145145
res = renderer.render(bp)
146146

147147
assert res == snapshot
148+
149+
150+
def test_render_doc_signature_path(snapshot, renderer):
151+
package = "quartodoc.tests"
152+
auto = Auto(name="example.a_func", package=package, signature_path="short")
153+
bp = blueprint(auto)
154+
155+
res = renderer.render(bp)
156+
157+
assert res == snapshot

0 commit comments

Comments
 (0)