diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 29051ed..fa4b573 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,7 @@ Changelog Versions follow `Semantic Versioning `_ (``..``). -v1.3.1 (2020-04-05) +vTBC (TBC) ------------------- Features @@ -11,6 +11,9 @@ Features * `#197 `: Added ``autoapi.__version__`` and ``autoapi.__version_info__`` attributes for accessing version information. +* `#201 `: (Python) + Added the ``autoapi_member_order`` option to allow the order that members + are documentated to be configurable. Bug Fixes ^^^^^^^^^ @@ -19,7 +22,7 @@ Bug Fixes Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - +* black shows diffs by default v1.3.0 (2020-04-05) ------------------- diff --git a/autoapi/extension.py b/autoapi/extension.py index 4cf17b6..e687ffd 100644 --- a/autoapi/extension.py +++ b/autoapi/extension.py @@ -290,6 +290,7 @@ def setup(app): app.add_config_value("autoapi_root", API_ROOT, "html") app.add_config_value("autoapi_ignore", [], "html") app.add_config_value("autoapi_options", _DEFAULT_OPTIONS, "html") + app.add_config_value("autoapi_member_order", "bysource", "html") app.add_config_value("autoapi_file_patterns", None, "html") app.add_config_value("autoapi_dirs", [], "html") app.add_config_value("autoapi_keep_files", False, "html") diff --git a/autoapi/mappers/python/mapper.py b/autoapi/mappers/python/mapper.py index 0767dfa..b7b5044 100644 --- a/autoapi/mappers/python/mapper.py +++ b/autoapi/mappers/python/mapper.py @@ -1,5 +1,6 @@ import collections import copy +import operator import os import sys @@ -339,4 +340,11 @@ class PythonSphinxMapper(SphinxMapperBase): child_data, options=options, **kwargs ): obj.children.append(child_obj) + + # Parser gives children in source order already + if self.app.config.autoapi_member_order == "alphabetical": + obj.children.sort(key=operator.attrgetter("name")) + elif self.app.config.autoapi_member_order == "groupwise": + obj.children.sort(key=lambda x: (x.member_order, x.name)) + yield obj diff --git a/autoapi/mappers/python/objects.py b/autoapi/mappers/python/objects.py index 6d418a6..9ae7a83 100644 --- a/autoapi/mappers/python/objects.py +++ b/autoapi/mappers/python/objects.py @@ -21,6 +21,7 @@ class PythonPythonMapper(PythonMapperBase): language = "python" is_callable = False + member_order = 0 def __init__(self, obj, class_content="class", **kwargs): super(PythonPythonMapper, self).__init__(obj, **kwargs) @@ -156,6 +157,7 @@ class PythonPythonMapper(PythonMapperBase): class PythonFunction(PythonPythonMapper): type = "function" is_callable = True + member_order = 40 def __init__(self, obj, **kwargs): super(PythonFunction, self).__init__(obj, **kwargs) @@ -180,6 +182,7 @@ class PythonFunction(PythonPythonMapper): class PythonMethod(PythonFunction): type = "method" is_callable = True + member_order = 50 def __init__(self, obj, **kwargs): super(PythonMethod, self).__init__(obj, **kwargs) @@ -211,6 +214,7 @@ class PythonData(PythonPythonMapper): """Global, module level data.""" type = "data" + member_order = 10 def __init__(self, obj, **kwargs): super(PythonData, self).__init__(obj, **kwargs) @@ -236,6 +240,7 @@ class PythonAttribute(PythonData): """An object/class level attribute.""" type = "attribute" + member_order = 10 class TopLevelPythonPythonMapper(PythonPythonMapper): @@ -290,6 +295,7 @@ class PythonPackage(TopLevelPythonPythonMapper): class PythonClass(PythonPythonMapper): type = "class" + member_order = 30 def __init__(self, obj, **kwargs): super(PythonClass, self).__init__(obj, **kwargs) @@ -374,3 +380,4 @@ class PythonClass(PythonPythonMapper): class PythonException(PythonClass): type = "exception" + member_order = 20 diff --git a/docs/reference/config.rst b/docs/reference/config.rst index 0d0c0fa..44ae2eb 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -144,6 +144,22 @@ Customisation Options docstring is empty and the class defines a ``__new__`` with a docstring, the ``__new__`` docstring is used instead of the ``__init__`` docstring. +.. confval:: autoapi_member_order + + Default: ``bysource`` + + The order to document members. + + * ``alphabetical``: Order members by their name, case sensitively. + * ``bysource``: Order members by the order that they were defined in the source code. + * ``groupwise``: Order members by their type then alphabetically, in the order: + * Submodules and subpackages + * Attributes + * Exceptions + * Classes + * Functions + * Methods + .. confval:: autoapi_python_use_implicit_namespaces Default: ``False`` diff --git a/tests/python/test_pyintegration.py b/tests/python/test_pyintegration.py index 15744c9..f60d29c 100644 --- a/tests/python/test_pyintegration.py +++ b/tests/python/test_pyintegration.py @@ -394,6 +394,26 @@ def test_skipping_members(builder): assert "not ignored" in example_file +@pytest.mark.parametrize( + "value,order", + [ + ("bysource", ["Foo", "decorator_okay", "Bar"]), + ("alphabetical", ["Bar", "Foo", "decorator_okay"]), + ("groupwise", ["Bar", "Foo", "decorator_okay"]), + ], +) +def test_order_members(builder, value, order): + confoverrides = {"autoapi_member_order": value} + builder("pyexample", confoverrides=confoverrides) + + example_path = "_build/text/autoapi/example/index.txt" + with io.open(example_path, encoding="utf8") as example_handle: + example_file = example_handle.read() + + indexes = [example_file.index(name) for name in order] + assert indexes == sorted(indexes) + + class _CompareInstanceType(object): def __init__(self, type_): self.type = type_