Statistics
| Branch: | Tag: | Revision:

root / lib / build / sphinx_ext.py @ c83c0410

History | View | Annotate | Download (11.7 kB)

1 5f43f393 Michael Hanselmann
#
2 5f43f393 Michael Hanselmann
#
3 5f43f393 Michael Hanselmann
4 c83c0410 Iustin Pop
# Copyright (C) 2011, 2012, 2013 Google Inc.
5 5f43f393 Michael Hanselmann
#
6 5f43f393 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 5f43f393 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 5f43f393 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 5f43f393 Michael Hanselmann
# (at your option) any later version.
10 5f43f393 Michael Hanselmann
#
11 5f43f393 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 5f43f393 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 5f43f393 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 5f43f393 Michael Hanselmann
# General Public License for more details.
15 5f43f393 Michael Hanselmann
#
16 5f43f393 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 5f43f393 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 5f43f393 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 5f43f393 Michael Hanselmann
# 02110-1301, USA.
20 5f43f393 Michael Hanselmann
21 5f43f393 Michael Hanselmann
22 5f43f393 Michael Hanselmann
"""Sphinx extension for building opcode documentation.
23 5f43f393 Michael Hanselmann

24 5f43f393 Michael Hanselmann
"""
25 5f43f393 Michael Hanselmann
26 5ce58234 Michael Hanselmann
import re
27 5f43f393 Michael Hanselmann
from cStringIO import StringIO
28 5f43f393 Michael Hanselmann
29 dac59ac5 Michael Hanselmann
import docutils.statemachine
30 685d3b42 Michael Hanselmann
import docutils.nodes
31 685d3b42 Michael Hanselmann
import docutils.utils
32 5ce58234 Michael Hanselmann
import docutils.parsers.rst
33 5f43f393 Michael Hanselmann
34 dac59ac5 Michael Hanselmann
import sphinx.errors
35 dac59ac5 Michael Hanselmann
import sphinx.util.compat
36 5ce58234 Michael Hanselmann
import sphinx.roles
37 5ce58234 Michael Hanselmann
import sphinx.addnodes
38 5f43f393 Michael Hanselmann
39 b6d02fa9 Iustin Pop
s_compat = sphinx.util.compat
40 b6d02fa9 Iustin Pop
41 5ce58234 Michael Hanselmann
try:
42 5ce58234 Michael Hanselmann
  # Access to a protected member of a client class
43 5ce58234 Michael Hanselmann
  # pylint: disable=W0212
44 5ce58234 Michael Hanselmann
  orig_manpage_role = docutils.parsers.rst.roles._roles["manpage"]
45 5ce58234 Michael Hanselmann
except (AttributeError, ValueError, KeyError), err:
46 5ce58234 Michael Hanselmann
  # Normally the "manpage" role is registered by sphinx/roles.py
47 5ce58234 Michael Hanselmann
  raise Exception("Can't find reST role named 'manpage': %s" % err)
48 5ce58234 Michael Hanselmann
49 685d3b42 Michael Hanselmann
from ganeti import constants
50 685d3b42 Michael Hanselmann
from ganeti import compat
51 685d3b42 Michael Hanselmann
from ganeti import errors
52 5f43f393 Michael Hanselmann
from ganeti import utils
53 5f43f393 Michael Hanselmann
from ganeti import opcodes
54 5f43f393 Michael Hanselmann
from ganeti import ht
55 b4fcee5b Michael Hanselmann
from ganeti import rapi
56 12e0ee0d Michael Hanselmann
from ganeti import luxi
57 5ce58234 Michael Hanselmann
from ganeti import _autoconf
58 b4fcee5b Michael Hanselmann
59 b459a848 Andrea Spadaccini
import ganeti.rapi.rlib2 # pylint: disable=W0611
60 5f43f393 Michael Hanselmann
61 5f43f393 Michael Hanselmann
62 5ce58234 Michael Hanselmann
#: Regular expression for man page names
63 5ce58234 Michael Hanselmann
_MAN_RE = re.compile(r"^(?P<name>[-\w_]+)\((?P<section>\d+)\)$")
64 5ce58234 Michael Hanselmann
65 5ce58234 Michael Hanselmann
66 5ce58234 Michael Hanselmann
class ReSTError(Exception):
67 5ce58234 Michael Hanselmann
  """Custom class for generating errors in Sphinx.
68 5ce58234 Michael Hanselmann

69 5ce58234 Michael Hanselmann
  """
70 5ce58234 Michael Hanselmann
71 5ce58234 Michael Hanselmann
72 a19a6326 Michael Hanselmann
def _GetCommonParamNames():
73 a19a6326 Michael Hanselmann
  """Builds a list of parameters common to all opcodes.
74 a19a6326 Michael Hanselmann

75 a19a6326 Michael Hanselmann
  """
76 a19a6326 Michael Hanselmann
  names = set(map(compat.fst, opcodes.OpCode.OP_PARAMS))
77 a19a6326 Michael Hanselmann
78 a19a6326 Michael Hanselmann
  # The "depends" attribute should be listed
79 a19a6326 Michael Hanselmann
  names.remove(opcodes.DEPEND_ATTR)
80 a19a6326 Michael Hanselmann
81 a19a6326 Michael Hanselmann
  return names
82 a19a6326 Michael Hanselmann
83 a19a6326 Michael Hanselmann
84 a19a6326 Michael Hanselmann
COMMON_PARAM_NAMES = _GetCommonParamNames()
85 5f43f393 Michael Hanselmann
86 685d3b42 Michael Hanselmann
#: Namespace for evaluating expressions
87 b4fcee5b Michael Hanselmann
EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors,
88 a12f0ef8 Michael Hanselmann
               rlib2=rapi.rlib2, luxi=luxi, rapi=rapi)
89 685d3b42 Michael Hanselmann
90 3ac3f5e4 Andrea Spadaccini
# Constants documentation for man pages
91 3ac3f5e4 Andrea Spadaccini
CV_ECODES_DOC = "ecodes"
92 3ac3f5e4 Andrea Spadaccini
# We don't care about the leak of variables _, name and doc here.
93 3ac3f5e4 Andrea Spadaccini
# pylint: disable=W0621
94 3ac3f5e4 Andrea Spadaccini
CV_ECODES_DOC_LIST = [(name, doc) for (_, name, doc) in constants.CV_ALL_ECODES]
95 3ac3f5e4 Andrea Spadaccini
DOCUMENTED_CONSTANTS = {
96 3ac3f5e4 Andrea Spadaccini
  CV_ECODES_DOC: CV_ECODES_DOC_LIST,
97 3ac3f5e4 Andrea Spadaccini
  }
98 3ac3f5e4 Andrea Spadaccini
99 5f43f393 Michael Hanselmann
100 dac59ac5 Michael Hanselmann
class OpcodeError(sphinx.errors.SphinxError):
101 5f43f393 Michael Hanselmann
  category = "Opcode error"
102 5f43f393 Michael Hanselmann
103 5f43f393 Michael Hanselmann
104 5f43f393 Michael Hanselmann
def _SplitOption(text):
105 5f43f393 Michael Hanselmann
  """Split simple option list.
106 5f43f393 Michael Hanselmann

107 5f43f393 Michael Hanselmann
  @type text: string
108 5f43f393 Michael Hanselmann
  @param text: Options, e.g. "foo, bar, baz"
109 5f43f393 Michael Hanselmann

110 5f43f393 Michael Hanselmann
  """
111 5f43f393 Michael Hanselmann
  return [i.strip(",").strip() for i in text.split()]
112 5f43f393 Michael Hanselmann
113 5f43f393 Michael Hanselmann
114 5f43f393 Michael Hanselmann
def _ParseAlias(text):
115 5f43f393 Michael Hanselmann
  """Parse simple assignment option.
116 5f43f393 Michael Hanselmann

117 5f43f393 Michael Hanselmann
  @type text: string
118 5f43f393 Michael Hanselmann
  @param text: Assignments, e.g. "foo=bar, hello=world"
119 5f43f393 Michael Hanselmann
  @rtype: dict
120 5f43f393 Michael Hanselmann

121 5f43f393 Michael Hanselmann
  """
122 5f43f393 Michael Hanselmann
  result = {}
123 5f43f393 Michael Hanselmann
124 5f43f393 Michael Hanselmann
  for part in _SplitOption(text):
125 5f43f393 Michael Hanselmann
    if "=" not in part:
126 5f43f393 Michael Hanselmann
      raise OpcodeError("Invalid option format, missing equal sign")
127 5f43f393 Michael Hanselmann
128 5f43f393 Michael Hanselmann
    (name, value) = part.split("=", 1)
129 5f43f393 Michael Hanselmann
130 5f43f393 Michael Hanselmann
    result[name.strip()] = value.strip()
131 5f43f393 Michael Hanselmann
132 5f43f393 Michael Hanselmann
  return result
133 5f43f393 Michael Hanselmann
134 5f43f393 Michael Hanselmann
135 5f43f393 Michael Hanselmann
def _BuildOpcodeParams(op_id, include, exclude, alias):
136 5f43f393 Michael Hanselmann
  """Build opcode parameter documentation.
137 5f43f393 Michael Hanselmann

138 5f43f393 Michael Hanselmann
  @type op_id: string
139 5f43f393 Michael Hanselmann
  @param op_id: Opcode ID
140 5f43f393 Michael Hanselmann

141 5f43f393 Michael Hanselmann
  """
142 5f43f393 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
143 5f43f393 Michael Hanselmann
144 5f43f393 Michael Hanselmann
  params_with_alias = \
145 5f43f393 Michael Hanselmann
    utils.NiceSort([(alias.get(name, name), name, default, test, doc)
146 5f43f393 Michael Hanselmann
                    for (name, default, test, doc) in op_cls.GetAllParams()],
147 eb62069e Iustin Pop
                   key=compat.fst)
148 5f43f393 Michael Hanselmann
149 5f43f393 Michael Hanselmann
  for (rapi_name, name, default, test, doc) in params_with_alias:
150 2ed0e208 Iustin Pop
    # Hide common parameters if not explicitly included
151 5f43f393 Michael Hanselmann
    if (name in COMMON_PARAM_NAMES and
152 5f43f393 Michael Hanselmann
        (not include or name not in include)):
153 5f43f393 Michael Hanselmann
      continue
154 5f43f393 Michael Hanselmann
    if exclude is not None and name in exclude:
155 5f43f393 Michael Hanselmann
      continue
156 5f43f393 Michael Hanselmann
    if include is not None and name not in include:
157 5f43f393 Michael Hanselmann
      continue
158 5f43f393 Michael Hanselmann
159 5f43f393 Michael Hanselmann
    has_default = default is not ht.NoDefault
160 5f43f393 Michael Hanselmann
    has_test = not (test is None or test is ht.NoType)
161 5f43f393 Michael Hanselmann
162 5f43f393 Michael Hanselmann
    buf = StringIO()
163 c83c0410 Iustin Pop
    buf.write("``%s``" % (rapi_name,))
164 5f43f393 Michael Hanselmann
    if has_default or has_test:
165 5f43f393 Michael Hanselmann
      buf.write(" (")
166 5f43f393 Michael Hanselmann
      if has_default:
167 c83c0410 Iustin Pop
        buf.write("defaults to ``%s``" % (default,))
168 5f43f393 Michael Hanselmann
        if has_test:
169 5f43f393 Michael Hanselmann
          buf.write(", ")
170 5f43f393 Michael Hanselmann
      if has_test:
171 c83c0410 Iustin Pop
        buf.write("must be ``%s``" % (test,))
172 5f43f393 Michael Hanselmann
      buf.write(")")
173 5f43f393 Michael Hanselmann
    yield buf.getvalue()
174 5f43f393 Michael Hanselmann
175 5f43f393 Michael Hanselmann
    # Add text
176 5f43f393 Michael Hanselmann
    for line in doc.splitlines():
177 5f43f393 Michael Hanselmann
      yield "  %s" % line
178 5f43f393 Michael Hanselmann
179 5f43f393 Michael Hanselmann
180 f96c51a0 Michael Hanselmann
def _BuildOpcodeResult(op_id):
181 f96c51a0 Michael Hanselmann
  """Build opcode result documentation.
182 f96c51a0 Michael Hanselmann

183 f96c51a0 Michael Hanselmann
  @type op_id: string
184 f96c51a0 Michael Hanselmann
  @param op_id: Opcode ID
185 f96c51a0 Michael Hanselmann

186 f96c51a0 Michael Hanselmann
  """
187 f96c51a0 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
188 f96c51a0 Michael Hanselmann
189 f96c51a0 Michael Hanselmann
  result_fn = getattr(op_cls, "OP_RESULT", None)
190 f96c51a0 Michael Hanselmann
191 f96c51a0 Michael Hanselmann
  if not result_fn:
192 f96c51a0 Michael Hanselmann
    raise OpcodeError("Opcode '%s' has no result description" % op_id)
193 f96c51a0 Michael Hanselmann
194 f96c51a0 Michael Hanselmann
  return "``%s``" % result_fn
195 f96c51a0 Michael Hanselmann
196 f96c51a0 Michael Hanselmann
197 b6d02fa9 Iustin Pop
class OpcodeParams(s_compat.Directive):
198 5f43f393 Michael Hanselmann
  """Custom directive for opcode parameters.
199 5f43f393 Michael Hanselmann

200 5f43f393 Michael Hanselmann
  See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
201 5f43f393 Michael Hanselmann

202 5f43f393 Michael Hanselmann
  """
203 5f43f393 Michael Hanselmann
  has_content = False
204 5f43f393 Michael Hanselmann
  required_arguments = 1
205 5f43f393 Michael Hanselmann
  optional_arguments = 0
206 5f43f393 Michael Hanselmann
  final_argument_whitespace = False
207 5f43f393 Michael Hanselmann
  option_spec = dict(include=_SplitOption, exclude=_SplitOption,
208 5f43f393 Michael Hanselmann
                     alias=_ParseAlias)
209 5f43f393 Michael Hanselmann
210 5f43f393 Michael Hanselmann
  def run(self):
211 5f43f393 Michael Hanselmann
    op_id = self.arguments[0]
212 5f43f393 Michael Hanselmann
    include = self.options.get("include", None)
213 5f43f393 Michael Hanselmann
    exclude = self.options.get("exclude", None)
214 5f43f393 Michael Hanselmann
    alias = self.options.get("alias", {})
215 5f43f393 Michael Hanselmann
216 5f43f393 Michael Hanselmann
    tab_width = 2
217 5f43f393 Michael Hanselmann
    path = op_id
218 5f43f393 Michael Hanselmann
    include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
219 5f43f393 Michael Hanselmann
220 5f43f393 Michael Hanselmann
    # Inject into state machine
221 dac59ac5 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
222 dac59ac5 Michael Hanselmann
                                                       convert_whitespace=1)
223 5f43f393 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
224 5f43f393 Michael Hanselmann
225 5f43f393 Michael Hanselmann
    return []
226 5f43f393 Michael Hanselmann
227 5f43f393 Michael Hanselmann
228 b6d02fa9 Iustin Pop
class OpcodeResult(s_compat.Directive):
229 f96c51a0 Michael Hanselmann
  """Custom directive for opcode result.
230 f96c51a0 Michael Hanselmann

231 f96c51a0 Michael Hanselmann
  See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
232 f96c51a0 Michael Hanselmann

233 f96c51a0 Michael Hanselmann
  """
234 f96c51a0 Michael Hanselmann
  has_content = False
235 f96c51a0 Michael Hanselmann
  required_arguments = 1
236 f96c51a0 Michael Hanselmann
  optional_arguments = 0
237 f96c51a0 Michael Hanselmann
  final_argument_whitespace = False
238 f96c51a0 Michael Hanselmann
239 f96c51a0 Michael Hanselmann
  def run(self):
240 f96c51a0 Michael Hanselmann
    op_id = self.arguments[0]
241 f96c51a0 Michael Hanselmann
242 f96c51a0 Michael Hanselmann
    tab_width = 2
243 f96c51a0 Michael Hanselmann
    path = op_id
244 f96c51a0 Michael Hanselmann
    include_text = _BuildOpcodeResult(op_id)
245 f96c51a0 Michael Hanselmann
246 f96c51a0 Michael Hanselmann
    # Inject into state machine
247 f96c51a0 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
248 f96c51a0 Michael Hanselmann
                                                       convert_whitespace=1)
249 f96c51a0 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
250 f96c51a0 Michael Hanselmann
251 f96c51a0 Michael Hanselmann
    return []
252 f96c51a0 Michael Hanselmann
253 f96c51a0 Michael Hanselmann
254 685d3b42 Michael Hanselmann
def PythonEvalRole(role, rawtext, text, lineno, inliner,
255 685d3b42 Michael Hanselmann
                   options={}, content=[]):
256 685d3b42 Michael Hanselmann
  """Custom role to evaluate Python expressions.
257 685d3b42 Michael Hanselmann

258 685d3b42 Michael Hanselmann
  The expression's result is included as a literal.
259 685d3b42 Michael Hanselmann

260 685d3b42 Michael Hanselmann
  """
261 b459a848 Andrea Spadaccini
  # pylint: disable=W0102,W0613,W0142
262 1aa50158 Michael Hanselmann
  # W0102: Dangerous default value as argument
263 1aa50158 Michael Hanselmann
  # W0142: Used * or ** magic
264 1aa50158 Michael Hanselmann
  # W0613: Unused argument
265 1aa50158 Michael Hanselmann
266 685d3b42 Michael Hanselmann
  code = docutils.utils.unescape(text, restore_backslashes=True)
267 685d3b42 Michael Hanselmann
268 685d3b42 Michael Hanselmann
  try:
269 685d3b42 Michael Hanselmann
    result = eval(code, EVAL_NS)
270 b459a848 Andrea Spadaccini
  except Exception, err: # pylint: disable=W0703
271 685d3b42 Michael Hanselmann
    msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
272 685d3b42 Michael Hanselmann
                                 line=lineno)
273 685d3b42 Michael Hanselmann
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
274 685d3b42 Michael Hanselmann
275 685d3b42 Michael Hanselmann
  node = docutils.nodes.literal("", unicode(result), **options)
276 685d3b42 Michael Hanselmann
277 685d3b42 Michael Hanselmann
  return ([node], [])
278 685d3b42 Michael Hanselmann
279 685d3b42 Michael Hanselmann
280 b6d02fa9 Iustin Pop
class PythonAssert(s_compat.Directive):
281 685d3b42 Michael Hanselmann
  """Custom directive for writing assertions.
282 685d3b42 Michael Hanselmann

283 685d3b42 Michael Hanselmann
  The content must be a valid Python expression. If its result does not
284 685d3b42 Michael Hanselmann
  evaluate to C{True}, the assertion fails.
285 685d3b42 Michael Hanselmann

286 685d3b42 Michael Hanselmann
  """
287 685d3b42 Michael Hanselmann
  has_content = True
288 685d3b42 Michael Hanselmann
  required_arguments = 0
289 685d3b42 Michael Hanselmann
  optional_arguments = 0
290 685d3b42 Michael Hanselmann
  final_argument_whitespace = False
291 685d3b42 Michael Hanselmann
292 685d3b42 Michael Hanselmann
  def run(self):
293 cbb86b63 Michael Hanselmann
    # Handle combinations of Sphinx and docutils not providing the wanted method
294 cbb86b63 Michael Hanselmann
    if hasattr(self, "assert_has_content"):
295 cbb86b63 Michael Hanselmann
      self.assert_has_content()
296 cbb86b63 Michael Hanselmann
    else:
297 cbb86b63 Michael Hanselmann
      assert self.content
298 685d3b42 Michael Hanselmann
299 685d3b42 Michael Hanselmann
    code = "\n".join(self.content)
300 685d3b42 Michael Hanselmann
301 685d3b42 Michael Hanselmann
    try:
302 685d3b42 Michael Hanselmann
      result = eval(code, EVAL_NS)
303 685d3b42 Michael Hanselmann
    except Exception, err:
304 685d3b42 Michael Hanselmann
      raise self.error("Failed to evaluate %r: %s" % (code, err))
305 685d3b42 Michael Hanselmann
306 685d3b42 Michael Hanselmann
    if not result:
307 685d3b42 Michael Hanselmann
      raise self.error("Assertion failed: %s" % (code, ))
308 685d3b42 Michael Hanselmann
309 685d3b42 Michael Hanselmann
    return []
310 685d3b42 Michael Hanselmann
311 685d3b42 Michael Hanselmann
312 95eb4188 Michael Hanselmann
def BuildQueryFields(fields):
313 95eb4188 Michael Hanselmann
  """Build query fields documentation.
314 95eb4188 Michael Hanselmann

315 95eb4188 Michael Hanselmann
  @type fields: dict (field name as key, field details as value)
316 95eb4188 Michael Hanselmann

317 95eb4188 Michael Hanselmann
  """
318 12637df5 Andrea Spadaccini
  defs = [(fdef.name, fdef.doc)
319 12637df5 Andrea Spadaccini
           for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
320 12637df5 Andrea Spadaccini
                                                      key=compat.fst)]
321 d505bc48 Iustin Pop
  return BuildValuesDoc(defs)
322 12637df5 Andrea Spadaccini
323 12637df5 Andrea Spadaccini
324 12637df5 Andrea Spadaccini
def BuildValuesDoc(values):
325 12637df5 Andrea Spadaccini
  """Builds documentation for a list of values
326 12637df5 Andrea Spadaccini

327 12637df5 Andrea Spadaccini
  @type values: list of tuples in the form (value, documentation)
328 12637df5 Andrea Spadaccini

329 12637df5 Andrea Spadaccini
  """
330 12637df5 Andrea Spadaccini
  for name, doc in values:
331 12637df5 Andrea Spadaccini
    assert len(doc.splitlines()) == 1
332 c83c0410 Iustin Pop
    yield "``%s``" % (name,)
333 c83c0410 Iustin Pop
    yield "  %s" % (doc,)
334 95eb4188 Michael Hanselmann
335 95eb4188 Michael Hanselmann
336 5ce58234 Michael Hanselmann
def _ManPageNodeClass(*args, **kwargs):
337 5ce58234 Michael Hanselmann
  """Generates a pending XRef like a ":doc:`...`" reference.
338 5ce58234 Michael Hanselmann

339 5ce58234 Michael Hanselmann
  """
340 5ce58234 Michael Hanselmann
  # Type for sphinx/environment.py:BuildEnvironment.resolve_references
341 5ce58234 Michael Hanselmann
  kwargs["reftype"] = "doc"
342 5ce58234 Michael Hanselmann
343 5ce58234 Michael Hanselmann
  # Force custom title
344 5ce58234 Michael Hanselmann
  kwargs["refexplicit"] = True
345 5ce58234 Michael Hanselmann
346 5ce58234 Michael Hanselmann
  return sphinx.addnodes.pending_xref(*args, **kwargs)
347 5ce58234 Michael Hanselmann
348 5ce58234 Michael Hanselmann
349 5ce58234 Michael Hanselmann
class _ManPageXRefRole(sphinx.roles.XRefRole):
350 5ce58234 Michael Hanselmann
  def __init__(self):
351 5ce58234 Michael Hanselmann
    """Initializes this class.
352 5ce58234 Michael Hanselmann

353 5ce58234 Michael Hanselmann
    """
354 5ce58234 Michael Hanselmann
    sphinx.roles.XRefRole.__init__(self, nodeclass=_ManPageNodeClass,
355 5ce58234 Michael Hanselmann
                                   warn_dangling=True)
356 5ce58234 Michael Hanselmann
357 5ce58234 Michael Hanselmann
    assert not hasattr(self, "converted"), \
358 5ce58234 Michael Hanselmann
      "Sphinx base class gained an attribute named 'converted'"
359 5ce58234 Michael Hanselmann
360 5ce58234 Michael Hanselmann
    self.converted = None
361 5ce58234 Michael Hanselmann
362 5ce58234 Michael Hanselmann
  def process_link(self, env, refnode, has_explicit_title, title, target):
363 5ce58234 Michael Hanselmann
    """Specialization for man page links.
364 5ce58234 Michael Hanselmann

365 5ce58234 Michael Hanselmann
    """
366 5ce58234 Michael Hanselmann
    if has_explicit_title:
367 5ce58234 Michael Hanselmann
      raise ReSTError("Setting explicit title is not allowed for man pages")
368 5ce58234 Michael Hanselmann
369 5ce58234 Michael Hanselmann
    # Check format and extract name and section
370 5ce58234 Michael Hanselmann
    m = _MAN_RE.match(title)
371 5ce58234 Michael Hanselmann
    if not m:
372 5ce58234 Michael Hanselmann
      raise ReSTError("Man page reference '%s' does not match regular"
373 5ce58234 Michael Hanselmann
                      " expression '%s'" % (title, _MAN_RE.pattern))
374 5ce58234 Michael Hanselmann
375 5ce58234 Michael Hanselmann
    name = m.group("name")
376 5ce58234 Michael Hanselmann
    section = int(m.group("section"))
377 5ce58234 Michael Hanselmann
378 5ce58234 Michael Hanselmann
    wanted_section = _autoconf.MAN_PAGES.get(name, None)
379 5ce58234 Michael Hanselmann
380 5ce58234 Michael Hanselmann
    if not (wanted_section is None or wanted_section == section):
381 5ce58234 Michael Hanselmann
      raise ReSTError("Referenced man page '%s' has section number %s, but the"
382 5ce58234 Michael Hanselmann
                      " reference uses section %s" %
383 5ce58234 Michael Hanselmann
                      (name, wanted_section, section))
384 5ce58234 Michael Hanselmann
385 5ce58234 Michael Hanselmann
    self.converted = bool(wanted_section is not None and
386 5ce58234 Michael Hanselmann
                          env.app.config.enable_manpages)
387 5ce58234 Michael Hanselmann
388 5ce58234 Michael Hanselmann
    if self.converted:
389 5ce58234 Michael Hanselmann
      # Create link to known man page
390 5ce58234 Michael Hanselmann
      return (title, "man-%s" % name)
391 5ce58234 Michael Hanselmann
    else:
392 5ce58234 Michael Hanselmann
      # No changes
393 5ce58234 Michael Hanselmann
      return (title, target)
394 5ce58234 Michael Hanselmann
395 5ce58234 Michael Hanselmann
396 5ce58234 Michael Hanselmann
def _ManPageRole(typ, rawtext, text, lineno, inliner, # pylint: disable=W0102
397 5ce58234 Michael Hanselmann
                 options={}, content=[]):
398 5ce58234 Michael Hanselmann
  """Custom role for man page references.
399 5ce58234 Michael Hanselmann

400 5ce58234 Michael Hanselmann
  Converts man pages to links if enabled during the build.
401 5ce58234 Michael Hanselmann

402 5ce58234 Michael Hanselmann
  """
403 5ce58234 Michael Hanselmann
  xref = _ManPageXRefRole()
404 5ce58234 Michael Hanselmann
405 5ce58234 Michael Hanselmann
  assert ht.TNone(xref.converted)
406 5ce58234 Michael Hanselmann
407 5ce58234 Michael Hanselmann
  # Check if it's a known man page
408 5ce58234 Michael Hanselmann
  try:
409 5ce58234 Michael Hanselmann
    result = xref(typ, rawtext, text, lineno, inliner,
410 5ce58234 Michael Hanselmann
                  options=options, content=content)
411 5ce58234 Michael Hanselmann
  except ReSTError, err:
412 5ce58234 Michael Hanselmann
    msg = inliner.reporter.error(str(err), line=lineno)
413 5ce58234 Michael Hanselmann
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
414 5ce58234 Michael Hanselmann
415 5ce58234 Michael Hanselmann
  assert ht.TBool(xref.converted)
416 5ce58234 Michael Hanselmann
417 5ce58234 Michael Hanselmann
  # Return if the conversion was successful (i.e. the man page was known and
418 5ce58234 Michael Hanselmann
  # conversion was enabled)
419 5ce58234 Michael Hanselmann
  if xref.converted:
420 5ce58234 Michael Hanselmann
    return result
421 5ce58234 Michael Hanselmann
422 5ce58234 Michael Hanselmann
  # Fallback if man page links are disabled or an unknown page is referenced
423 5ce58234 Michael Hanselmann
  return orig_manpage_role(typ, rawtext, text, lineno, inliner,
424 5ce58234 Michael Hanselmann
                           options=options, content=content)
425 95eb4188 Michael Hanselmann
426 95eb4188 Michael Hanselmann
427 5f43f393 Michael Hanselmann
def setup(app):
428 5f43f393 Michael Hanselmann
  """Sphinx extension callback.
429 5f43f393 Michael Hanselmann

430 5f43f393 Michael Hanselmann
  """
431 5ce58234 Michael Hanselmann
  # TODO: Implement Sphinx directive for query fields
432 5f43f393 Michael Hanselmann
  app.add_directive("opcode_params", OpcodeParams)
433 f96c51a0 Michael Hanselmann
  app.add_directive("opcode_result", OpcodeResult)
434 685d3b42 Michael Hanselmann
  app.add_directive("pyassert", PythonAssert)
435 685d3b42 Michael Hanselmann
  app.add_role("pyeval", PythonEvalRole)
436 5ce58234 Michael Hanselmann
437 5ce58234 Michael Hanselmann
  app.add_config_value("enable_manpages", False, True)
438 5ce58234 Michael Hanselmann
  app.add_role("manpage", _ManPageRole)