summaryrefslogtreecommitdiff
path: root/oslo_policy/sphinxext.py
diff options
context:
space:
mode:
authorStephen Finucane <sfinucan@redhat.com>2017-04-27 12:37:31 +0100
committerStephen Finucane <sfinucan@redhat.com>2017-04-27 15:10:25 +0100
commit2320ee61a3ac369985ae22dbe035a9d451ae680a (patch)
tree6c15c7089008fdc5a8105e66f98a44c53fc0f7e6 /oslo_policy/sphinxext.py
parenta7ab49e42cf2c6d998cd1e0c8b6f037cc3d71749 (diff)
downloadoslo-policy-2320ee61a3ac369985ae22dbe035a9d451ae680a.tar.gz
Add Sphinx extension to pretty-print modules
This will provide a more parsable sample policy file for us in docs. This compliments the 'sphinxpolicygen' module. Change-Id: Id99b21e7c0a66aaf4223e60074626914235e0ca5
Diffstat (limited to 'oslo_policy/sphinxext.py')
-rw-r--r--oslo_policy/sphinxext.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/oslo_policy/sphinxext.py b/oslo_policy/sphinxext.py
new file mode 100644
index 0000000..36ae334
--- /dev/null
+++ b/oslo_policy/sphinxext.py
@@ -0,0 +1,157 @@
+# Copyright 2017 Red Hat, Inc.
+#
+# 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.
+
+"""Sphinx extension for pretty-formatting policy docs."""
+
+import os
+
+from docutils import nodes
+from docutils.parsers import rst
+from docutils.parsers.rst import directives
+from docutils import statemachine
+from oslo_config import cfg
+from sphinx.util.nodes import nested_parse_with_titles
+
+from oslo_policy import generator
+
+
+def _indent(text):
+ """Indent by four spaces."""
+ prefix = ' ' * 4
+
+ def prefixed_lines():
+ for line in text.splitlines(True):
+ yield (prefix + line if line.strip() else line)
+
+ return ''.join(prefixed_lines())
+
+
+def _format_policy_rule(rule):
+ """Output a definition list-style rule.
+
+ For example:
+
+ ``os_compute_api:servers:create``
+
+ Create a server
+
+ Default::
+
+ rule:admin_or_owner
+
+ Operations:
+
+ - **POST** ``/servers``
+ """
+ yield '``{}``'.format(rule.name)
+ yield ''
+
+ if rule.description:
+ for line in statemachine.string2lines(
+ rule.description, tab_width=4, convert_whitespace=True):
+ yield _indent(line)
+
+ yield ''
+
+ yield _indent('Default::')
+ yield ''
+ yield _indent(_indent(rule.check_str))
+
+ if hasattr(rule, 'operations'):
+ yield ''
+ yield _indent('Operations:')
+ yield ''
+ for operation in rule.operations:
+ yield _indent('- **{}** ``{}``'.format(operation['method'],
+ operation['path']))
+
+ yield ''
+
+
+def _format_policy_section(section, rules):
+ # The nested_parse_with_titles will ensure the correct header leve is used.
+ yield section
+ yield '=' * len(section)
+ yield ''
+
+ for rule in rules:
+ for line in _format_policy_rule(rule):
+ yield line
+
+
+def _format_policy(namespaces):
+ policies = generator.get_policies_dict(namespaces)
+
+ for section in sorted(policies.keys()):
+ for line in _format_policy_section(section, policies[section]):
+ yield line
+
+
+class ShowPolicyDirective(rst.Directive):
+
+ has_content = False
+ option_spec = {
+ 'config-file': directives.unchanged,
+ }
+
+ def run(self):
+ env = self.state.document.settings.env
+ app = env.app
+
+ config_file = self.options.get('config-file')
+
+ # if the config_file option was not defined, attempt to reuse the
+ # 'oslo_policy.sphinxpolicygen' extension's setting
+ if not config_file and hasattr(env.config,
+ 'policy_generator_config_file'):
+ config_file = env.config.policy_generator_config_file
+
+ # If we are given a file that isn't an absolute path, look for it
+ # in the source directory if it doesn't exist.
+ candidates = [
+ config_file,
+ os.path.join(app.srcdir, config_file,),
+ ]
+ for c in candidates:
+ if os.path.isfile(c):
+ config_path = c
+ break
+ else:
+ self.error('could not find config file in: %s' % str(candidates))
+
+ self.info('loading config file %s' % config_path)
+
+ conf = cfg.ConfigOpts()
+ opts = generator.GENERATOR_OPTS + generator.RULE_OPTS
+ conf.register_cli_opts(opts)
+ conf.register_opts(opts)
+ conf(
+ args=['--config-file', config_path],
+ )
+ namespaces = conf.namespace[:]
+
+ result = statemachine.ViewList()
+ source_name = '<' + __name__ + '>'
+ for line in _format_policy(namespaces):
+ result.append(line, source_name)
+
+ node = nodes.section()
+ node.document = self.state.document
+ nested_parse_with_titles(self.state, result, node)
+
+ return node.children
+
+
+def setup(app):
+ app.add_directive('show-policy', ShowPolicyDirective)