diff options
author | Stephen Finucane <sfinucan@redhat.com> | 2017-04-27 12:37:31 +0100 |
---|---|---|
committer | Stephen Finucane <sfinucan@redhat.com> | 2017-04-27 15:10:25 +0100 |
commit | 2320ee61a3ac369985ae22dbe035a9d451ae680a (patch) | |
tree | 6c15c7089008fdc5a8105e66f98a44c53fc0f7e6 /oslo_policy/sphinxext.py | |
parent | a7ab49e42cf2c6d998cd1e0c8b6f037cc3d71749 (diff) | |
download | oslo-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.py | 157 |
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) |