Revision 5f43f393

b/Makefile.am
125 125
	lib/_autoconf.py
126 126

  
127 127
noinst_PYTHON = \
128
	lib/build/__init__.py
128
	lib/build/__init__.py \
129
	lib/build/sphinx_ext.py
129 130

  
130 131
pkgpython_PYTHON = \
131 132
	lib/__init__.py \
......
261 262
$(RUN_IN_TEMPDIR): | $(all_dirfiles)
262 263

  
263 264
doc/html/index.html: $(docrst) $(docpng) doc/conf.py configure.ac \
264
	$(RUN_IN_TEMPDIR)
265
	$(RUN_IN_TEMPDIR) lib/build/sphinx_ext.py lib/opcodes.py lib/opdoc.py
265 266
	@test -n "$(SPHINX)" || \
266 267
	    { echo 'sphinx-build' not found during configure; exit 1; }
267 268
	@mkdir_p@ $(dir $@)
b/doc/conf.py
22 22

  
23 23
# Add any Sphinx extension module names here, as strings. They can be extensions
24 24
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
25
extensions = ['sphinx.ext.todo']
25
extensions = ['sphinx.ext.todo', "ganeti.build.sphinx_ext"]
26 26

  
27 27
# Add any paths that contain templates here, relative to this directory.
28 28
templates_path = ['_templates']
b/lib/build/sphinx_ext.py
1
#
2
#
3

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

  
21

  
22
"""Sphinx extension for building opcode documentation.
23

  
24
"""
25

  
26
import operator
27
from cStringIO import StringIO
28

  
29
from docutils import statemachine
30

  
31
from sphinx.errors import SphinxError
32
from sphinx.util.compat import Directive
33

  
34
from ganeti import utils
35
from ganeti import opcodes
36
from ganeti import ht
37

  
38

  
39
COMMON_PARAM_NAMES = map(operator.itemgetter(0), opcodes.OpCode.OP_PARAMS)
40

  
41

  
42
class OpcodeError(SphinxError):
43
  category = "Opcode error"
44

  
45

  
46
def _SplitOption(text):
47
  """Split simple option list.
48

  
49
  @type text: string
50
  @param text: Options, e.g. "foo, bar, baz"
51

  
52
  """
53
  return [i.strip(",").strip() for i in text.split()]
54

  
55

  
56
def _ParseAlias(text):
57
  """Parse simple assignment option.
58

  
59
  @type text: string
60
  @param text: Assignments, e.g. "foo=bar, hello=world"
61
  @rtype: dict
62

  
63
  """
64
  result = {}
65

  
66
  for part in _SplitOption(text):
67
    if "=" not in part:
68
      raise OpcodeError("Invalid option format, missing equal sign")
69

  
70
    (name, value) = part.split("=", 1)
71

  
72
    result[name.strip()] = value.strip()
73

  
74
  return result
75

  
76

  
77
def _BuildOpcodeParams(op_id, include, exclude, alias):
78
  """Build opcode parameter documentation.
79

  
80
  @type op_id: string
81
  @param op_id: Opcode ID
82

  
83
  """
84
  op_cls = opcodes.OP_MAPPING[op_id]
85

  
86
  params_with_alias = \
87
    utils.NiceSort([(alias.get(name, name), name, default, test, doc)
88
                    for (name, default, test, doc) in op_cls.GetAllParams()],
89
                   key=operator.itemgetter(0))
90

  
91
  for (rapi_name, name, default, test, doc) in params_with_alias:
92
    # Hide common parameters if not explicitely included
93
    if (name in COMMON_PARAM_NAMES and
94
        (not include or name not in include)):
95
      continue
96
    if exclude is not None and name in exclude:
97
      continue
98
    if include is not None and name not in include:
99
      continue
100

  
101
    has_default = default is not ht.NoDefault
102
    has_test = not (test is None or test is ht.NoType)
103

  
104
    buf = StringIO()
105
    buf.write("``%s``" % rapi_name)
106
    if has_default or has_test:
107
      buf.write(" (")
108
      if has_default:
109
        buf.write("defaults to ``%s``" % default)
110
        if has_test:
111
          buf.write(", ")
112
      if has_test:
113
        buf.write("must be ``%s``" % test)
114
      buf.write(")")
115
    yield buf.getvalue()
116

  
117
    # Add text
118
    for line in doc.splitlines():
119
      yield "  %s" % line
120

  
121

  
122
class OpcodeParams(Directive):
123
  """Custom directive for opcode parameters.
124

  
125
  See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
126

  
127
  """
128
  has_content = False
129
  required_arguments = 1
130
  optional_arguments = 0
131
  final_argument_whitespace = False
132
  option_spec = dict(include=_SplitOption, exclude=_SplitOption,
133
                     alias=_ParseAlias)
134

  
135
  def run(self):
136
    op_id = self.arguments[0]
137
    include = self.options.get("include", None)
138
    exclude = self.options.get("exclude", None)
139
    alias = self.options.get("alias", {})
140

  
141
    tab_width = 2
142
    path = op_id
143
    include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
144

  
145
    # Inject into state machine
146
    include_lines = statemachine.string2lines(include_text, tab_width,
147
                                              convert_whitespace=1)
148
    self.state_machine.insert_input(include_lines, path)
149

  
150
    return []
151

  
152

  
153
def setup(app):
154
  """Sphinx extension callback.
155

  
156
  """
157
  app.add_directive("opcode_params", OpcodeParams)

Also available in: Unified diff