Skip to content

Commit 1a048cd

Browse files
authored
Merge pull request #257 from machow/feat-member-signature
feat: add Auto signature_path and member_options fields
2 parents dcdeb41 + 334074f commit 1a048cd

File tree

7 files changed

+108
-30
lines changed

7 files changed

+108
-30
lines changed

quartodoc/builder/blueprint.py

Lines changed: 16 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,13 @@ 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,
331+
obj,
332+
children,
333+
flat=is_flat,
334+
signature_name=el.signature_name,
335+
)
329336

330337
@staticmethod
331338
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_name: 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_name: 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_name: 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_name": signature_name,
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: 30 additions & 14 deletions
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
@@ -86,7 +87,7 @@ def _increment_header(self, n=1):
8687

8788
def _fetch_object_dispname(self, el: "dc.Alias | dc.Object"):
8889
# TODO: copied from Builder, should move into util function
89-
if self.display_name == "name":
90+
if self.display_name in {"name", "short"}:
9091
return el.name
9192
elif self.display_name == "relative":
9293
return ".".join(el.path.split(".")[1:])
@@ -128,6 +129,22 @@ def render_annotation(self, el: "str | expr.Name | expr.Expression | None"):
128129

129130
# signature method --------------------------------------------------------
130131

132+
@dispatch
133+
def signature(self, el: layout.Doc):
134+
orig = self.display_name
135+
136+
# set signature path, generate signature, then set back
137+
# TODO: this is for backwards compatibility with the old approach
138+
# of only defining signature over griffe objects, which projects
139+
# like shiny currently extend
140+
self.display_name = el.signature_name
141+
res = self.signature(el.obj)
142+
self.display_name = orig
143+
144+
return res
145+
146+
147+
131148
@dispatch
132149
def signature(self, el: dc.Alias, source: Optional[dc.Alias] = None):
133150
"""Return a string representation of an object's signature."""
@@ -310,28 +327,30 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]):
310327
[self.render(x) for x in raw_meths if isinstance(x, layout.Doc)]
311328
)
312329

330+
331+
str_sig = self.signature(el)
332+
sig_part = [str_sig] if self.show_signature else []
333+
313334
body = self.render(el.obj)
314-
return "\n\n".join([title, body, *attr_docs, *class_docs, *meth_docs])
315335

316-
@dispatch
317-
def render(self, el: layout.DocFunction):
318-
title = self.render_header(el)
319336

320-
return "\n\n".join([title, self.render(el.obj)])
337+
return "\n\n".join([title, *sig_part, body, *attr_docs, *class_docs, *meth_docs])
321338

322339
@dispatch
323-
def render(self, el: layout.DocAttribute):
340+
def render(self, el: Union[layout.DocFunction, layout.DocAttribute]):
324341
title = self.render_header(el)
325-
return "\n\n".join([title, self.render(el.obj)])
342+
343+
str_sig = self.signature(el)
344+
sig_part = [str_sig] if self.show_signature else []
345+
346+
return "\n\n".join([title, *sig_part, self.render(el.obj)])
326347

327348
# render griffe objects ===================================================
328349

329350
@dispatch
330351
def render(self, el: Union[dc.Object, dc.Alias]):
331352
"""Render high level objects representing functions, classes, etc.."""
332353

333-
str_sig = self.signature(el)
334-
335354
str_body = []
336355
if el.docstring is None:
337356
pass
@@ -347,10 +366,7 @@ def render(self, el: Union[dc.Object, dc.Alias]):
347366
else:
348367
str_body.append(body)
349368

350-
if self.show_signature:
351-
parts = [str_sig, *str_body]
352-
else:
353-
parts = [*str_body]
369+
parts = [*str_body]
354370

355371
return "\n\n".join(parts)
356372

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_name
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_basic.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,7 @@ def test_render_attribute():
4949
# TODO: snapshot tests
5050
a = get_object("quartodoc", "tests.example_attribute.a")
5151

52-
assert (
53-
MdRenderer().render(a)
54-
== "`tests.example_attribute.a`\n\nI am an attribute docstring"
55-
)
52+
assert MdRenderer().render(a) == "I am an attribute docstring"
5653

5754

5855
def test_get_object_dynamic_module_root():

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_name": "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_name == "short"
199+
200+
# this currently does not apply to members of members
201+
assert doc_a_class.members[0].signature_name == "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_name(snapshot, renderer):
151+
package = "quartodoc.tests"
152+
auto = Auto(name="example.a_func", package=package, signature_name="short")
153+
bp = blueprint(auto)
154+
155+
res = renderer.render(bp)
156+
157+
assert res == snapshot

0 commit comments

Comments
 (0)