Belle II Software development
basf2domain.py
1
8
9from docutils import nodes
10from docutils.parsers.rst import directives
11from sphinx import addnodes
12from sphinx.roles import XRefRole
13from sphinx.directives import ObjectDescription
14from sphinx.domains import Domain, ObjType, Index
15from sphinx.util.docfields import TypedField, Field
16from sphinx.util.nodes import make_refnode
17from sphinx.util import logging
18logger = logging.getLogger(__name__)
19
20
21class Basf2Object(ObjectDescription):
22 """
23 A basf2 x-ref directive registered with Sphinx.add_object_type().
24 Basically copied together from sphinxcontrib-domaintools and sphinxcontrib-adadomain.
25 """
26
27
28
29 doc_field_types = {
30 TypedField("parameter", label="Parameters", names=("param", "parameter", "arg", "argument"),
31 typenames=("paramtype", "type"), typerolename=None, can_collapse=True),
32 Field('returnvalue', label='Returns', has_arg=False,
33 names=('returns', 'return')),
34 }
35
36 def handle_signature(self, sig, signode):
37 signode.clear()
38
39 # check if we have arguments
40 try:
41 name, args = sig.split("(", 1)
42 except ValueError:
43 name = sig
44 args = None
45
46 # Add the name of the signature
47 name = name.strip()
48 signode += addnodes.desc_name(name, name)
49
50 # if there was a opening parenthesis add all the arguments to the
51 # parameter list, separate them by comma
52 if args:
53 paramlist = addnodes.desc_parameterlist()
54 for arg in (e.strip() for e in args[:-1].split(",")):
55 if not arg:
56 continue
57 paramlist += addnodes.desc_parameter(arg, arg)
58 signode += paramlist
59
60 # return the object name for referencing
61 return name
62
63
64
65 def add_target_and_index(self, name, sig, signode):
66 # Create a full id from objtype and name
67 targetname = f'{self.objtype}-{name}'
68 # and append the id to the object node
69 signode['ids'].append(targetname)
70 self.state.document.note_explicit_target(signode)
71
72 # remember the name -> (document, targetname) mapping in the domain data
73 # dictionary so that we can use it to resolve x-refs
74 ddata = self.env.domaindata["b2"][self.objtype + 's']
75
76 if name in ddata:
77 # already exists, give warning
78 logger.warn(f"Duplicate description of basf2 {self.objtype} {name}, " +
79 "Other instance in " + self.env.doc2path(ddata[name][0]) +
80 ", please add ':noindex:' to one",
81 location=(self.env.docname, self.lineno))
82 else:
83 ddata[name] = (self.env.docname, targetname)
84
85
86class Basf2ModuleIndex(Index):
87 """Create an alphabetic index of all modules"""
88
89 name = "modindex"
90 localname = "basf2 Module Index"
91 shortname = "basf2 modules"
92
93 def generate(self, docnames=None):
94 content = {}
95 modules = self.domain.data["modules"].items()
96 for modname, (docname, target) in sorted(modules):
97 letter = modname[0].upper()
98 content.setdefault(letter, [])
99 content[letter].append([modname, 0, docname, target, "", "", ""])
100 return list(content.items()), False
101
102
103
105 """Create an alphabetic index of all variables"""
106
107 name = "varindex"
108 localname = "basf2 Variable Index"
109 shortname = "basf2 variables"
110
111 def generate(self, docnames=None):
112 content = {}
113 modules = self.domain.data["variables"].items()
114 modules = sorted(modules, key=lambda x: x[0].lower())
115 for modname, (docname, target) in modules:
116 letter = modname[0].upper()
117 content.setdefault(letter, [])
118 content[letter].append([modname, 0, docname, target, "", "", ""])
119 return list(content.items()), False
120
121
122
124 # extend the option spec to accept a source: URL
125 option_spec = Basf2Object.option_spec.copy() if hasattr(Basf2Object, "option_spec") else {}
126 option_spec.update({
127 "source": directives.uri, # support :source: option
128 })
129
130 def handle_signature(self, sig, signode):
131 # Call parent to parse the signature and add the variable name
132 name = super().handle_signature(sig, signode)
133
134 # If :source: option is given, add a [source] link inline
135 if "source" in self.options:
136 uri = self.options["source"]
137 linknode = nodes.reference('', '[source]', refuri=uri, classes=['var-source-link'])
138 signode += linknode
139
140 return name
141
142
143class Basf2Domain(Domain):
144 """basf2 Software Domain"""
145
146 name = "b2"
147 label = "Belle II Software"
148 object_types = {
149 "module": ObjType("module", "mod"),
150 "variable": ObjType("variable", "var")
151 }
152
153 directives = {
154 "module": Basf2Object,
155 "variable": Basf2VariableObject,
156 }
157 roles = {
158 "mod": XRefRole(),
159 "var": XRefRole(),
160 }
161 initial_data = {
162 "modules": {},
163 "variables": {},
164 }
165 indices = [
166 Basf2ModuleIndex,
167 Basf2VariableIndex,
168 ]
169
170
171 def clear_doc(self, docname):
172 """Remove the existing domain data for a given document name"""
173 for t in "modules", "variables":
174 try:
175 for name, (doc, target) in list(self.data[t].items()):
176 if doc == docname:
177 del self.data[t][name]
178 except Exception:
179 pass
180
181
182 def get_objects(self):
183 for i, type in enumerate(["modules", "variables"]):
184 for name, (docname, target) in self.data[type].items():
185 yield (name, name, type, docname, target, i)
186
187 def get_type_name(self, type, primary=False):
188 # never prepend "Default"
189 return type.lname
190
191 def resolve_xref(self, env, fromdocname, builder,
192 typ, target, node, contnode):
193 t = {"mod": "modules", "var": "variables"}[typ]
194 try:
195 docname, labelid = self.data[t][target]
196 return make_refnode(builder, fromdocname, docname,
197 labelid, contnode)
198 except Exception:
199 return None
200
clear_doc(self, docname)