From 5aa7911ae876ca43342c7cc20b917a7bab92c559 Mon Sep 17 00:00:00 2001 From: nstarman Date: Mon, 15 Jan 2024 23:42:41 -0500 Subject: [PATCH 1/5] Add sort option to automodsumm Signed-off-by: nstarman --- sphinx_automodapi/automodapi.py | 10 ++++++++++ sphinx_automodapi/automodsumm.py | 3 ++- sphinx_automodapi/utils.py | 5 ++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sphinx_automodapi/automodapi.py b/sphinx_automodapi/automodapi.py index f216bf9..8618659 100644 --- a/sphinx_automodapi/automodapi.py +++ b/sphinx_automodapi/automodapi.py @@ -67,6 +67,11 @@ Propagates the ``noindex`` flag to autodoc. Use it to avoid duplicate objects warnings. + * ``:sort:`` + Sort the objects in the module alphabetically. The default is to + sort by the order they appear in the module (as specified by `__all__` + or `dir`). + This extension also adds five sphinx configuration options: @@ -250,6 +255,7 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None, allowedpkgnms = [] allowothers = False noindex = False + sort = False # look for actual options unknownops = [] @@ -279,6 +285,8 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None, allowothers = True elif opname == 'noindex': noindex = True + elif opname == 'sort': + sort = True else: unknownops.append(opname) @@ -336,6 +344,8 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None, clsfuncoptions.append(toctreestr) if noindex: clsfuncoptions.append(':noindex:') + if sort: + clsfuncoptions.append(':sort:') if toskip: clsfuncoptions.append(':skip: ' + ','.join(toskip)) if allowedpkgnms: diff --git a/sphinx_automodapi/automodsumm.py b/sphinx_automodapi/automodsumm.py index 323c184..7ff388b 100644 --- a/sphinx_automodapi/automodsumm.py +++ b/sphinx_automodapi/automodsumm.py @@ -130,6 +130,7 @@ class Automodsumm(Autosummary): option_spec['inherited-members'] = flag option_spec['no-inherited-members'] = flag option_spec['noindex'] = flag + option_spec['sort'] = flag def run(self): env = self.state.document.settings.env @@ -138,7 +139,7 @@ def run(self): nodelist = [] try: - localnames, fqns, objs = find_mod_objs(modname) + localnames, fqns, objs = find_mod_objs(modname, sort='sort' in self.options) except ImportError: logger.warning("Couldn't import module " + modname) return [] diff --git a/sphinx_automodapi/utils.py b/sphinx_automodapi/utils.py index 6934124..80300d7 100644 --- a/sphinx_automodapi/utils.py +++ b/sphinx_automodapi/utils.py @@ -37,7 +37,7 @@ def cleanup_whitespace(text): return text -def find_mod_objs(modname, onlylocals=False): +def find_mod_objs(modname, onlylocals=False, sort=False): """ Returns all the public attributes of a module referenced by name. .. note:: @@ -81,6 +81,9 @@ def find_mod_objs(modname, onlylocals=False): else: pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != '_'] + if sort: + pkgitems.sort() + # filter out modules and pull the names and objs out ismodule = inspect.ismodule localnames = [k for k, v in pkgitems if not ismodule(v)] From d1e21945b1799f8a6cc0235aa9fa6264dc432465 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 16 Jan 2024 11:58:57 -0500 Subject: [PATCH 2/5] address review comments Signed-off-by: nstarman --- sphinx_automodapi/automodapi.py | 6 +++--- sphinx_automodapi/utils.py | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sphinx_automodapi/automodapi.py b/sphinx_automodapi/automodapi.py index 8618659..eae0803 100644 --- a/sphinx_automodapi/automodapi.py +++ b/sphinx_automodapi/automodapi.py @@ -68,9 +68,9 @@ objects warnings. * ``:sort:`` - Sort the objects in the module alphabetically. The default is to - sort by the order they appear in the module (as specified by `__all__` - or `dir`). + If the module contains ``__all__``, sort the module's objects + alphabetically (if ``__all__`` is not present, the objects are found + using `dir`, which always gives a sorted list). This extension also adds five sphinx configuration options: diff --git a/sphinx_automodapi/utils.py b/sphinx_automodapi/utils.py index 80300d7..f67a0a9 100644 --- a/sphinx_automodapi/utils.py +++ b/sphinx_automodapi/utils.py @@ -78,11 +78,12 @@ def find_mod_objs(modname, onlylocals=False, sort=False): # define their own __getattr__ and __dir__. if hasattr(mod, '__all__'): pkgitems = [(k, getattr(mod, k)) for k in mod.__all__] - else: - pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != '_'] + # Optionally sort the items alphabetically + if sort: + pkgitems.sort() - if sort: - pkgitems.sort() + else: + pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != "_"] # filter out modules and pull the names and objs out ismodule = inspect.ismodule From bd8e1dbafa0c43c9bb30178153f9022b8fc36960 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 16 Jan 2024 12:00:05 -0500 Subject: [PATCH 3/5] improve import Signed-off-by: nstarman --- sphinx_automodapi/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx_automodapi/utils.py b/sphinx_automodapi/utils.py index f67a0a9..1763fcb 100644 --- a/sphinx_automodapi/utils.py +++ b/sphinx_automodapi/utils.py @@ -1,7 +1,7 @@ -import inspect import sys import re import os +from inspect import ismodule from warnings import warn from sphinx.ext.autosummary.generate import find_autosummary_in_docstring @@ -86,7 +86,6 @@ def find_mod_objs(modname, onlylocals=False, sort=False): pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != "_"] # filter out modules and pull the names and objs out - ismodule = inspect.ismodule localnames = [k for k, v in pkgitems if not ismodule(v)] objs = [v for k, v in pkgitems if not ismodule(v)] From 0afd92a2ae488386e310c669b8e8157a5249b527 Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 22 Feb 2024 11:47:22 -0500 Subject: [PATCH 4/5] WIP test Signed-off-by: nstarman --- sphinx_automodapi/tests/test_automodsumm.py | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sphinx_automodapi/tests/test_automodsumm.py b/sphinx_automodapi/tests/test_automodsumm.py index fe14e9f..e755139 100644 --- a/sphinx_automodapi/tests/test_automodsumm.py +++ b/sphinx_automodapi/tests/test_automodsumm.py @@ -201,3 +201,39 @@ def test_ams_cython(tmpdir, cython_testpackage): # noqa result = f.read() assert result == ams_cython_expected + + +# ============================================================================= + +sorted_str = """ +Before + +.. automodsumm:: sphinx_automodapi.tests.example_module.classes + :sort: + +And After +""" + +sorted_expected = """\ +.. currentmodule:: sphinx_automodapi.tests.example_module.classes + +.. autosummary:: + + Egg + Spam + +""" + + +def test_sort(tmpdir): + with open(tmpdir.join("index.rst").strpath, "w") as f: + f.write(sorted_str) + + write_api_files_to_tmpdir(tmpdir) + + run_sphinx_in_tmpdir(tmpdir) + + with open(tmpdir.join("index.rst.automodsumm").strpath) as f: + result = f.read() + + assert result == sorted_expected From 2b016903019eef954dc9b468f64929f8ac3d7e44 Mon Sep 17 00:00:00 2001 From: Marten Henric van Kerkwijk Date: Thu, 18 Apr 2024 19:41:33 -0400 Subject: [PATCH 5/5] Ensure sort is done also in automodsumm file writing Signed-off-by: nstarman # Conflicts: # sphinx_automodapi/automodsumm.py --- sphinx_automodapi/automodsumm.py | 15 ++++++++++++--- sphinx_automodapi/tests/test_automodsumm.py | 14 +++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/sphinx_automodapi/automodsumm.py b/sphinx_automodapi/automodsumm.py index 7ff388b..2ddb478 100644 --- a/sphinx_automodapi/automodsumm.py +++ b/sphinx_automodapi/automodsumm.py @@ -46,6 +46,12 @@ in the generated documentation. The flags ``:inherited-members:`` or ``:no-inherited-members:`` allows overrriding this global setting. + * ``:sort:`` + If the module contains ``__all__``, sort the module's objects + alphabetically (if ``__all__`` is not present, the objects are found + using `dir`, which always gives a sorted list). + + This extension also adds three sphinx configuration options: * ``automodsumm_writereprocessed`` @@ -381,12 +387,12 @@ def automodsumm_to_autosummary_lines(fn, app): opssecs, remainders)): allindent = i1 + (' ' if i2 is None else i2) - # filter out functions-only, classes-only, and ariables-only + # filter out functions-only, classes-only, variables-only, and sort # options if present. oplines = ops.split('\n') toskip = [] allowedpkgnms = [] - funcsonly = clssonly = varsonly = False + funcsonly = clssonly = varsonly = sort = False for i, ln in reversed(list(enumerate(oplines))): if ':functions-only:' in ln: funcsonly = True @@ -403,6 +409,9 @@ def automodsumm_to_autosummary_lines(fn, app): if ':allowed-package-names:' in ln: allowedpkgnms.extend(_str_list_converter(ln.replace(':allowed-package-names:', ''))) del oplines[i] + if ':sort:' in ln: + sort = True + del oplines[i] if [funcsonly, clssonly, varsonly].count(True) > 1: msg = ('Defined more than one of functions-only, classes-only, ' 'and variables-only. Skipping this directive.') @@ -420,7 +429,7 @@ def automodsumm_to_autosummary_lines(fn, app): newlines.extend(oplines) ols = True if len(allowedpkgnms) == 0 else allowedpkgnms - for nm, fqn, obj in zip(*find_mod_objs(modnm, onlylocals=ols)): + for nm, fqn, obj in zip(*find_mod_objs(modnm, onlylocals=ols, sort=sort)): if nm in toskip: continue if funcsonly and not inspect.isroutine(obj): diff --git a/sphinx_automodapi/tests/test_automodsumm.py b/sphinx_automodapi/tests/test_automodsumm.py index e755139..3922faa 100644 --- a/sphinx_automodapi/tests/test_automodsumm.py +++ b/sphinx_automodapi/tests/test_automodsumm.py @@ -205,6 +205,14 @@ def test_ams_cython(tmpdir, cython_testpackage): # noqa # ============================================================================= +CLASS_RST = """ +:orphan: + +.. currentmodule:: {mod} + +.. autoclass:: {cls} +""".strip() + sorted_str = """ Before @@ -229,7 +237,11 @@ def test_sort(tmpdir): with open(tmpdir.join("index.rst").strpath, "w") as f: f.write(sorted_str) - write_api_files_to_tmpdir(tmpdir) + apidir = tmpdir.mkdir('api') + mod = 'sphinx_automodapi.tests.example_module.classes' + for cls in "Spam", "Egg": + with open(apidir.join(f'{mod}.{cls}.rst').strpath, 'w') as f: + f.write(CLASS_RST.format(mod=mod, cls=cls)) run_sphinx_in_tmpdir(tmpdir)