Statistics
| Branch: | Tag: | Revision:

root / lib / build / sphinx_ext.py @ 0602cef3

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 12e0ee0d Michael Hanselmann
from ganeti import luxi
45 b4fcee5b Michael Hanselmann
46 b459a848 Andrea Spadaccini
import ganeti.rapi.rlib2 # pylint: disable=W0611
47 5f43f393 Michael Hanselmann
48 5f43f393 Michael Hanselmann
49 a19a6326 Michael Hanselmann
def _GetCommonParamNames():
50 a19a6326 Michael Hanselmann
  """Builds a list of parameters common to all opcodes.
51 a19a6326 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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