From adbf5e6de33cd6a6b6faffedb6453c3f0de4b6e6 Mon Sep 17 00:00:00 2001 From: Daniel Fremont Date: Sun, 12 Apr 2020 10:58:22 -0700 Subject: [PATCH 1/4] autosummary includes module attributes --- sphinx/ext/autosummary/generate.py | 15 +++++++++++++++ .../autosummary/templates/autosummary/module.rst | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 7b128bea5..bbc1c409b 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -197,6 +197,21 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, get_members(obj, {'class'}, imported=imported_members) ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, {'exception'}, imported=imported_members) + + # Find module attributes with docstrings + attrs, public = [], [] + from sphinx.pycode import ModuleAnalyzer, PycodeError + try: + analyzer = ModuleAnalyzer.for_module(name) + attr_docs = analyzer.find_attr_docs() + for _, attr_name in attr_docs: + if attr_name in ns['members']: + attrs.append(attr_name) + if not attr_name.startswith('_'): + public.append(attr_name) + except PycodeError as err: + pass + ns['attributes'], ns['all_attributes'] = attrs, public elif doc.objtype == 'class': ns['members'] = dir(obj) ns['inherited_members'] = \ diff --git a/sphinx/ext/autosummary/templates/autosummary/module.rst b/sphinx/ext/autosummary/templates/autosummary/module.rst index 6ec89e05e..2eff53843 100644 --- a/sphinx/ext/autosummary/templates/autosummary/module.rst +++ b/sphinx/ext/autosummary/templates/autosummary/module.rst @@ -2,6 +2,17 @@ .. automodule:: {{ fullname }} + {% block attributes %} + {% if attributes %} + .. rubric:: Module Attributes + + .. autosummary:: + {% for item in attributes %} + {{item}} + {%- endfor %} + {% endif %} + {% endblock %} + {% block functions %} {% if functions %} .. rubric:: Functions From 8ee7d908f3deefb90c9d75453b190f13c89a8142 Mon Sep 17 00:00:00 2001 From: Daniel Fremont Date: Sun, 12 Apr 2020 12:06:14 -0700 Subject: [PATCH 2/4] updated corresponding documentation --- doc/usage/extensions/autosummary.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index cedc8a42f..c3a0bfed8 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -260,8 +260,8 @@ The following variables available in the templates: .. data:: attributes - List containing names of "public" attributes in the class. Only available - for classes. + List containing names of "public" attributes in the class/module. Only + available for classes and modules. Additionally, the following filters are available From de4aca857c28f007a9a633a04a2e2e516c3af19a Mon Sep 17 00:00:00 2001 From: Daniel Fremont Date: Sun, 17 May 2020 09:07:11 -0700 Subject: [PATCH 3/4] revisions per comments from tk0miya --- doc/usage/extensions/autosummary.rst | 4 +++ sphinx/ext/autosummary/generate.py | 33 ++++++++++--------- .../templates/autosummary/module.rst | 2 +- .../autosummary_dummy_module.py | 4 +++ tests/roots/test-ext-autosummary/index.rst | 1 + tests/test_ext_autosummary.py | 9 ++++- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index 49e327019..b5acab65d 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -288,6 +288,10 @@ The following variables available in the templates: List containing names of "public" attributes in the class/module. Only available for classes and modules. + .. versionchanged:: 3.1 + + Attributes of modules are supported. + .. data:: modules List containing names of "public" modules in the package. Only available for diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index a6d86445e..1471b5eb2 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -42,6 +42,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warnin from sphinx.ext.autodoc import Documenter from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.locale import __ +from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.registry import SphinxComponentRegistry from sphinx.util import logging from sphinx.util import rst @@ -218,6 +219,21 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, public.append(name) return public, items + def get_module_attrs(members: Any) -> Tuple[List[str], List[str]]: + """Find module attributes with docstrings.""" + attrs, public = [], [] + try: + analyzer = ModuleAnalyzer.for_module(name) + attr_docs = analyzer.find_attr_docs() + for namespace, attr_name in attr_docs: + if namespace == '' and attr_name in members: + attrs.append(attr_name) + if not attr_name.startswith('_'): + public.append(attr_name) + except PycodeError: + pass # give up if ModuleAnalyzer fails to parse code + return attrs, public + def get_modules(obj: Any) -> Tuple[List[str], List[str]]: items = [] # type: List[str] for _, modname, ispkg in pkgutil.iter_modules(obj.__path__): @@ -237,24 +253,11 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, get_members(obj, {'class'}, imported=imported_members) ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, {'exception'}, imported=imported_members) + ns['attributes'], ns['all_attributes'] = \ + get_module_attrs(ns['members']) ispackage = hasattr(obj, '__path__') if ispackage and recursive: ns['modules'], ns['all_modules'] = get_modules(obj) - - # Find module attributes with docstrings - attrs, public = [], [] - from sphinx.pycode import ModuleAnalyzer, PycodeError - try: - analyzer = ModuleAnalyzer.for_module(name) - attr_docs = analyzer.find_attr_docs() - for _, attr_name in attr_docs: - if attr_name in ns['members']: - attrs.append(attr_name) - if not attr_name.startswith('_'): - public.append(attr_name) - except PycodeError as err: - pass - ns['attributes'], ns['all_attributes'] = attrs, public elif doc.objtype == 'class': ns['members'] = dir(obj) ns['inherited_members'] = \ diff --git a/sphinx/ext/autosummary/templates/autosummary/module.rst b/sphinx/ext/autosummary/templates/autosummary/module.rst index 8617e9814..3a93e872a 100644 --- a/sphinx/ext/autosummary/templates/autosummary/module.rst +++ b/sphinx/ext/autosummary/templates/autosummary/module.rst @@ -8,7 +8,7 @@ .. autosummary:: {% for item in attributes %} - {{item}} + {{ item }} {%- endfor %} {% endif %} {% endblock %} diff --git a/tests/roots/test-ext-autosummary/autosummary_dummy_module.py b/tests/roots/test-ext-autosummary/autosummary_dummy_module.py index ffd381f51..0c54a1477 100644 --- a/tests/roots/test-ext-autosummary/autosummary_dummy_module.py +++ b/tests/roots/test-ext-autosummary/autosummary_dummy_module.py @@ -19,3 +19,7 @@ class Foo: def bar(x: Union[int, str], y: int = 1): pass + + +#: a module-level attribute +qux = 2 diff --git a/tests/roots/test-ext-autosummary/index.rst b/tests/roots/test-ext-autosummary/index.rst index bc3f80234..9f657bb73 100644 --- a/tests/roots/test-ext-autosummary/index.rst +++ b/tests/roots/test-ext-autosummary/index.rst @@ -11,4 +11,5 @@ autosummary_dummy_module.Foo autosummary_dummy_module.Foo.Bar autosummary_dummy_module.bar + autosummary_dummy_module.qux autosummary_importfail diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index aa075a9e6..166029ccb 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -203,16 +203,18 @@ def test_autosummary_generate(app, status, warning): [autosummary_table, nodes.table, nodes.tgroup, (nodes.colspec, nodes.colspec, [nodes.tbody, (nodes.row, + nodes.row, nodes.row, nodes.row, nodes.row)])]) assert_node(doctree[4][0], addnodes.toctree, caption="An autosummary") - assert len(doctree[3][0][0][2]) == 4 + assert len(doctree[3][0][0][2]) == 5 assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n' assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n' assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.Foo.Bar\n\n' assert doctree[3][0][0][2][3].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n' + assert doctree[3][0][0][2][4].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute' module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text() assert (' .. autosummary::\n' @@ -237,6 +239,11 @@ def test_autosummary_generate(app, status, warning): '\n' '.. autoclass:: Foo.Bar\n' in FooBar) + qux = (app.srcdir / 'generated' / 'autosummary_dummy_module.qux.rst').read_text() + assert ('.. currentmodule:: autosummary_dummy_module\n' + '\n' + '.. autodata:: qux' in qux) + @pytest.mark.sphinx('dummy', testroot='ext-autosummary', confoverrides={'autosummary_generate_overwrite': False}) From aad103885f971273b7b97386bd8ab483195ada30 Mon Sep 17 00:00:00 2001 From: Daniel Fremont Date: Sun, 17 May 2020 09:16:29 -0700 Subject: [PATCH 4/4] got public/all attrs backwards --- sphinx/ext/autosummary/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 1471b5eb2..a57c73fb7 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -232,7 +232,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, public.append(attr_name) except PycodeError: pass # give up if ModuleAnalyzer fails to parse code - return attrs, public + return public, attrs def get_modules(obj: Any) -> Tuple[List[str], List[str]]: items = [] # type: List[str]