-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ Contents: | |
patterns_enabling | ||
tutorial/index | ||
managers | ||
sphinxext | ||
install | ||
essays/* | ||
history | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
==================== | ||
Sphinx Integration | ||
==================== | ||
|
||
Stevedore includes an extension for integrating with Sphinx to | ||
automatically produce documentation about the supported plugins. To | ||
activate the plugin add ``stevedore.sphinxext`` to the list of | ||
extensions in your ``conf.py``. | ||
|
||
.. rst:directive:: .. list-plugins:: namespace | ||
List the plugins in a namespace. | ||
|
||
Options: | ||
|
||
``detailed`` | ||
Flag to switch between simple and detailed output (see | ||
below). | ||
``overline-style`` | ||
Character to use to draw line above header, | ||
defaults to none. | ||
``underline-style`` | ||
Character to use to draw line below header, | ||
defaults to ``=``. | ||
|
||
Simple List | ||
=========== | ||
|
||
By default, the ``list-plugins`` directive produces a simple list of | ||
plugins in a given namespace including the name and the first line of | ||
the docstring. For example: | ||
|
||
:: | ||
|
||
.. list-plugins:: stevedore.example.formatter | ||
|
||
produces | ||
|
||
------ | ||
|
||
.. list-plugins:: stevedore.example.formatter | ||
|
||
------ | ||
|
||
Detailed Lists | ||
============== | ||
|
||
Adding the ``detailed`` flag to the directive causes the output to | ||
include a separate subsection for each plugin, with the full docstring | ||
rendered. The section heading level can be controlled using the | ||
``underline-style`` and ``overline-style`` options to fit the results | ||
into the structure of your existing document. | ||
|
||
:: | ||
|
||
.. list-plugins:: stevedore.example.formatter | ||
:detailed: | ||
|
||
produces | ||
|
||
------ | ||
|
||
.. list-plugins:: stevedore.example.formatter | ||
:detailed: | ||
:underline-style: - | ||
|
||
------ | ||
|
||
.. note:: | ||
|
||
Depending on how Sphinx is configured, bad reStructuredText syntax in | ||
the docstrings of the plugins may cause the documentation build to | ||
fail completely when detailed mode is enabled. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# 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 __future__ import unicode_literals | ||
|
||
import inspect | ||
|
||
from docutils import nodes | ||
from docutils.parsers import rst | ||
from docutils.parsers.rst import directives | ||
from docutils.statemachine import ViewList | ||
from sphinx.util.nodes import nested_parse_with_titles | ||
|
||
from stevedore import extension | ||
|
||
|
||
def _get_docstring(plugin): | ||
return inspect.getdoc(plugin) or '' | ||
|
||
|
||
def _simple_list(mgr): | ||
for name in sorted(mgr.names()): | ||
ext = mgr[name] | ||
doc = _get_docstring(ext.plugin) or '\n' | ||
summary = doc.splitlines()[0].strip() | ||
yield('* %s -- %s' % (ext.name, summary), | ||
ext.entry_point.module_name) | ||
|
||
|
||
def _detailed_list(mgr, over='', under='-'): | ||
for name in sorted(mgr.names()): | ||
ext = mgr[name] | ||
if over: | ||
yield (over * len(ext.name), ext.entry_point.module_name) | ||
yield (ext.name, ext.entry_point.module_name) | ||
if under: | ||
yield (under * len(ext.name), ext.entry_point.module_name) | ||
yield ('\n', ext.entry_point.module_name) | ||
doc = _get_docstring(ext.plugin) | ||
if doc: | ||
yield (doc, ext.entry_point.module_name) | ||
else: | ||
yield ('.. warning:: No documentation found in %s' | ||
% ext.entry_point, | ||
ext.entry_point.module_name) | ||
yield ('\n', ext.entry_point.module_name) | ||
|
||
|
||
class ListPluginsDirective(rst.Directive): | ||
"""Present a simple list of the plugins in a namespace.""" | ||
|
||
option_spec = { | ||
'class': directives.class_option, | ||
'detailed': directives.flag, | ||
'overline-style': directives.single_char_or_unicode, | ||
'underline-style': directives.single_char_or_unicode, | ||
} | ||
|
||
has_content = True | ||
|
||
def run(self): | ||
env = self.state.document.settings.env | ||
app = env.app | ||
|
||
namespace = ' '.join(self.content).strip() | ||
app.info('documenting plugins from %r' % namespace) | ||
overline_style = self.options.get('overline-style', '') | ||
underline_style = self.options.get('underline-style', '=') | ||
|
||
def report_load_failure(mgr, ep, err): | ||
app.warn(u'Failed to load %s: %s' % (ep.module_name, err)) | ||
|
||
mgr = extension.ExtensionManager( | ||
namespace, | ||
on_load_failure_callback=report_load_failure, | ||
) | ||
|
||
result = ViewList() | ||
|
||
if 'detailed' in self.options: | ||
data = _detailed_list( | ||
mgr, over=overline_style, under=underline_style) | ||
else: | ||
data = _simple_list(mgr) | ||
for text, source in data: | ||
for line in text.splitlines(): | ||
result.append(line, source) | ||
|
||
# Parse what we have into a new section. | ||
node = nodes.section() | ||
node.document = self.state.document | ||
nested_parse_with_titles(self.state, result, node) | ||
|
||
return node.children | ||
|
||
|
||
def setup(app): | ||
app.info('loading stevedore.sphinxext') | ||
app.add_directive('list-plugins', ListPluginsDirective) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# 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. | ||
"""Tests for the sphinx extension | ||
""" | ||
|
||
from __future__ import unicode_literals | ||
|
||
from stevedore import extension | ||
from stevedore import sphinxext | ||
from stevedore.tests import utils | ||
|
||
import mock | ||
import pkg_resources | ||
|
||
|
||
def _make_ext(name, docstring): | ||
def inner(): | ||
pass | ||
|
||
inner.__doc__ = docstring | ||
m1 = mock.Mock(spec=pkg_resources.EntryPoint) | ||
m1.module_name = '%s_module' % name | ||
s = mock.Mock(return_value='ENTRY_POINT(%s)' % name) | ||
m1.__str__ = s | ||
return extension.Extension(name, m1, inner, None) | ||
|
||
|
||
class TestSphinxExt(utils.TestCase): | ||
|
||
def setUp(self): | ||
super(TestSphinxExt, self).setUp() | ||
self.exts = [ | ||
_make_ext('test1', 'One-line docstring'), | ||
_make_ext('test2', 'Multi-line docstring\n\nAnother para'), | ||
] | ||
self.em = extension.ExtensionManager.make_test_instance(self.exts) | ||
|
||
def test_simple_list(self): | ||
results = list(sphinxext._simple_list(self.em)) | ||
self.assertEqual( | ||
[ | ||
('* test1 -- One-line docstring', 'test1_module'), | ||
('* test2 -- Multi-line docstring', 'test2_module'), | ||
], | ||
results, | ||
) | ||
|
||
def test_simple_list_no_docstring(self): | ||
ext = [_make_ext('nodoc', None)] | ||
em = extension.ExtensionManager.make_test_instance(ext) | ||
results = list(sphinxext._simple_list(em)) | ||
self.assertEqual( | ||
[ | ||
('* nodoc -- ', 'nodoc_module'), | ||
], | ||
results, | ||
) | ||
|
||
def test_detailed_list(self): | ||
results = list(sphinxext._detailed_list(self.em)) | ||
self.assertEqual( | ||
[ | ||
('test1', 'test1_module'), | ||
('-----', 'test1_module'), | ||
('\n', 'test1_module'), | ||
('One-line docstring', 'test1_module'), | ||
('\n', 'test1_module'), | ||
('test2', 'test2_module'), | ||
('-----', 'test2_module'), | ||
('\n', 'test2_module'), | ||
('Multi-line docstring\n\nAnother para', 'test2_module'), | ||
('\n', 'test2_module'), | ||
], | ||
results, | ||
) | ||
|
||
def test_detailed_list_format(self): | ||
results = list(sphinxext._detailed_list(self.em, over='+', under='+')) | ||
self.assertEqual( | ||
[ | ||
('+++++', 'test1_module'), | ||
('test1', 'test1_module'), | ||
('+++++', 'test1_module'), | ||
('\n', 'test1_module'), | ||
('One-line docstring', 'test1_module'), | ||
('\n', 'test1_module'), | ||
('+++++', 'test2_module'), | ||
('test2', 'test2_module'), | ||
('+++++', 'test2_module'), | ||
('\n', 'test2_module'), | ||
('Multi-line docstring\n\nAnother para', 'test2_module'), | ||
('\n', 'test2_module'), | ||
], | ||
results, | ||
) | ||
|
||
def test_detailed_list_no_docstring(self): | ||
ext = [_make_ext('nodoc', None)] | ||
em = extension.ExtensionManager.make_test_instance(ext) | ||
results = list(sphinxext._detailed_list(em)) | ||
self.assertEqual( | ||
[ | ||
('nodoc', 'nodoc_module'), | ||
('-----', 'nodoc_module'), | ||
('\n', 'nodoc_module'), | ||
('.. warning:: No documentation found in ENTRY_POINT(nodoc)', | ||
'nodoc_module'), | ||
('\n', 'nodoc_module'), | ||
], | ||
results, | ||
) |