Statistics
| Branch: | Tag: | Revision:

root / lib / build / sphinx_ext.py @ 98dfcaff

History | View | Annotate | Download (7.4 kB)

1 5f43f393 Michael Hanselmann
#
2 5f43f393 Michael Hanselmann
#
3 5f43f393 Michael Hanselmann
4 5f43f393 Michael Hanselmann
# Copyright (C) 2011 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 eb62069e Iustin Pop
COMMON_PARAM_NAMES = map(compat.fst, opcodes.OpCode.OP_PARAMS)
49 5f43f393 Michael Hanselmann
50 685d3b42 Michael Hanselmann
#: Namespace for evaluating expressions
51 b4fcee5b Michael Hanselmann
EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors,
52 b4fcee5b Michael Hanselmann
               rlib2=rapi.rlib2)
53 685d3b42 Michael Hanselmann
54 5f43f393 Michael Hanselmann
55 dac59ac5 Michael Hanselmann
class OpcodeError(sphinx.errors.SphinxError):
56 5f43f393 Michael Hanselmann
  category = "Opcode error"
57 5f43f393 Michael Hanselmann
58 5f43f393 Michael Hanselmann
59 5f43f393 Michael Hanselmann
def _SplitOption(text):
60 5f43f393 Michael Hanselmann
  """Split simple option list.
61 5f43f393 Michael Hanselmann

62 5f43f393 Michael Hanselmann
  @type text: string
63 5f43f393 Michael Hanselmann
  @param text: Options, e.g. "foo, bar, baz"
64 5f43f393 Michael Hanselmann

65 5f43f393 Michael Hanselmann
  """
66 5f43f393 Michael Hanselmann
  return [i.strip(",").strip() for i in text.split()]
67 5f43f393 Michael Hanselmann
68 5f43f393 Michael Hanselmann
69 5f43f393 Michael Hanselmann
def _ParseAlias(text):
70 5f43f393 Michael Hanselmann
  """Parse simple assignment option.
71 5f43f393 Michael Hanselmann

72 5f43f393 Michael Hanselmann
  @type text: string
73 5f43f393 Michael Hanselmann
  @param text: Assignments, e.g. "foo=bar, hello=world"
74 5f43f393 Michael Hanselmann
  @rtype: dict
75 5f43f393 Michael Hanselmann

76 5f43f393 Michael Hanselmann
  """
77 5f43f393 Michael Hanselmann
  result = {}
78 5f43f393 Michael Hanselmann
79 5f43f393 Michael Hanselmann
  for part in _SplitOption(text):
80 5f43f393 Michael Hanselmann
    if "=" not in part:
81 5f43f393 Michael Hanselmann
      raise OpcodeError("Invalid option format, missing equal sign")
82 5f43f393 Michael Hanselmann
83 5f43f393 Michael Hanselmann
    (name, value) = part.split("=", 1)
84 5f43f393 Michael Hanselmann
85 5f43f393 Michael Hanselmann
    result[name.strip()] = value.strip()
86 5f43f393 Michael Hanselmann
87 5f43f393 Michael Hanselmann
  return result
88 5f43f393 Michael Hanselmann
89 5f43f393 Michael Hanselmann
90 5f43f393 Michael Hanselmann
def _BuildOpcodeParams(op_id, include, exclude, alias):
91 5f43f393 Michael Hanselmann
  """Build opcode parameter documentation.
92 5f43f393 Michael Hanselmann

93 5f43f393 Michael Hanselmann
  @type op_id: string
94 5f43f393 Michael Hanselmann
  @param op_id: Opcode ID
95 5f43f393 Michael Hanselmann

96 5f43f393 Michael Hanselmann
  """
97 5f43f393 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
98 5f43f393 Michael Hanselmann
99 5f43f393 Michael Hanselmann
  params_with_alias = \
100 5f43f393 Michael Hanselmann
    utils.NiceSort([(alias.get(name, name), name, default, test, doc)
101 5f43f393 Michael Hanselmann
                    for (name, default, test, doc) in op_cls.GetAllParams()],
102 eb62069e Iustin Pop
                   key=compat.fst)
103 5f43f393 Michael Hanselmann
104 5f43f393 Michael Hanselmann
  for (rapi_name, name, default, test, doc) in params_with_alias:
105 5f43f393 Michael Hanselmann
    # Hide common parameters if not explicitely included
106 5f43f393 Michael Hanselmann
    if (name in COMMON_PARAM_NAMES and
107 5f43f393 Michael Hanselmann
        (not include or name not in include)):
108 5f43f393 Michael Hanselmann
      continue
109 5f43f393 Michael Hanselmann
    if exclude is not None and name in exclude:
110 5f43f393 Michael Hanselmann
      continue
111 5f43f393 Michael Hanselmann
    if include is not None and name not in include:
112 5f43f393 Michael Hanselmann
      continue
113 5f43f393 Michael Hanselmann
114 5f43f393 Michael Hanselmann
    has_default = default is not ht.NoDefault
115 5f43f393 Michael Hanselmann
    has_test = not (test is None or test is ht.NoType)
116 5f43f393 Michael Hanselmann
117 5f43f393 Michael Hanselmann
    buf = StringIO()
118 5f43f393 Michael Hanselmann
    buf.write("``%s``" % rapi_name)
119 5f43f393 Michael Hanselmann
    if has_default or has_test:
120 5f43f393 Michael Hanselmann
      buf.write(" (")
121 5f43f393 Michael Hanselmann
      if has_default:
122 5f43f393 Michael Hanselmann
        buf.write("defaults to ``%s``" % default)
123 5f43f393 Michael Hanselmann
        if has_test:
124 5f43f393 Michael Hanselmann
          buf.write(", ")
125 5f43f393 Michael Hanselmann
      if has_test:
126 5f43f393 Michael Hanselmann
        buf.write("must be ``%s``" % test)
127 5f43f393 Michael Hanselmann
      buf.write(")")
128 5f43f393 Michael Hanselmann
    yield buf.getvalue()
129 5f43f393 Michael Hanselmann
130 5f43f393 Michael Hanselmann
    # Add text
131 5f43f393 Michael Hanselmann
    for line in doc.splitlines():
132 5f43f393 Michael Hanselmann
      yield "  %s" % line
133 5f43f393 Michael Hanselmann
134 5f43f393 Michael Hanselmann
135 f96c51a0 Michael Hanselmann
def _BuildOpcodeResult(op_id):
136 f96c51a0 Michael Hanselmann
  """Build opcode result documentation.
137 f96c51a0 Michael Hanselmann

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

141 f96c51a0 Michael Hanselmann
  """
142 f96c51a0 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
143 f96c51a0 Michael Hanselmann
144 f96c51a0 Michael Hanselmann
  result_fn = getattr(op_cls, "OP_RESULT", None)
145 f96c51a0 Michael Hanselmann
146 f96c51a0 Michael Hanselmann
  if not result_fn:
147 f96c51a0 Michael Hanselmann
    raise OpcodeError("Opcode '%s' has no result description" % op_id)
148 f96c51a0 Michael Hanselmann
149 f96c51a0 Michael Hanselmann
  return "``%s``" % result_fn
150 f96c51a0 Michael Hanselmann
151 f96c51a0 Michael Hanselmann
152 b6d02fa9 Iustin Pop
class OpcodeParams(s_compat.Directive):
153 5f43f393 Michael Hanselmann
  """Custom directive for opcode parameters.
154 5f43f393 Michael Hanselmann

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

157 5f43f393 Michael Hanselmann
  """
158 5f43f393 Michael Hanselmann
  has_content = False
159 5f43f393 Michael Hanselmann
  required_arguments = 1
160 5f43f393 Michael Hanselmann
  optional_arguments = 0
161 5f43f393 Michael Hanselmann
  final_argument_whitespace = False
162 5f43f393 Michael Hanselmann
  option_spec = dict(include=_SplitOption, exclude=_SplitOption,
163 5f43f393 Michael Hanselmann
                     alias=_ParseAlias)
164 5f43f393 Michael Hanselmann
165 5f43f393 Michael Hanselmann
  def run(self):
166 5f43f393 Michael Hanselmann
    op_id = self.arguments[0]
167 5f43f393 Michael Hanselmann
    include = self.options.get("include", None)
168 5f43f393 Michael Hanselmann
    exclude = self.options.get("exclude", None)
169 5f43f393 Michael Hanselmann
    alias = self.options.get("alias", {})
170 5f43f393 Michael Hanselmann
171 5f43f393 Michael Hanselmann
    tab_width = 2
172 5f43f393 Michael Hanselmann
    path = op_id
173 5f43f393 Michael Hanselmann
    include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
174 5f43f393 Michael Hanselmann
175 5f43f393 Michael Hanselmann
    # Inject into state machine
176 dac59ac5 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
177 dac59ac5 Michael Hanselmann
                                                       convert_whitespace=1)
178 5f43f393 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
179 5f43f393 Michael Hanselmann
180 5f43f393 Michael Hanselmann
    return []
181 5f43f393 Michael Hanselmann
182 5f43f393 Michael Hanselmann
183 b6d02fa9 Iustin Pop
class OpcodeResult(s_compat.Directive):
184 f96c51a0 Michael Hanselmann
  """Custom directive for opcode result.
185 f96c51a0 Michael Hanselmann

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

188 f96c51a0 Michael Hanselmann
  """
189 f96c51a0 Michael Hanselmann
  has_content = False
190 f96c51a0 Michael Hanselmann
  required_arguments = 1
191 f96c51a0 Michael Hanselmann
  optional_arguments = 0
192 f96c51a0 Michael Hanselmann
  final_argument_whitespace = False
193 f96c51a0 Michael Hanselmann
194 f96c51a0 Michael Hanselmann
  def run(self):
195 f96c51a0 Michael Hanselmann
    op_id = self.arguments[0]
196 f96c51a0 Michael Hanselmann
197 f96c51a0 Michael Hanselmann
    tab_width = 2
198 f96c51a0 Michael Hanselmann
    path = op_id
199 f96c51a0 Michael Hanselmann
    include_text = _BuildOpcodeResult(op_id)
200 f96c51a0 Michael Hanselmann
201 f96c51a0 Michael Hanselmann
    # Inject into state machine
202 f96c51a0 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
203 f96c51a0 Michael Hanselmann
                                                       convert_whitespace=1)
204 f96c51a0 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
205 f96c51a0 Michael Hanselmann
206 f96c51a0 Michael Hanselmann
    return []
207 f96c51a0 Michael Hanselmann
208 f96c51a0 Michael Hanselmann
209 685d3b42 Michael Hanselmann
def PythonEvalRole(role, rawtext, text, lineno, inliner,
210 685d3b42 Michael Hanselmann
                   options={}, content=[]):
211 685d3b42 Michael Hanselmann
  """Custom role to evaluate Python expressions.
212 685d3b42 Michael Hanselmann

213 685d3b42 Michael Hanselmann
  The expression's result is included as a literal.
214 685d3b42 Michael Hanselmann

215 685d3b42 Michael Hanselmann
  """
216 b459a848 Andrea Spadaccini
  # pylint: disable=W0102,W0613,W0142
217 1aa50158 Michael Hanselmann
  # W0102: Dangerous default value as argument
218 1aa50158 Michael Hanselmann
  # W0142: Used * or ** magic
219 1aa50158 Michael Hanselmann
  # W0613: Unused argument
220 1aa50158 Michael Hanselmann
221 685d3b42 Michael Hanselmann
  code = docutils.utils.unescape(text, restore_backslashes=True)
222 685d3b42 Michael Hanselmann
223 685d3b42 Michael Hanselmann
  try:
224 685d3b42 Michael Hanselmann
    result = eval(code, EVAL_NS)
225 b459a848 Andrea Spadaccini
  except Exception, err: # pylint: disable=W0703
226 685d3b42 Michael Hanselmann
    msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
227 685d3b42 Michael Hanselmann
                                 line=lineno)
228 685d3b42 Michael Hanselmann
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
229 685d3b42 Michael Hanselmann
230 685d3b42 Michael Hanselmann
  node = docutils.nodes.literal("", unicode(result), **options)
231 685d3b42 Michael Hanselmann
232 685d3b42 Michael Hanselmann
  return ([node], [])
233 685d3b42 Michael Hanselmann
234 685d3b42 Michael Hanselmann
235 b6d02fa9 Iustin Pop
class PythonAssert(s_compat.Directive):
236 685d3b42 Michael Hanselmann
  """Custom directive for writing assertions.
237 685d3b42 Michael Hanselmann

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

241 685d3b42 Michael Hanselmann
  """
242 685d3b42 Michael Hanselmann
  has_content = True
243 685d3b42 Michael Hanselmann
  required_arguments = 0
244 685d3b42 Michael Hanselmann
  optional_arguments = 0
245 685d3b42 Michael Hanselmann
  final_argument_whitespace = False
246 685d3b42 Michael Hanselmann
247 685d3b42 Michael Hanselmann
  def run(self):
248 cbb86b63 Michael Hanselmann
    # Handle combinations of Sphinx and docutils not providing the wanted method
249 cbb86b63 Michael Hanselmann
    if hasattr(self, "assert_has_content"):
250 cbb86b63 Michael Hanselmann
      self.assert_has_content()
251 cbb86b63 Michael Hanselmann
    else:
252 cbb86b63 Michael Hanselmann
      assert self.content
253 685d3b42 Michael Hanselmann
254 685d3b42 Michael Hanselmann
    code = "\n".join(self.content)
255 685d3b42 Michael Hanselmann
256 685d3b42 Michael Hanselmann
    try:
257 685d3b42 Michael Hanselmann
      result = eval(code, EVAL_NS)
258 685d3b42 Michael Hanselmann
    except Exception, err:
259 685d3b42 Michael Hanselmann
      raise self.error("Failed to evaluate %r: %s" % (code, err))
260 685d3b42 Michael Hanselmann
261 685d3b42 Michael Hanselmann
    if not result:
262 685d3b42 Michael Hanselmann
      raise self.error("Assertion failed: %s" % (code, ))
263 685d3b42 Michael Hanselmann
264 685d3b42 Michael Hanselmann
    return []
265 685d3b42 Michael Hanselmann
266 685d3b42 Michael Hanselmann
267 95eb4188 Michael Hanselmann
def BuildQueryFields(fields):
268 95eb4188 Michael Hanselmann
  """Build query fields documentation.
269 95eb4188 Michael Hanselmann

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

272 95eb4188 Michael Hanselmann
  """
273 111bf531 Michael Hanselmann
  for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
274 eb62069e Iustin Pop
                                             key=compat.fst):
275 95eb4188 Michael Hanselmann
    assert len(fdef.doc.splitlines()) == 1
276 95eb4188 Michael Hanselmann
    yield "``%s``" % fdef.name
277 95eb4188 Michael Hanselmann
    yield "  %s" % fdef.doc
278 95eb4188 Michael Hanselmann
279 95eb4188 Michael Hanselmann
280 95eb4188 Michael Hanselmann
# TODO: Implement Sphinx directive for query fields
281 95eb4188 Michael Hanselmann
282 95eb4188 Michael Hanselmann
283 5f43f393 Michael Hanselmann
def setup(app):
284 5f43f393 Michael Hanselmann
  """Sphinx extension callback.
285 5f43f393 Michael Hanselmann

286 5f43f393 Michael Hanselmann
  """
287 5f43f393 Michael Hanselmann
  app.add_directive("opcode_params", OpcodeParams)
288 f96c51a0 Michael Hanselmann
  app.add_directive("opcode_result", OpcodeResult)
289 685d3b42 Michael Hanselmann
  app.add_directive("pyassert", PythonAssert)
290 685d3b42 Michael Hanselmann
  app.add_role("pyeval", PythonEvalRole)