|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 | 3 | import logging
|
| 4 | +import json |
| 5 | +import yaml |
4 | 6 |
|
5 | 7 | from griffe import dataclasses as dc
|
6 | 8 | from griffe.loader import GriffeLoader
|
|
31 | 33 | _log = logging.getLogger(__name__)
|
32 | 34 |
|
33 | 35 |
|
| 36 | +def _auto_package(mod: dc.Module) -> list[Section]: |
| 37 | + """Create default sections for the given package.""" |
| 38 | + |
| 39 | + import griffe.docstrings.dataclasses as ds |
| 40 | + |
| 41 | + # get module members for content ---- |
| 42 | + contents = [] |
| 43 | + for name, member in mod.members.items(): |
| 44 | + if member.is_module or name.startswith("__all__"): |
| 45 | + continue |
| 46 | + |
| 47 | + contents.append(Auto(name=name)) |
| 48 | + |
| 49 | + # try to fetch a description of the module ---- |
| 50 | + mod_summary = mod.docstring.parsed[0] |
| 51 | + if isinstance(mod_summary, ds.DocstringSectionText): |
| 52 | + desc = mod_summary.value |
| 53 | + else: |
| 54 | + desc = "" |
| 55 | + |
| 56 | + return [Section(title=mod.name, desc=desc, contents=contents)] |
| 57 | + |
| 58 | + |
| 59 | +def _to_simple_dict(el): |
| 60 | + # round-trip to json, so we can take advantage of pydantic |
| 61 | + # dumping Enums, etc.. There may be a simple way to do |
| 62 | + # this in pydantic v2. |
| 63 | + return json.loads(el.json(exclude_unset=True)) |
| 64 | + |
| 65 | + |
34 | 66 | class BlueprintTransformer(PydanticTransformer):
|
35 | 67 | def __init__(self, get_object=None, parser="numpy"):
|
36 | 68 |
|
@@ -80,6 +112,37 @@ def visit(self, el):
|
80 | 112 | finally:
|
81 | 113 | self.crnt_package = old
|
82 | 114 |
|
| 115 | + @dispatch |
| 116 | + def enter(self, el: Layout): |
| 117 | + if not el.sections: |
| 118 | + # TODO: should be shown all the time, not just logged, |
| 119 | + # but also want to be able to disable (similar to pins) |
| 120 | + print("Autogenerating contents (since no contents specified in config)") |
| 121 | + |
| 122 | + package = el.package |
| 123 | + |
| 124 | + mod = self.get_object_fixed(package) |
| 125 | + sections = _auto_package(mod) |
| 126 | + |
| 127 | + if not sections: |
| 128 | + # TODO: informative message. When would this occur? |
| 129 | + raise ValueError() |
| 130 | + |
| 131 | + new_el = el.copy() |
| 132 | + new_el.sections = sections |
| 133 | + |
| 134 | + print( |
| 135 | + "Use the following config configuration to recreate the automatically", |
| 136 | + " generated site:\n\n\n", |
| 137 | + yaml.safe_dump(_to_simple_dict(new_el)), |
| 138 | + "\n", |
| 139 | + sep="", |
| 140 | + ) |
| 141 | + |
| 142 | + return super().enter(new_el) |
| 143 | + |
| 144 | + return super().enter(el) |
| 145 | + |
83 | 146 | @dispatch
|
84 | 147 | def exit(self, el: Section):
|
85 | 148 | """Transform top-level sections, so their contents are all Pages."""
|
@@ -178,8 +241,8 @@ def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
|
178 | 241 | # for modules, remove any Alias objects, since they were imported from
|
179 | 242 | # other places. Sphinx has a flag for this behavior, so may be good
|
180 | 243 | # to do something similar.
|
181 |
| - if obj.is_module: |
182 |
| - options = {k: v for k, v in options.items() if not v.is_alias} |
| 244 | + # if obj.is_module: |
| 245 | + # options = {k: v for k, v in options.items() if not v.is_alias} |
183 | 246 |
|
184 | 247 | return sorted(options)
|
185 | 248 |
|
|
0 commit comments