10This module is a Sphinx Extension for the Belle~II Software:
12* add a domain "b2" for modules and variables.
13 Modules can be documented using the ``.. b2:module::` directive and variables
14 using the ``.. b2:variable::` directive. They can be cross referenced
with
15 :b2:mod:
and :b2:var: respectively
16* add an index
for basf2 modules
17* add a directive to automatically document basf2 modules similar to autodoc
21 :modules: EventInfoSetter, EventInfoPrinter
23 :regex-filter: Event.*
25* add directive to automatically document basf2 variables
38from docutils import nodes
39from sphinx.util.nodes import nested_parse_with_titles
40from docutils.parsers.rst import Directive, directives
41from docutils.statemachine import StringList
42from basf2domain import Basf2Domain
43from basf2 import list_available_modules, register_module
44from sphinx.domains.std import StandardDomain
47def parse_with_titles(state, content):
48 """Shortcut function to parse a reStructuredText fragment into docutils nodes"""
49 node = nodes.container()
51 node.document = state.document
52 if isinstance(content, str):
53 content = content.splitlines()
54 if not isinstance(content, StringList):
55 content = StringList(content)
56 nested_parse_with_titles(state, content, node)
61 """Directive to Render Docstring as Docutils nodes.
62 This is useful
as standard reStructuredText does
not parse Google
63 Docstrings but we support it
in docstrings
for python functions, modules
64 and variables. So to show example docstrings
in the documentation we don
't
65 want to write the example in Google Docstring
and keep that synchronous
66 with a reStructuredText version
71 "lines": directives.unchanged
76 """Just pass on the content to the autodoc-process-docstring event and
77 then parse the resulting reStructuredText."""
78 env = self.state.document.settings.env
79 content = list(self.content)
81 start_index, end_index = (int(e)
for e
in self.options.get(
"lines",
None).split(
","))
82 content = content[start_index:end_index]
87 content = textwrap.dedent(
"\n".join(content)).splitlines()
89 env.app.emit(
'autodoc-process-docstring',
"docstring",
None,
None,
None, content)
90 return parse_with_titles(self.state, content)
94class ModuleListDirective(Directive):
97 "library": directives.unchanged,
98 "modules": directives.unchanged,
99 "package": directives.unchanged,
100 "no-parameters": directives.flag,
101 "noindex": directives.flag,
102 "regex-filter": directives.unchanged,
103 "io-plots": directives.flag,
107 def show_module(self, module, library):
108 description = module.description().splitlines()
112 env = self.state.document.settings.env
113 env.app.emit(
'autodoc-process-docstring',
"b2:module", module.name(), module,
None, description)
114 description += [
"",
"",
115 f
":Package: {module.package()}",
116 f
":Library: {os.path.basename(library)}",
119 if "no-parameters" not in self.options:
122 for p
in module.available_params():
123 dest = required_params
if p.forceInSteering
else optional_params
124 default =
"" if p.forceInSteering
else f
", default={p.default!r}"
125 param_desc = p.description.splitlines()
128 env.app.emit(
'autodoc-process-docstring',
'b2:module:param', module.name() +
'.' + p.name, p,
None, param_desc)
129 param_desc = textwrap.indent(
"\n".join(param_desc), 8 *
" ").splitlines()
130 dest += [f
" * **{p.name}** *({p.type}{default})*"]
134 description += [
":Required Parameters:",
" "] + required_params
136 description += [
":Parameters:",
" "] + optional_params
138 if "io-plots" in self.options:
139 image = f
"build/ioplots/{module.name()}.png"
140 if os.path.exists(image):
141 description += [
":IO diagram:",
" ", f
" .. image:: /{image}"]
143 content = [f
".. b2:module:: {module.name()}"] + self.noindex + [
" "]
144 content += [
" " + e
for e
in description]
145 return parse_with_titles(self.state, content)
148 all_modules = list_available_modules().items()
151 if "modules" in self.options:
152 modules = [e.strip()
for e
in self.options[
"modules"].split(
",")]
153 all_modules = [e
for e
in all_modules
if e[0]
in modules]
156 if "regex-filter" in self.options:
157 re_filter = re.compile(self.options[
"regex-filter"])
158 all_modules = [e
for e
in all_modules
if re_filter.match(e[0])
is not None]
162 if "library" in self.options:
163 lib = self.options[
"library"].strip()
164 all_modules = [e
for e
in all_modules
if os.path.basenam(e[1]) == lib]
167 self.noindex = [
" :noindex:"]
if "noindex" in self.options
else []
173 for name, library
in sorted(all_modules):
174 module = register_module(name)
176 if "package" in self.options
and module.package() != self.options[
"package"]:
180 all_nodes += self.show_module(module, library)
186class VariableListDirective(Directive):
189 "group": directives.unchanged,
190 "variables": directives.unchanged,
191 "regex-filter": directives.unchanged,
192 "description-regex-filter": directives.unchanged,
193 "noindex": directives.flag,
198 from ROOT
import Belle2
200 self.noindex = [
" :noindex:"]
if "noindex" in self.options
else []
204 desc_regex_filter =
None
205 if "variables" in self.options:
206 explicit_list = [e.strip()
for e
in self.options[
"variables"].split(
",")]
207 if "regex-filter" in self.options:
208 regex_filter = re.compile(self.options[
"regex-filter"])
209 if "description-regex-filter" in self.options:
210 desc_regex_filter = re.compile(self.options[
"description-regex-filter"])
212 for var
in manager.getVariables():
213 if "group" in self.options
and self.options[
"group"] != var.group:
215 if explicit_list
and var.name
not in explicit_list:
217 if regex_filter
and not regex_filter.match(var.name):
219 if desc_regex_filter
and not desc_regex_filter.match(var.description):
221 all_variables.append(var)
224 env = self.state.document.settings.env
225 for var
in sorted(all_variables, key=
lambda x: x.name):
230 if ":noindex:" in var.description:
231 index = [
" :noindex:"]
232 var.description = var.description.replace(
":noindex:",
"")
234 docstring = var.description.splitlines()
238 env.app.emit(
'autodoc-process-docstring',
"b2:variable", var.name, var,
None, docstring)
240 description = [f
".. b2:variable:: {var.name}"] + index + [
""]
241 description += [
" " + e
for e
in docstring]
242 if "group" not in self.options:
243 description += [
"", f
" :Group: {var.group}"]
245 all_nodes += parse_with_titles(self.state, description)
250def html_page_context(app, pagename, templatename, context, doctree):
251 """Provide Link to GitLab Repository, see https://mg.pov.lt/blog/sphinx-edit-on-github.html
253 this goes in conjunction
with
254 site_scons/sphinx/_sphinxtemplates/sourcelink.html
and adds a link to our
255 git repository instead to the local source link
258 if templatename !=
'page.html' or not doctree:
261 path = os.path.relpath(doctree.get(
'source'), app.builder.srcdir)
262 repository = app.config.basf2_repository
266 commit = app.config.basf2_commitid
267 context[
"source_url"] = f
"{repository}/-/blob/main/{path}"
269 context[
"source_url"] = f
"{repository}/-/blob/{commit}/{path}"
272def gitlab_issue_role(role, rawtext, text, lineno, inliner, options=None, content=None):
277 issue_url = inliner.document.settings.env.app.config.basf2_issues
279 return [nodes.literal(rawtext, text=text, language=
None)], []
281 url = f
"{issue_url}/{text}"
282 return [nodes.reference(rawtext, text=text, refuri=url)], []
287 basf2.logging.log_level = basf2.LogLevel.WARNING
289 app.add_config_value(
"basf2_repository",
"",
True)
290 app.add_config_value(
"basf2_commitid",
"",
True)
291 app.add_config_value(
"basf2_issues",
"",
True)
292 app.add_domain(Basf2Domain)
293 app.add_directive(
"b2-modules", ModuleListDirective)
294 app.add_directive(
"b2-variables", VariableListDirective)
295 app.add_directive(
"docstring", RenderDocstring)
296 app.add_role(
"issue", gitlab_issue_role)
297 app.connect(
'html-page-context', html_page_context)
300 StandardDomain.initial_data[
"labels"][
"b2-modindex"] = (
"b2-modindex",
"",
"basf2 Module Index")
301 StandardDomain.initial_data[
"labels"][
"b2-varindex"] = (
"b2-varindex",
"",
"basf2 Variable Index")
302 StandardDomain.initial_data[
"anonlabels"][
"b2-modindex"] = (
"b2-modindex",
"")
303 StandardDomain.initial_data[
"anonlabels"][
"b2-varindex"] = (
"b2-varindex",
"")
304 return {
'version': 0.2}
static Manager & Instance()
get singleton instance.