Statistics
| Branch: | Tag: | Revision:

root / lib / build / sphinx_ext.py @ 7142485a

History | View | Annotate | Download (8.2 kB)

1 5f43f393 Michael Hanselmann
#
2 5f43f393 Michael Hanselmann
#
3 5f43f393 Michael Hanselmann
4 d505bc48 Iustin Pop
# Copyright (C) 2011, 2012 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 5f43f393 Michael Hanselmann
from cStringIO import StringIO
27 5f43f393 Michael Hanselmann
28 dac59ac5 Michael Hanselmann
import docutils.statemachine
29 685d3b42 Michael Hanselmann
import docutils.nodes
30 685d3b42 Michael Hanselmann
import docutils.utils
31 5f43f393 Michael Hanselmann
32 dac59ac5 Michael Hanselmann
import sphinx.errors
33 dac59ac5 Michael Hanselmann
import sphinx.util.compat
34 5f43f393 Michael Hanselmann
35 b6d02fa9 Iustin Pop
s_compat = sphinx.util.compat
36 b6d02fa9 Iustin Pop
37 685d3b42 Michael Hanselmann
from ganeti import constants
38 685d3b42 Michael Hanselmann
from ganeti import compat
39 685d3b42 Michael Hanselmann
from ganeti import errors
40 5f43f393 Michael Hanselmann
from ganeti import utils
41 5f43f393 Michael Hanselmann
from ganeti import opcodes
42 5f43f393 Michael Hanselmann
from ganeti import ht
43 b4fcee5b Michael Hanselmann
from ganeti import rapi
44 b4fcee5b Michael Hanselmann
45 b459a848 Andrea Spadaccini
import ganeti.rapi.rlib2 # pylint: disable=W0611
46 5f43f393 Michael Hanselmann
47 5f43f393 Michael Hanselmann
48 a19a6326 Michael Hanselmann
def _GetCommonParamNames():
49 a19a6326 Michael Hanselmann
  """Builds a list of parameters common to all opcodes.
50 a19a6326 Michael Hanselmann

51 a19a6326 Michael Hanselmann
  """
52 a19a6326 Michael Hanselmann
  names = set(map(compat.fst, opcodes.OpCode.OP_PARAMS))
53 a19a6326 Michael Hanselmann
54 a19a6326 Michael Hanselmann
  # The "depends" attribute should be listed
55 a19a6326 Michael Hanselmann
  names.remove(opcodes.DEPEND_ATTR)
56 a19a6326 Michael Hanselmann
57 a19a6326 Michael Hanselmann
  return names
58 a19a6326 Michael Hanselmann
59 a19a6326 Michael Hanselmann
60 a19a6326 Michael Hanselmann
COMMON_PARAM_NAMES = _GetCommonParamNames()
61 5f43f393 Michael Hanselmann
62 685d3b42 Michael Hanselmann
#: Namespace for evaluating expressions
63 b4fcee5b Michael Hanselmann
EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors,
64 b4fcee5b Michael Hanselmann
               rlib2=rapi.rlib2)
65 685d3b42 Michael Hanselmann
66 3ac3f5e4 Andrea Spadaccini
# Constants documentation for man pages
67 3ac3f5e4 Andrea Spadaccini
CV_ECODES_DOC = "ecodes"
68 3ac3f5e4 Andrea Spadaccini
# We don't care about the leak of variables _, name and doc here.
69 3ac3f5e4 Andrea Spadaccini
# pylint: disable=W0621
70 3ac3f5e4 Andrea Spadaccini
CV_ECODES_DOC_LIST = [(name, doc) for (_, name, doc) in constants.CV_ALL_ECODES]
71 3ac3f5e4 Andrea Spadaccini
DOCUMENTED_CONSTANTS = {
72 3ac3f5e4 Andrea Spadaccini
  CV_ECODES_DOC: CV_ECODES_DOC_LIST,
73 3ac3f5e4 Andrea Spadaccini
  }
74 3ac3f5e4 Andrea Spadaccini
75 5f43f393 Michael Hanselmann
76 dac59ac5 Michael Hanselmann
class OpcodeError(sphinx.errors.SphinxError):
77 5f43f393 Michael Hanselmann
  category = "Opcode error"
78 5f43f393 Michael Hanselmann
79 5f43f393 Michael Hanselmann
80 5f43f393 Michael Hanselmann
def _SplitOption(text):
81 5f43f393 Michael Hanselmann
  """Split simple option list.
82 5f43f393 Michael Hanselmann

83 5f43f393 Michael Hanselmann
  @type text: string
84 5f43f393 Michael Hanselmann
  @param text: Options, e.g. "foo, bar, baz"
85 5f43f393 Michael Hanselmann

86 5f43f393 Michael Hanselmann
  """
87 5f43f393 Michael Hanselmann
  return [i.strip(",").strip() for i in text.split()]
88 5f43f393 Michael Hanselmann
89 5f43f393 Michael Hanselmann
90 5f43f393 Michael Hanselmann
def _ParseAlias(text):
91 5f43f393 Michael Hanselmann
  """Parse simple assignment option.
92 5f43f393 Michael Hanselmann

93 5f43f393 Michael Hanselmann
  @type text: string
94 5f43f393 Michael Hanselmann
  @param text: Assignments, e.g. "foo=bar, hello=world"
95 5f43f393 Michael Hanselmann
  @rtype: dict
96 5f43f393 Michael Hanselmann

97 5f43f393 Michael Hanselmann
  """
98 5f43f393 Michael Hanselmann
  result = {}
99 5f43f393 Michael Hanselmann
100 5f43f393 Michael Hanselmann
  for part in _SplitOption(text):
101 5f43f393 Michael Hanselmann
    if "=" not in part:
102 5f43f393 Michael Hanselmann
      raise OpcodeError("Invalid option format, missing equal sign")
103 5f43f393 Michael Hanselmann
104 5f43f393 Michael Hanselmann
    (name, value) = part.split("=", 1)
105 5f43f393 Michael Hanselmann
106 5f43f393 Michael Hanselmann
    result[name.strip()] = value.strip()
107 5f43f393 Michael Hanselmann
108 5f43f393 Michael Hanselmann
  return result
109 5f43f393 Michael Hanselmann
110 5f43f393 Michael Hanselmann
111 5f43f393 Michael Hanselmann
def _BuildOpcodeParams(op_id, include, exclude, alias):
112 5f43f393 Michael Hanselmann
  """Build opcode parameter documentation.
113 5f43f393 Michael Hanselmann

114 5f43f393 Michael Hanselmann
  @type op_id: string
115 5f43f393 Michael Hanselmann
  @param op_id: Opcode ID
116 5f43f393 Michael Hanselmann

117 5f43f393 Michael Hanselmann
  """
118 5f43f393 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
119 5f43f393 Michael Hanselmann
120 5f43f393 Michael Hanselmann
  params_with_alias = \
121 5f43f393 Michael Hanselmann
    utils.NiceSort([(alias.get(name, name), name, default, test, doc)
122 5f43f393 Michael Hanselmann
                    for (name, default, test, doc) in op_cls.GetAllParams()],
123 eb62069e Iustin Pop
                   key=compat.fst)
124 5f43f393 Michael Hanselmann
125 5f43f393 Michael Hanselmann
  for (rapi_name, name, default, test, doc) in params_with_alias:
126 5f43f393 Michael Hanselmann
    # Hide common parameters if not explicitely included
127 5f43f393 Michael Hanselmann
    if (name in COMMON_PARAM_NAMES and
128 5f43f393 Michael Hanselmann
        (not include or name not in include)):
129 5f43f393 Michael Hanselmann
      continue
130 5f43f393 Michael Hanselmann
    if exclude is not None and name in exclude:
131 5f43f393 Michael Hanselmann
      continue
132 5f43f393 Michael Hanselmann
    if include is not None and name not in include:
133 5f43f393 Michael Hanselmann
      continue
134 5f43f393 Michael Hanselmann
135 5f43f393 Michael Hanselmann
    has_default = default is not ht.NoDefault
136 5f43f393 Michael Hanselmann
    has_test = not (test is None or test is ht.NoType)
137 5f43f393 Michael Hanselmann
138 5f43f393 Michael Hanselmann
    buf = StringIO()
139 5f43f393 Michael Hanselmann
    buf.write("``%s``" % rapi_name)
140 5f43f393 Michael Hanselmann
    if has_default or has_test:
141 5f43f393 Michael Hanselmann
      buf.write(" (")
142 5f43f393 Michael Hanselmann
      if has_default:
143 5f43f393 Michael Hanselmann
        buf.write("defaults to ``%s``" % default)
144 5f43f393 Michael Hanselmann
        if has_test:
145 5f43f393 Michael Hanselmann
          buf.write(", ")
146 5f43f393 Michael Hanselmann
      if has_test:
147 5f43f393 Michael Hanselmann
        buf.write("must be ``%s``" % test)
148 5f43f393 Michael Hanselmann
      buf.write(")")
149 5f43f393 Michael Hanselmann
    yield buf.getvalue()
150 5f43f393 Michael Hanselmann
151 5f43f393 Michael Hanselmann
    # Add text
152 5f43f393 Michael Hanselmann
    for line in doc.splitlines():
153 5f43f393 Michael Hanselmann
      yield "  %s" % line
154 5f43f393 Michael Hanselmann
155 5f43f393 Michael Hanselmann
156 f96c51a0 Michael Hanselmann
def _BuildOpcodeResult(op_id):
157 f96c51a0 Michael Hanselmann
  """Build opcode result documentation.
158 f96c51a0 Michael Hanselmann

159 f96c51a0 Michael Hanselmann
  @type op_id: string
160 f96c51a0 Michael Hanselmann
  @param op_id: Opcode ID
161 f96c51a0 Michael Hanselmann

162 f96c51a0 Michael Hanselmann
  """
163 f96c51a0 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
164 f96c51a0 Michael Hanselmann
165 f96c51a0 Michael Hanselmann
  result_fn = getattr(op_cls, "OP_RESULT", None)
166 f96c51a0 Michael Hanselmann
167 f96c51a0 Michael Hanselmann
  if not result_fn:
168 f96c51a0 Michael Hanselmann
    raise OpcodeError("Opcode '%s' has no result description" % op_id)
169 f96c51a0 Michael Hanselmann
170 f96c51a0 Michael Hanselmann
  return "``%s``" % result_fn
171 f96c51a0 Michael Hanselmann
172 f96c51a0 Michael Hanselmann
173 b6d02fa9 Iustin Pop
class OpcodeParams(s_compat.Directive):
174 5f43f393 Michael Hanselmann
  """Custom directive for opcode parameters.
175 5f43f393 Michael Hanselmann

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

178 5f43f393 Michael Hanselmann
  """
179 5f43f393 Michael Hanselmann
  has_content = False
180 5f43f393 Michael Hanselmann
  required_arguments = 1
181 5f43f393 Michael Hanselmann
  optional_arguments = 0
182 5f43f393 Michael Hanselmann
  final_argument_whitespace = False
183 5f43f393 Michael Hanselmann
  option_spec = dict(include=_SplitOption, exclude=_SplitOption,
184 5f43f393 Michael Hanselmann
                     alias=_ParseAlias)
185 5f43f393 Michael Hanselmann
186 5f43f393 Michael Hanselmann
  def run(self):
187 5f43f393 Michael Hanselmann
    op_id = self.arguments[0]
188 5f43f393 Michael Hanselmann
    include = self.options.get("include", None)
189 5f43f393 Michael Hanselmann
    exclude = self.options.get("exclude", None)
190 5f43f393 Michael Hanselmann
    alias = self.options.get("alias", {})
191 5f43f393 Michael Hanselmann
192 5f43f393 Michael Hanselmann
    tab_width = 2
193 5f43f393 Michael Hanselmann
    path = op_id
194 5f43f393 Michael Hanselmann
    include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
195 5f43f393 Michael Hanselmann
196 5f43f393 Michael Hanselmann
    # Inject into state machine
197 dac59ac5 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
198 dac59ac5 Michael Hanselmann
                                                       convert_whitespace=1)
199 5f43f393 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
200 5f43f393 Michael Hanselmann
201 5f43f393 Michael Hanselmann
    return []
202 5f43f393 Michael Hanselmann
203 5f43f393 Michael Hanselmann
204 b6d02fa9 Iustin Pop
class OpcodeResult(s_compat.Directive):
205 f96c51a0 Michael Hanselmann
  """Custom directive for opcode result.
206 f96c51a0 Michael Hanselmann

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

209 f96c51a0 Michael Hanselmann
  """
210 f96c51a0 Michael Hanselmann
  has_content = False
211 f96c51a0 Michael Hanselmann
  required_arguments = 1
212 f96c51a0 Michael Hanselmann
  optional_arguments = 0
213 f96c51a0 Michael Hanselmann
  final_argument_whitespace = False
214 f96c51a0 Michael Hanselmann
215 f96c51a0 Michael Hanselmann
  def run(self):
216 f96c51a0 Michael Hanselmann
    op_id = self.arguments[0]
217 f96c51a0 Michael Hanselmann
218 f96c51a0 Michael Hanselmann
    tab_width = 2
219 f96c51a0 Michael Hanselmann
    path = op_id
220 f96c51a0 Michael Hanselmann
    include_text = _BuildOpcodeResult(op_id)
221 f96c51a0 Michael Hanselmann
222 f96c51a0 Michael Hanselmann
    # Inject into state machine
223 f96c51a0 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
224 f96c51a0 Michael Hanselmann
                                                       convert_whitespace=1)
225 f96c51a0 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
226 f96c51a0 Michael Hanselmann
227 f96c51a0 Michael Hanselmann
    return []
228 f96c51a0 Michael Hanselmann
229 f96c51a0 Michael Hanselmann
230 685d3b42 Michael Hanselmann
def PythonEvalRole(role, rawtext, text, lineno, inliner,
231 685d3b42 Michael Hanselmann
                   options={}, content=[]):
232 685d3b42 Michael Hanselmann
  """Custom role to evaluate Python expressions.
233 685d3b42 Michael Hanselmann

234 685d3b42 Michael Hanselmann
  The expression's result is included as a literal.
235 685d3b42 Michael Hanselmann

236 685d3b42 Michael Hanselmann
  """
237 b459a848 Andrea Spadaccini
  # pylint: disable=W0102,W0613,W0142
238 1aa50158 Michael Hanselmann
  # W0102: Dangerous default value as argument
239 1aa50158 Michael Hanselmann
  # W0142: Used * or ** magic
240 1aa50158 Michael Hanselmann
  # W0613: Unused argument
241 1aa50158 Michael Hanselmann
242 685d3b42 Michael Hanselmann
  code = docutils.utils.unescape(text, restore_backslashes=True)
243 685d3b42 Michael Hanselmann
244 685d3b42 Michael Hanselmann
  try:
245 685d3b42 Michael Hanselmann
    result = eval(code, EVAL_NS)
246 b459a848 Andrea Spadaccini
  except Exception, err: # pylint: disable=W0703
247 685d3b42 Michael Hanselmann
    msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
248 685d3b42 Michael Hanselmann
                                 line=lineno)
249 685d3b42 Michael Hanselmann
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
250 685d3b42 Michael Hanselmann
251 685d3b42 Michael Hanselmann
  node = docutils.nodes.literal("", unicode(result), **options)
252 685d3b42 Michael Hanselmann
253 685d3b42 Michael Hanselmann
  return ([node], [])
254 685d3b42 Michael Hanselmann
255 685d3b42 Michael Hanselmann
256 b6d02fa9 Iustin Pop
class PythonAssert(s_compat.Directive):
257 685d3b42 Michael Hanselmann
  """Custom directive for writing assertions.
258 685d3b42 Michael Hanselmann

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

262 685d3b42 Michael Hanselmann
  """
263 685d3b42 Michael Hanselmann
  has_content = True
264 685d3b42 Michael Hanselmann
  required_arguments = 0
265 685d3b42 Michael Hanselmann
  optional_arguments = 0
266 685d3b42 Michael Hanselmann
  final_argument_whitespace = False
267 685d3b42 Michael Hanselmann
268 685d3b42 Michael Hanselmann
  def run(self):
269 cbb86b63 Michael Hanselmann
    # Handle combinations of Sphinx and docutils not providing the wanted method
270 cbb86b63 Michael Hanselmann
    if hasattr(self, "assert_has_content"):
271 cbb86b63 Michael Hanselmann
      self.assert_has_content()
272 cbb86b63 Michael Hanselmann
    else:
273 cbb86b63 Michael Hanselmann
      assert self.content
274 685d3b42 Michael Hanselmann
275 685d3b42 Michael Hanselmann
    code = "\n".join(self.content)
276 685d3b42 Michael Hanselmann
277 685d3b42 Michael Hanselmann
    try:
278 685d3b42 Michael Hanselmann
      result = eval(code, EVAL_NS)
279 685d3b42 Michael Hanselmann
    except Exception, err:
280 685d3b42 Michael Hanselmann
      raise self.error("Failed to evaluate %r: %s" % (code, err))
281 685d3b42 Michael Hanselmann
282 685d3b42 Michael Hanselmann
    if not result:
283 685d3b42 Michael Hanselmann
      raise self.error("Assertion failed: %s" % (code, ))
284 685d3b42 Michael Hanselmann
285 685d3b42 Michael Hanselmann
    return []
286 685d3b42 Michael Hanselmann
287 685d3b42 Michael Hanselmann
288 95eb4188 Michael Hanselmann
def BuildQueryFields(fields):
289 95eb4188 Michael Hanselmann
  """Build query fields documentation.
290 95eb4188 Michael Hanselmann

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

293 95eb4188 Michael Hanselmann
  """
294 12637df5 Andrea Spadaccini
  defs = [(fdef.name, fdef.doc)
295 12637df5 Andrea Spadaccini
           for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
296 12637df5 Andrea Spadaccini
                                                      key=compat.fst)]
297 d505bc48 Iustin Pop
  return BuildValuesDoc(defs)
298 12637df5 Andrea Spadaccini
299 12637df5 Andrea Spadaccini
300 12637df5 Andrea Spadaccini
def BuildValuesDoc(values):
301 12637df5 Andrea Spadaccini
  """Builds documentation for a list of values
302 12637df5 Andrea Spadaccini

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

305 12637df5 Andrea Spadaccini
  """
306 12637df5 Andrea Spadaccini
  for name, doc in values:
307 12637df5 Andrea Spadaccini
    assert len(doc.splitlines()) == 1
308 12637df5 Andrea Spadaccini
    yield "``%s``" % name
309 12637df5 Andrea Spadaccini
    yield "  %s" % doc
310 95eb4188 Michael Hanselmann
311 95eb4188 Michael Hanselmann
312 95eb4188 Michael Hanselmann
# TODO: Implement Sphinx directive for query fields
313 95eb4188 Michael Hanselmann
314 95eb4188 Michael Hanselmann
315 5f43f393 Michael Hanselmann
def setup(app):
316 5f43f393 Michael Hanselmann
  """Sphinx extension callback.
317 5f43f393 Michael Hanselmann

318 5f43f393 Michael Hanselmann
  """
319 5f43f393 Michael Hanselmann
  app.add_directive("opcode_params", OpcodeParams)
320 f96c51a0 Michael Hanselmann
  app.add_directive("opcode_result", OpcodeResult)
321 685d3b42 Michael Hanselmann
  app.add_directive("pyassert", PythonAssert)
322 685d3b42 Michael Hanselmann
  app.add_role("pyeval", PythonEvalRole)