sphinxext.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. import inspect
  13. from docutils import nodes
  14. from docutils.parsers import rst
  15. from docutils.parsers.rst import directives
  16. from docutils.statemachine import ViewList
  17. from sphinx.util import logging
  18. from sphinx.util.nodes import nested_parse_with_titles
  19. from stevedore import extension
  20. LOG = logging.getLogger(__name__)
  21. def _get_docstring(plugin):
  22. return inspect.getdoc(plugin) or ''
  23. def _simple_list(mgr):
  24. for name in sorted(mgr.names()):
  25. ext = mgr[name]
  26. doc = _get_docstring(ext.plugin) or '\n'
  27. summary = doc.splitlines()[0].strip()
  28. yield('* %s -- %s' % (ext.name, summary),
  29. ext.module_name)
  30. def _detailed_list(mgr, over='', under='-', titlecase=False):
  31. for name in sorted(mgr.names()):
  32. ext = mgr[name]
  33. if over:
  34. yield (over * len(ext.name), ext.module_name)
  35. if titlecase:
  36. yield (ext.name.title(), ext.module_name)
  37. else:
  38. yield (ext.name, ext.module_name)
  39. if under:
  40. yield (under * len(ext.name), ext.module_name)
  41. yield ('\n', ext.module_name)
  42. doc = _get_docstring(ext.plugin)
  43. if doc:
  44. yield (doc, ext.module_name)
  45. else:
  46. yield (
  47. '.. warning:: No documentation found for {} in {}'.format(
  48. ext.name, ext.entry_point_target,
  49. ),
  50. ext.module_name,
  51. )
  52. yield ('\n', ext.module_name)
  53. class ListPluginsDirective(rst.Directive):
  54. """Present a simple list of the plugins in a namespace."""
  55. option_spec = {
  56. 'class': directives.class_option,
  57. 'detailed': directives.flag,
  58. 'titlecase': directives.flag,
  59. 'overline-style': directives.single_char_or_unicode,
  60. 'underline-style': directives.single_char_or_unicode,
  61. }
  62. has_content = True
  63. def run(self):
  64. namespace = ' '.join(self.content).strip()
  65. LOG.info('documenting plugins from %r' % namespace)
  66. overline_style = self.options.get('overline-style', '')
  67. underline_style = self.options.get('underline-style', '=')
  68. def report_load_failure(mgr, ep, err):
  69. LOG.warning(u'Failed to load %s: %s' % (ep.module, err))
  70. mgr = extension.ExtensionManager(
  71. namespace,
  72. on_load_failure_callback=report_load_failure,
  73. )
  74. result = ViewList()
  75. titlecase = 'titlecase' in self.options
  76. if 'detailed' in self.options:
  77. data = _detailed_list(
  78. mgr, over=overline_style, under=underline_style,
  79. titlecase=titlecase)
  80. else:
  81. data = _simple_list(mgr)
  82. for text, source in data:
  83. for line in text.splitlines():
  84. result.append(line, source)
  85. # Parse what we have into a new section.
  86. node = nodes.section()
  87. node.document = self.state.document
  88. nested_parse_with_titles(self.state, result, node)
  89. return node.children
  90. def setup(app):
  91. LOG.info('loading stevedore.sphinxext')
  92. app.add_directive('list-plugins', ListPluginsDirective)
  93. return {
  94. 'parallel_read_safe': True,
  95. 'parallel_write_safe': True,
  96. }