Statistics
| Branch: | Tag: | Revision:

root / lib / build / sphinx_ext.py @ e3303a4e

History | View | Annotate | Download (6.3 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
import operator
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 5f43f393 Michael Hanselmann
33 dac59ac5 Michael Hanselmann
import sphinx.errors
34 dac59ac5 Michael Hanselmann
import sphinx.util.compat
35 5f43f393 Michael Hanselmann
36 685d3b42 Michael Hanselmann
from ganeti import constants
37 685d3b42 Michael Hanselmann
from ganeti import compat
38 685d3b42 Michael Hanselmann
from ganeti import errors
39 5f43f393 Michael Hanselmann
from ganeti import utils
40 5f43f393 Michael Hanselmann
from ganeti import opcodes
41 5f43f393 Michael Hanselmann
from ganeti import ht
42 5f43f393 Michael Hanselmann
43 5f43f393 Michael Hanselmann
44 5f43f393 Michael Hanselmann
COMMON_PARAM_NAMES = map(operator.itemgetter(0), opcodes.OpCode.OP_PARAMS)
45 5f43f393 Michael Hanselmann
46 685d3b42 Michael Hanselmann
#: Namespace for evaluating expressions
47 685d3b42 Michael Hanselmann
EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors)
48 685d3b42 Michael Hanselmann
49 5f43f393 Michael Hanselmann
50 dac59ac5 Michael Hanselmann
class OpcodeError(sphinx.errors.SphinxError):
51 5f43f393 Michael Hanselmann
  category = "Opcode error"
52 5f43f393 Michael Hanselmann
53 5f43f393 Michael Hanselmann
54 5f43f393 Michael Hanselmann
def _SplitOption(text):
55 5f43f393 Michael Hanselmann
  """Split simple option list.
56 5f43f393 Michael Hanselmann

57 5f43f393 Michael Hanselmann
  @type text: string
58 5f43f393 Michael Hanselmann
  @param text: Options, e.g. "foo, bar, baz"
59 5f43f393 Michael Hanselmann

60 5f43f393 Michael Hanselmann
  """
61 5f43f393 Michael Hanselmann
  return [i.strip(",").strip() for i in text.split()]
62 5f43f393 Michael Hanselmann
63 5f43f393 Michael Hanselmann
64 5f43f393 Michael Hanselmann
def _ParseAlias(text):
65 5f43f393 Michael Hanselmann
  """Parse simple assignment option.
66 5f43f393 Michael Hanselmann

67 5f43f393 Michael Hanselmann
  @type text: string
68 5f43f393 Michael Hanselmann
  @param text: Assignments, e.g. "foo=bar, hello=world"
69 5f43f393 Michael Hanselmann
  @rtype: dict
70 5f43f393 Michael Hanselmann

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

88 5f43f393 Michael Hanselmann
  @type op_id: string
89 5f43f393 Michael Hanselmann
  @param op_id: Opcode ID
90 5f43f393 Michael Hanselmann

91 5f43f393 Michael Hanselmann
  """
92 5f43f393 Michael Hanselmann
  op_cls = opcodes.OP_MAPPING[op_id]
93 5f43f393 Michael Hanselmann
94 5f43f393 Michael Hanselmann
  params_with_alias = \
95 5f43f393 Michael Hanselmann
    utils.NiceSort([(alias.get(name, name), name, default, test, doc)
96 5f43f393 Michael Hanselmann
                    for (name, default, test, doc) in op_cls.GetAllParams()],
97 5f43f393 Michael Hanselmann
                   key=operator.itemgetter(0))
98 5f43f393 Michael Hanselmann
99 5f43f393 Michael Hanselmann
  for (rapi_name, name, default, test, doc) in params_with_alias:
100 5f43f393 Michael Hanselmann
    # Hide common parameters if not explicitely included
101 5f43f393 Michael Hanselmann
    if (name in COMMON_PARAM_NAMES and
102 5f43f393 Michael Hanselmann
        (not include or name not in include)):
103 5f43f393 Michael Hanselmann
      continue
104 5f43f393 Michael Hanselmann
    if exclude is not None and name in exclude:
105 5f43f393 Michael Hanselmann
      continue
106 5f43f393 Michael Hanselmann
    if include is not None and name not in include:
107 5f43f393 Michael Hanselmann
      continue
108 5f43f393 Michael Hanselmann
109 5f43f393 Michael Hanselmann
    has_default = default is not ht.NoDefault
110 5f43f393 Michael Hanselmann
    has_test = not (test is None or test is ht.NoType)
111 5f43f393 Michael Hanselmann
112 5f43f393 Michael Hanselmann
    buf = StringIO()
113 5f43f393 Michael Hanselmann
    buf.write("``%s``" % rapi_name)
114 5f43f393 Michael Hanselmann
    if has_default or has_test:
115 5f43f393 Michael Hanselmann
      buf.write(" (")
116 5f43f393 Michael Hanselmann
      if has_default:
117 5f43f393 Michael Hanselmann
        buf.write("defaults to ``%s``" % default)
118 5f43f393 Michael Hanselmann
        if has_test:
119 5f43f393 Michael Hanselmann
          buf.write(", ")
120 5f43f393 Michael Hanselmann
      if has_test:
121 5f43f393 Michael Hanselmann
        buf.write("must be ``%s``" % test)
122 5f43f393 Michael Hanselmann
      buf.write(")")
123 5f43f393 Michael Hanselmann
    yield buf.getvalue()
124 5f43f393 Michael Hanselmann
125 5f43f393 Michael Hanselmann
    # Add text
126 5f43f393 Michael Hanselmann
    for line in doc.splitlines():
127 5f43f393 Michael Hanselmann
      yield "  %s" % line
128 5f43f393 Michael Hanselmann
129 5f43f393 Michael Hanselmann
130 dac59ac5 Michael Hanselmann
class OpcodeParams(sphinx.util.compat.Directive):
131 5f43f393 Michael Hanselmann
  """Custom directive for opcode parameters.
132 5f43f393 Michael Hanselmann

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

135 5f43f393 Michael Hanselmann
  """
136 5f43f393 Michael Hanselmann
  has_content = False
137 5f43f393 Michael Hanselmann
  required_arguments = 1
138 5f43f393 Michael Hanselmann
  optional_arguments = 0
139 5f43f393 Michael Hanselmann
  final_argument_whitespace = False
140 5f43f393 Michael Hanselmann
  option_spec = dict(include=_SplitOption, exclude=_SplitOption,
141 5f43f393 Michael Hanselmann
                     alias=_ParseAlias)
142 5f43f393 Michael Hanselmann
143 5f43f393 Michael Hanselmann
  def run(self):
144 5f43f393 Michael Hanselmann
    op_id = self.arguments[0]
145 5f43f393 Michael Hanselmann
    include = self.options.get("include", None)
146 5f43f393 Michael Hanselmann
    exclude = self.options.get("exclude", None)
147 5f43f393 Michael Hanselmann
    alias = self.options.get("alias", {})
148 5f43f393 Michael Hanselmann
149 5f43f393 Michael Hanselmann
    tab_width = 2
150 5f43f393 Michael Hanselmann
    path = op_id
151 5f43f393 Michael Hanselmann
    include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
152 5f43f393 Michael Hanselmann
153 5f43f393 Michael Hanselmann
    # Inject into state machine
154 dac59ac5 Michael Hanselmann
    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
155 dac59ac5 Michael Hanselmann
                                                       convert_whitespace=1)
156 5f43f393 Michael Hanselmann
    self.state_machine.insert_input(include_lines, path)
157 5f43f393 Michael Hanselmann
158 5f43f393 Michael Hanselmann
    return []
159 5f43f393 Michael Hanselmann
160 5f43f393 Michael Hanselmann
161 685d3b42 Michael Hanselmann
def PythonEvalRole(role, rawtext, text, lineno, inliner,
162 685d3b42 Michael Hanselmann
                   options={}, content=[]):
163 685d3b42 Michael Hanselmann
  """Custom role to evaluate Python expressions.
164 685d3b42 Michael Hanselmann

165 685d3b42 Michael Hanselmann
  The expression's result is included as a literal.
166 685d3b42 Michael Hanselmann

167 685d3b42 Michael Hanselmann
  """
168 1aa50158 Michael Hanselmann
  # pylint: disable-msg=W0102,W0613,W0142
169 1aa50158 Michael Hanselmann
  # W0102: Dangerous default value as argument
170 1aa50158 Michael Hanselmann
  # W0142: Used * or ** magic
171 1aa50158 Michael Hanselmann
  # W0613: Unused argument
172 1aa50158 Michael Hanselmann
173 685d3b42 Michael Hanselmann
  code = docutils.utils.unescape(text, restore_backslashes=True)
174 685d3b42 Michael Hanselmann
175 685d3b42 Michael Hanselmann
  try:
176 685d3b42 Michael Hanselmann
    result = eval(code, EVAL_NS)
177 1aa50158 Michael Hanselmann
  except Exception, err: # pylint: disable-msg=W0703
178 685d3b42 Michael Hanselmann
    msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
179 685d3b42 Michael Hanselmann
                                 line=lineno)
180 685d3b42 Michael Hanselmann
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
181 685d3b42 Michael Hanselmann
182 685d3b42 Michael Hanselmann
  node = docutils.nodes.literal("", unicode(result), **options)
183 685d3b42 Michael Hanselmann
184 685d3b42 Michael Hanselmann
  return ([node], [])
185 685d3b42 Michael Hanselmann
186 685d3b42 Michael Hanselmann
187 685d3b42 Michael Hanselmann
class PythonAssert(sphinx.util.compat.Directive):
188 685d3b42 Michael Hanselmann
  """Custom directive for writing assertions.
189 685d3b42 Michael Hanselmann

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

193 685d3b42 Michael Hanselmann
  """
194 685d3b42 Michael Hanselmann
  has_content = True
195 685d3b42 Michael Hanselmann
  required_arguments = 0
196 685d3b42 Michael Hanselmann
  optional_arguments = 0
197 685d3b42 Michael Hanselmann
  final_argument_whitespace = False
198 685d3b42 Michael Hanselmann
199 685d3b42 Michael Hanselmann
  def run(self):
200 cbb86b63 Michael Hanselmann
    # Handle combinations of Sphinx and docutils not providing the wanted method
201 cbb86b63 Michael Hanselmann
    if hasattr(self, "assert_has_content"):
202 cbb86b63 Michael Hanselmann
      self.assert_has_content()
203 cbb86b63 Michael Hanselmann
    else:
204 cbb86b63 Michael Hanselmann
      assert self.content
205 685d3b42 Michael Hanselmann
206 685d3b42 Michael Hanselmann
    code = "\n".join(self.content)
207 685d3b42 Michael Hanselmann
208 685d3b42 Michael Hanselmann
    try:
209 685d3b42 Michael Hanselmann
      result = eval(code, EVAL_NS)
210 685d3b42 Michael Hanselmann
    except Exception, err:
211 685d3b42 Michael Hanselmann
      raise self.error("Failed to evaluate %r: %s" % (code, err))
212 685d3b42 Michael Hanselmann
213 685d3b42 Michael Hanselmann
    if not result:
214 685d3b42 Michael Hanselmann
      raise self.error("Assertion failed: %s" % (code, ))
215 685d3b42 Michael Hanselmann
216 685d3b42 Michael Hanselmann
    return []
217 685d3b42 Michael Hanselmann
218 685d3b42 Michael Hanselmann
219 95eb4188 Michael Hanselmann
def BuildQueryFields(fields):
220 95eb4188 Michael Hanselmann
  """Build query fields documentation.
221 95eb4188 Michael Hanselmann

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

224 95eb4188 Michael Hanselmann
  """
225 111bf531 Michael Hanselmann
  for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
226 111bf531 Michael Hanselmann
                                             key=operator.itemgetter(0)):
227 95eb4188 Michael Hanselmann
    assert len(fdef.doc.splitlines()) == 1
228 95eb4188 Michael Hanselmann
    yield "``%s``" % fdef.name
229 95eb4188 Michael Hanselmann
    yield "  %s" % fdef.doc
230 95eb4188 Michael Hanselmann
231 95eb4188 Michael Hanselmann
232 95eb4188 Michael Hanselmann
# TODO: Implement Sphinx directive for query fields
233 95eb4188 Michael Hanselmann
234 95eb4188 Michael Hanselmann
235 5f43f393 Michael Hanselmann
def setup(app):
236 5f43f393 Michael Hanselmann
  """Sphinx extension callback.
237 5f43f393 Michael Hanselmann

238 5f43f393 Michael Hanselmann
  """
239 5f43f393 Michael Hanselmann
  app.add_directive("opcode_params", OpcodeParams)
240 685d3b42 Michael Hanselmann
  app.add_directive("pyassert", PythonAssert)
241 685d3b42 Michael Hanselmann
  app.add_role("pyeval", PythonEvalRole)