Skip to content

Commit

Permalink
Merge pull request #104 from Chilipp/autoexception-support
Browse files Browse the repository at this point in the history
Autoexception support
  • Loading branch information
Chilipp authored Oct 21, 2024
2 parents 811352b + db473cf commit f81147e
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 48 deletions.
28 changes: 0 additions & 28 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,31 +75,3 @@ jobs:
"sphinxcontrib-jsmath<1.0.1"
"sphinxcontrib-qthelp<1.0.7"
"sphinxcontrib-serializinghtml<1.1.10"'


build-legacy-sphinx-30plus:
name: Build

strategy:
fail-fast: false
matrix:
python-version: [ "3.7", "3.8", "3.9" ]
sphinx-version: [
"3.0.*", # possible range: 3.0.0 - 3.5.4
]
include:
- python-version: "3.7"
sphinx-version: "3.5.*" # latest version that supports py3.7
uses: ./.github/workflows/build.yml
with:
python-version: ${{ matrix.python-version }}
extra-requirements: '\
"sphinx==${{ matrix.sphinx-version }}"
"jinja2<3.1"
"alabaster<0.7.14"
"sphinxcontrib-applehelp<1.0.8"
"sphinxcontrib-devhelp<1.0.6"
"sphinxcontrib-htmlhelp<2.0.5"
"sphinxcontrib-jsmath<1.0.1"
"sphinxcontrib-qthelp<1.0.7"
"sphinxcontrib-serializinghtml<1.1.10"'
90 changes: 75 additions & 15 deletions autodocsumm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

from sphinx.ext.autodoc import (
ClassDocumenter, ModuleDocumenter, ALL, PycodeError,
ModuleAnalyzer, AttributeDocumenter, DataDocumenter, Options,
ModuleAnalyzer, AttributeDocumenter, DataDocumenter, Options, ExceptionDocumenter,
Documenter, prepare_docstring)
import sphinx.ext.autodoc as ad

Expand Down Expand Up @@ -349,7 +349,7 @@ class AutoSummModuleDocumenter(ModuleDocumenter, AutosummaryDocumenter):

#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.ModuleDocumenter`
priority = ModuleDocumenter.priority + 0.1
priority = ModuleDocumenter.priority + 0.1 # type: ignore[assignment]

#: original option_spec from :class:`sphinx.ext.autodoc.ModuleDocumenter`
#: but with additional autosummary boolean option
Expand Down Expand Up @@ -399,7 +399,7 @@ class AutoSummClassDocumenter(ClassDocumenter, AutosummaryDocumenter):

#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.ClassDocumenter`
priority = ClassDocumenter.priority + 0.1
priority = ClassDocumenter.priority + 0.1 # type: ignore[assignment]

#: original option_spec from :class:`sphinx.ext.autodoc.ClassDocumenter`
#: but with additional autosummary boolean option
Expand Down Expand Up @@ -437,11 +437,64 @@ def add_content(self, *args, **kwargs):
self.add_autosummary(relative_ref_paths=True)


class AutoSummExceptionDocumenter(ExceptionDocumenter, AutosummaryDocumenter):
"""Exception Documenter with autosummary tables for its members.
This class has the same functionality as the base
:class:`sphinx.ext.autodoc.ExceptionDocumenter` class but with an
additional `autosummary` option to provide the ability to provide a summary
of all methods and attributes.
It's priority is slightly higher than the one of the ExceptionDocumenter
"""

#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.ExceptionDocumenter`
priority = ExceptionDocumenter.priority + 0.1 # type: ignore[assignment]

#: original option_spec from
#: :class:`sphinx.ext.autodoc.ExceptionDocumenter` but with additional
#: autosummary boolean option
option_spec = ExceptionDocumenter.option_spec.copy()
option_spec['autosummary'] = bool_option
option_spec['autosummary-no-nesting'] = bool_option
option_spec['autosummary-sections'] = list_option
option_spec['autosummary-no-titles'] = bool_option
option_spec['autosummary-force-inline'] = bool_option
option_spec['autosummary-nosignatures'] = bool_option

#: Add options for members for the autosummary
for _option in member_options.intersection(option_spec):
option_spec['autosummary-' + _option] = option_spec[_option]
del _option

member_sections = {
ad.ExceptionDocumenter.member_order: 'Classes',
ad.MethodDocumenter.member_order: 'Methods',
ad.AttributeDocumenter.member_order: 'Attributes',
}
""":class:`dict` that includes the autosummary sections
This dictionary defines the sections for the autosummmary option. The
values correspond to the :attr:`sphinx.ext.autodoc.Documenter.member_order`
attribute that shall be used for each section."""

def add_content(self, *args, **kwargs):
super().add_content(*args, **kwargs)

# If the class is already documented under another name, Sphinx
# documents it as data/attribute. In this case, we do not want to
# generate an autosummary of the class for the attribute (see #69).
if not self.doc_as_attr:
self.add_autosummary(relative_ref_paths=True)


class CallableDataDocumenter(DataDocumenter):
""":class:`sphinx.ext.autodoc.DataDocumenter` that uses the __call__ attr
"""

priority = DataDocumenter.priority + 0.1
#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.DataDocumenter`
priority = DataDocumenter.priority + 0.1 # type: ignore[assignment]

def format_args(self):
# for classes, the relevant signature is the __init__ method's
Expand Down Expand Up @@ -474,6 +527,8 @@ def get_doc(self, *args, **kwargs):

doc = []
for docstring in docstrings:
encoding = _get_arg("encoding", 0, None, *args, **kwargs)
ignore = _get_arg("ignore", 1, 1, *args, **kwargs)
if not isinstance(docstring, str):
docstring = force_decode(docstring, encoding)
doc.append(prepare_docstring(docstring, ignore))
Expand All @@ -486,7 +541,9 @@ class CallableAttributeDocumenter(AttributeDocumenter):
attr
"""

priority = AttributeDocumenter.priority + 0.1
#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.AttributeDocumenter`
priority = AttributeDocumenter.priority + 0.1 # type: ignore[assignment]

def format_args(self):
# for classes, the relevant signature is the __init__ method's
Expand Down Expand Up @@ -565,7 +622,7 @@ class NoDataDataDocumenter(CallableDataDocumenter):
"""DataDocumenter that prevents the displaying of large data"""

#: slightly higher priority as the one of the CallableDataDocumenter
priority = CallableDataDocumenter.priority + 0.1
priority = CallableDataDocumenter.priority + 0.1 # type: ignore[assignment]

def __init__(self, *args, **kwargs):
super(NoDataDataDocumenter, self).__init__(*args, **kwargs)
Expand All @@ -580,7 +637,7 @@ class NoDataAttributeDocumenter(CallableAttributeDocumenter):
"""AttributeDocumenter that prevents the displaying of large data"""

#: slightly higher priority as the one of the CallableAttributeDocumenter
priority = CallableAttributeDocumenter.priority + 0.1
priority = CallableAttributeDocumenter.priority + 0.1 # type: ignore[assignment]

def __init__(self, *args, **kwargs):
super(NoDataAttributeDocumenter, self).__init__(*args, **kwargs)
Expand All @@ -596,13 +653,15 @@ class AutoDocSummDirective(SphinxDirective):
Usage::
.. autoclasssum:: <Class>
.. autoclasssumm:: <Class>
.. automodsumm:: <module>
.. automodsum:: <module>
.. autoexceptionsumm:: <ExceptionClass>
The directive additionally supports all options of the ``autoclass`` or
``automod`` directive respectively. Sections can be a list of section titles
to be included. If ommitted, all sections are used.
The directive additionally supports all options of the ``autoclass``,
``automod``, or ``autoexception`` directive respectively. Sections can be a
list of section titles to be included. If ommitted, all sections are used.
"""

has_content = False
Expand All @@ -616,9 +675,9 @@ def run(self):
reporter = self.state.document.reporter

try:
source, lineno = reporter.get_source_and_line(self.lineno)
_, lineno = reporter.get_source_and_line(self.lineno)
except AttributeError:
source, lineno = (None, None)
_, lineno = (None, None)

# look up target Documenter
objtype = self.name[4:-4] # strip prefix (auto-) and suffix (-summ).
Expand Down Expand Up @@ -659,6 +718,7 @@ def setup(app):
app.setup_extension('sphinx.ext.autosummary')
app.setup_extension('sphinx.ext.autodoc')
app.add_directive('autoclasssumm', AutoDocSummDirective)
app.add_directive('autoexceptionsumm', AutoDocSummDirective)
app.add_directive('automodulesumm', AutoDocSummDirective)

AUTODOC_DEFAULT_OPTIONS.extend(
Expand All @@ -673,7 +733,7 @@ def setup(app):
registry = app.registry.documenters
for cls in [AutoSummClassDocumenter, AutoSummModuleDocumenter,
CallableAttributeDocumenter, NoDataDataDocumenter,
NoDataAttributeDocumenter]:
NoDataAttributeDocumenter, AutoSummExceptionDocumenter]:
if not issubclass(registry.get(cls.objtype), cls):
app.add_autodocumenter(cls, override=True)

Expand Down
5 changes: 5 additions & 0 deletions docs/conf_settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ Directives
By default, this directives also sets the `:members:` option unless you
specify `:no-members`.

.. rst:directive:: autoexceptionsumm
The same as the ``autoclasssumm`` directive, just for an ``Exception``
subclass.

.. rst:directive:: automodulesumm
The same as the ``autoclasssumm`` directive, just for a module.
Expand Down
8 changes: 8 additions & 0 deletions docs/demo_exception.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. _demo_exception:

Demo Exception
==============

.. autoexception:: dummy.MyException
:members:
:noindex:
13 changes: 13 additions & 0 deletions docs/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,18 @@ def do_something(self):
some_other_attr = None


class MyException(object):
"""Some Exception
With some description"""

def do_something_exceptional(self):
"""Do something exceptional"""
pass

#: Any instance attribute
some_exception_attr = None


#: Some module data
large_data = 'Whatever'
14 changes: 10 additions & 4 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Examples

Demo Module <demo_module>
Demo Class <demo_class>
Demo Exception <demo_exception>
Demo Grouper <demo_grouper>

Including a table of contents
Expand All @@ -24,11 +25,16 @@ The *autosummary* flag introduces a small table of contents. So::

produces :ref:`this <demo_module>`. And::

.. autoclass:: dummy.SomeClass
.. autoclass:: dummy.MyClass
:members:
:autosummary:

produces :ref:`this <demo_class>`.
produces :ref:`this <demo_class>`, and for exceptions::

.. autoexception:: dummy.MyException
:members:
:autosummary:
produces :ref:`this <demo_exception>`.

By default, module members are (mainly) grouped according into *Functions*,
*Classes* and *Data*, class members are grouped into *Methods* and
Expand Down Expand Up @@ -178,8 +184,8 @@ section of a class, you can specify::
Multiple sections might be separated by `;;`, e.g.
``:autosummary-sections: Methods ;; Attributes``.

This also works for the ``autoclasssumm`` and ``automodulesumm`` directives,
e.g.::
This also works for the ``autoclasssumm``, ``autoexceptionsumm`` and
``automodulesumm`` directives, e.g.::

.. autoclasssumm:: dummy.SomeClass
:autosummary-sections: Methods
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ classifiers = [

requires-python = '>= 3.7'
dependencies = [
'Sphinx >= 2.2, < 9.0',
'Sphinx >= 4.0, < 9.0',
]

[project.urls]
Expand Down
12 changes: 12 additions & 0 deletions tests/test-root/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ class InnerClass(object):
small_data = 'Should be skipped'


class TestException(Exception):
"""Exception test for autosummary"""

def __init__(self):
#: This is an exception attribute
self.exception_instance_attribute = 1

def test_exception_method(self):
"""Test if the method is included"""
pass


class InheritedTestClass(TestClass):
"""Class test for inherited attributes"""

Expand Down
4 changes: 4 additions & 0 deletions tests/test-root/test_autoexceptionsumm.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Autoexceptionsumm of Dummy Exception
====================================

.. autoexceptionsumm:: dummy.TestException
4 changes: 4 additions & 0 deletions tests/test-root/test_exception.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Dummy Exception Doc
===================

.. autoexception:: dummy.TestException
24 changes: 24 additions & 0 deletions tests/test_autodocsumm.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,17 @@ def test_class(self, app):
'DummySection'
)

def test_exception(self, app):
app.build()
html = get_html(app, 'test_exception.html')

if sphinx_version[:2] > [3, 1]:
assert in_autosummary("exception_instance_attribute", html)
elif sphinx_version[:2] < [3, 1]:
assert in_autosummary("TestException.exception_instance_attribute", html)

assert in_autosummary("test_exception_method", html)

@pytest.mark.skipif(
sphinx_version[:2] < [3, 1], reason="Only available for sphinx>=3"
)
Expand Down Expand Up @@ -412,6 +423,19 @@ def test_autoclasssumm(self, app):
assert in_autosummary("test_method", html)
assert in_autosummary("test_attr", html)

def test_autoexceptionsumm(self, app):
"""Test building the autosummary of a class."""
app.build()

html = get_html(app, 'test_autoexceptionsumm.html')

# the class docstring must not be in the html
assert "Class exception for autosummary" not in html

# test if the methods and attributes are there in a table
assert in_autosummary("test_exception_method", html)
assert in_autosummary("exception_instance_attribute", html)

def test_autoclasssumm_no_titles(self, app):
"""Test building the autosummary of a class."""
app.build()
Expand Down

0 comments on commit f81147e

Please sign in to comment.