rally-openstack/doc/ext/plugin_reference.py
Andrey Kurilin 648e4698ed [docs] Improve plugins and cli references
* re-use ``make_definition`` function for displaying parameters of plugins.
  Profit: unified view; references to each argument of each plugin.
* Use list in ``make_definition`` function
* fix indention of plugins parameters descriptions

Change-Id: I0b7e567134d36abde313d5538eab99b282f60178
2017-02-02 14:43:07 +00:00

164 lines
6.1 KiB
Python

# Copyright 2015: Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from docutils.parsers import rst
import re
from rally.common.plugin import discover
from rally.common.plugin import plugin
from rally import plugins
from utils import category, subcategory, section, paragraph, parse_text, \
make_definition
CATEGORIES = {
"Common": ["OS Client"],
"Deployment": ["Engine", "Provider Factory"],
"Task Component": ["Chart", "Context", "Exporter", "Hook",
"Resource Type", "SLA", "Scenario", "Scenario Runner",
"Trigger"],
"Verification Component": ["Verifier Context", "Verification Reporter",
"Verifier Manager"]
}
# NOTE(andreykurilin): several bases do not have docstings at all, so it is
# redundant to display them
IGNORED_BASES = ["Resource Type", "Task Exporter", "OS Client"]
class PluginsReferenceDirective(rst.Directive):
optional_arguments = 1
option_spec = {"base_cls": str}
@staticmethod
def _make_pretty_parameters(parameters, ref_prefix):
if not parameters:
return []
results = [paragraph("**Parameters**:")]
for p in parameters:
pname = p["name"]
ref = ("%s%s" % (ref_prefix, pname)).lower().replace(".", "-")
if "type" in p:
pname += " (%s)" % p["type"]
pdoc = "\n ".join(p["doc"].split("\n"))
results.extend(make_definition(pname, ref, [pdoc]))
return results
def _make_plugin_section(self, plugin_cls, base_name=None):
section_name = plugin_cls.get_name()
if base_name:
section_name += " [%s]" % base_name
section_obj = section(section_name)
info = plugin_cls.get_info()
if info["title"]:
section_obj.append(paragraph(info["title"]))
if info["description"]:
section_obj.extend(parse_text(info["description"]))
if info["namespace"]:
section_obj.append(paragraph(
"**Namespace**: %s" % info["namespace"]))
if info["parameters"]:
if base_name:
ref_prefix = "%s-%s-" % (base_name, plugin_cls.get_name())
else:
ref_prefix = "%s-" % plugin_cls.get_name()
section_obj.extend(self._make_pretty_parameters(info["parameters"],
ref_prefix))
if info["returns"]:
section_obj.extend(parse_text(
"**Returns**:\n%s" % info["returns"]))
filename = info["module"].replace(".", "/")
ref = "https://github.com/openstack/rally/blob/master/%s.py" % filename
section_obj.extend(parse_text("**Module**:\n`%s`__\n\n__ %s"
% (info["module"], ref)))
return section_obj
def _make_plugin_base_section(self, base_cls, base_name=None):
if base_name:
title = ("%ss" % base_name if base_name[-1] != "y"
else "%sies" % base_name[:-1])
subcategory_obj = subcategory(title)
else:
subcategory_obj = []
for p in sorted(base_cls.get_all(), key=lambda o: o.get_name()):
subcategory_obj.append(self._make_plugin_section(p, base_name))
return subcategory_obj
@staticmethod
def _parse_class_name(cls):
name = ""
for word in re.split(r'([A-Z][a-z]*)', cls.__name__):
if word:
if len(word) > 1 and name:
name += " "
name += word
return name
def _get_all_plugins_bases(self):
"""Return grouped and sorted all plugins bases."""
bases = []
bases_names = []
for p in discover.itersubclasses(plugin.Plugin):
base_ref = getattr(p, "base_ref", None)
if base_ref == p:
name = self._parse_class_name(p)
if name in bases_names:
raise Exception("Two base classes with same name '%s' are "
"detected." % name)
bases_names.append(name)
category_of_base = "Common"
for cname, cbases in CATEGORIES.items():
if name in cbases:
category_of_base = cname
bases.append((category_of_base, name, p))
return sorted(bases)
def run(self):
plugins.load()
bases = self._get_all_plugins_bases()
if "base_cls" in self.options:
for _category_name, base_name, base_cls in bases:
if base_name == self.options["base_cls"]:
return self._make_plugin_base_section(base_cls)
raise Exception("Failed to generate plugins reference for '%s'"
" plugin base." % self.options["base_cls"])
categories = {}
for category_name, base_name, base_cls in bases:
# FIXME(andreykurilin): do not ignore anything
if base_name in IGNORED_BASES:
continue
if category_name not in categories:
categories[category_name] = category(category_name)
category_of_base = categories[category_name]
category_of_base.append(self._make_plugin_base_section(base_cls,
base_name))
return [content for _name, content in sorted(categories.items())]
def setup(app):
plugins.load()
app.add_directive("generate_plugin_reference", PluginsReferenceDirective)