10 This 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
38 from docutils
import nodes
39 from sphinx.util.nodes
import nested_parse_with_titles
40 from docutils.parsers.rst
import Directive, directives
41 from docutils.statemachine
import StringList
42 from basf2domain
import Basf2Domain
43 from basf2
import list_available_modules, register_module
44 from sphinx.domains.std
import StandardDomain
47 def 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
70 "lines": directives.unchanged
74 """Just pass on the content to the autodoc-process-docstring event and
75 then parse the resulting reStructuredText."""
76 env = self.state.document.settings.env
77 content = list(self.content)
79 start_index, end_index = [int(e)
for e
in self.options.get(
"lines",
None).split(
",")]
80 content = content[start_index:end_index]
85 content = textwrap.dedent(
"\n".join(content)).splitlines()
87 env.app.emit(
'autodoc-process-docstring',
"docstring",
None,
None,
None, content)
88 return parse_with_titles(self.state, content)
94 "library": directives.unchanged,
95 "modules": directives.unchanged,
96 "package": directives.unchanged,
97 "no-parameters": directives.flag,
98 "noindex": directives.flag,
99 "regex-filter": directives.unchanged,
100 "io-plots": directives.flag,
103 def show_module(self, module, library):
104 description = module.description().splitlines()
108 env = self.state.document.settings.env
109 env.app.emit(
'autodoc-process-docstring',
"b2:module", module.name(), module,
None, description)
110 description += [
"",
"",
111 ":Package: %s" % module.package(),
112 ":Library: %s" % os.path.basename(library),
115 if "no-parameters" not in self.options:
118 for p
in module.available_params():
119 dest = required_params
if p.forceInSteering
else optional_params
120 default =
"" if p.forceInSteering
else ", default={default!r}".format(default=p.default)
121 param_desc = p.description.splitlines()
124 env.app.emit(
'autodoc-process-docstring',
'b2:module:param', module.name() +
'.' + p.name, p,
None, param_desc)
125 param_desc = textwrap.indent(
"\n".join(param_desc), 8 *
" ").splitlines()
126 dest += [
" * **{name}** *({type}{default})*".format(name=p.name, type=p.type, default=default)]
130 description += [
":Required Parameters:",
" "] + required_params
132 description += [
":Parameters:",
" "] + optional_params
134 if "io-plots" in self.options:
135 image =
"build/ioplots/%s.png" % module.name()
136 if os.path.exists(image):
137 description += [
":IO diagram:",
" ",
" .. image:: /%s" % image]
139 content = [
".. b2:module:: {module}".format(module=module.name())] + self.
noindexnoindex + [
" "]
140 content += [
" " + e
for e
in description]
141 return parse_with_titles(self.state, content)
144 all_modules = list_available_modules().items()
147 if "modules" in self.options:
148 modules = [e.strip()
for e
in self.options[
"modules"].split(
",")]
149 all_modules = [e
for e
in all_modules
if e[0]
in modules]
152 if "regex-filter" in self.options:
153 re_filter = re.compile(self.options[
"regex-filter"])
154 all_modules = [e
for e
in all_modules
if re_filter.match(e[0])
is not None]
158 if "library" in self.options:
159 lib = self.options[
"library"].strip()
160 all_modules = [e
for e
in all_modules
if os.path.basenam(e[1]) == lib]
163 self.
noindexnoindex = [
" :noindex:"]
if "noindex" in self.options
else []
169 for name, library
in sorted(all_modules):
170 module = register_module(name)
172 if "package" in self.options
and module.package() != self.options[
"package"]:
176 all_nodes += self.
show_moduleshow_module(module, library)
184 "group": directives.unchanged,
185 "variables": directives.unchanged,
186 "regex-filter": directives.unchanged,
187 "description-regex-filter": directives.unchanged,
188 "noindex": directives.flag,
192 from ROOT
import Belle2
194 self.
noindexnoindex = [
" :noindex:"]
if "noindex" in self.options
else []
198 desc_regex_filter =
None
199 if "variables" in self.options:
200 explicit_list = [e.strip()
for e
in self.options[
"variables"].split(
",")]
201 if "regex-filter" in self.options:
202 regex_filter = re.compile(self.options[
"regex-filter"])
203 if "description-regex-filter" in self.options:
204 desc_regex_filter = re.compile(self.options[
"description-regex-filter"])
206 for var
in manager.getVariables():
207 if "group" in self.options
and self.options[
"group"] != var.group:
209 if explicit_list
and var.name
not in explicit_list:
211 if regex_filter
and not regex_filter.match(var.name):
213 if desc_regex_filter
and not desc_regex_filter.match(var.description):
215 all_variables.append(var)
218 env = self.state.document.settings.env
219 for var
in sorted(all_variables, key=
lambda x: x.name):
224 if ":noindex:" in var.description:
225 index = [
" :noindex:"]
226 var.description = var.description.replace(
":noindex:",
"")
228 docstring = var.description.splitlines()
232 env.app.emit(
'autodoc-process-docstring',
"b2:variable", var.name, var,
None, docstring)
234 description = [f
".. b2:variable:: {var.name}"] + index + [
""]
235 description += [
" " + e
for e
in docstring]
236 if "group" not in self.options:
237 description += [
"", f
" :Group: {var.group}"]
239 all_nodes += parse_with_titles(self.state, description)
244 def html_page_context(app, pagename, templatename, context, doctree):
245 """Provide Link to GitLab Repository, see https://mg.pov.lt/blog/sphinx-edit-on-github.html
247 this goes in conjunction with
248 site_scons/sphinx/_sphinxtemplates/sourcelink.html and adds a link to our
249 git repository instead to the local source link
252 if templatename !=
'page.html' or not doctree:
255 path = os.path.relpath(doctree.get(
'source'), app.builder.srcdir)
256 repository = app.config.basf2_repository
260 commit = app.config.basf2_commitid
261 context[
"source_url"] = f
"{repository}/-/blob/main/{path}"
263 context[
"source_url"] = f
"{repository}/-/blob/{commit}/{path}"
266 def gitlab_issue_role(role, rawtext, text, lineno, inliner, options=None, content=None):
271 issue_url = inliner.document.settings.env.app.config.basf2_issues
273 return [nodes.literal(rawtext, text=text, language=
None)], []
275 url = f
"{issue_url}/{text}"
276 return [nodes.reference(rawtext, text=text, refuri=url)], []
281 basf2.logging.log_level = basf2.LogLevel.WARNING
283 app.add_config_value(
"basf2_repository",
"",
True)
284 app.add_config_value(
"basf2_commitid",
"",
True)
285 app.add_config_value(
"basf2_issues",
"",
True)
286 app.add_domain(Basf2Domain)
287 app.add_directive(
"b2-modules", ModuleListDirective)
288 app.add_directive(
"b2-variables", VariableListDirective)
289 app.add_directive(
"docstring", RenderDocstring)
290 app.add_role(
"issue", gitlab_issue_role)
291 app.connect(
'html-page-context', html_page_context)
294 StandardDomain.initial_data[
"labels"][
"b2-modindex"] = (
"b2-modindex",
"",
"basf2 Module Index")
295 StandardDomain.initial_data[
"labels"][
"b2-varindex"] = (
"b2-varindex",
"",
"basf2 Variable Index")
296 StandardDomain.initial_data[
"anonlabels"][
"b2-modindex"] = (
"b2-modindex",
"")
297 StandardDomain.initial_data[
"anonlabels"][
"b2-varindex"] = (
"b2-varindex",
"")
298 return {
'version': 0.2}
static Manager & Instance()
get singleton instance.
def show_module(self, module, library)