diff --git a/README.rst b/README.rst index 9cdb099..2c45609 100644 --- a/README.rst +++ b/README.rst @@ -28,6 +28,7 @@ as self + other Docstring +It also works when using autodoc on a class that implements these methods. After installing this module, add the following to your `conf.py` to enable it @@ -38,6 +39,25 @@ After installing this module, add the following to your `conf.py` to enable it 'sphinxcontrib.prettyspecialmethods', ] +Changing "self" +--------------- + +If a `meta info field`_ named :code:`self-param` is included in the docstring, its +value will be used in place of "self" in the output: + +.. _meta info field: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists + +.. code-block:: rst + + .. method:: __add__(other) + Docstring + :meta self-param: obj + +renders to + +obj + other + Docstring + Links ----- diff --git a/sphinxcontrib/prettyspecialmethods.py b/sphinxcontrib/prettyspecialmethods.py index 35839a9..ec32112 100644 --- a/sphinxcontrib/prettyspecialmethods.py +++ b/sphinxcontrib/prettyspecialmethods.py @@ -10,12 +10,13 @@ import pbr.version import sphinx.addnodes as SphinxNodes -from docutils.nodes import Text, emphasis, inline from sphinx.transforms import SphinxTransform +from docutils.nodes import Text, emphasis, field, field_name, field_body, inline, pending if False: # For type annotations from typing import Any, Dict # noqa + from docutils.nodes import Node # noqa from sphinx.application import Sphinx # noqa __version__ = pbr.version.VersionInfo( @@ -41,11 +42,11 @@ def patch_node(node, text=None, children=None, *, constructor=None): def function_transformer(new_name): - def xf(name_node, parameters_node): + def xf(name_node, parameters_node, self_param): return ( patch_node(name_node, new_name, ()), patch_node(parameters_node, '', [ - SphinxNodes.desc_parameter('', 'self'), + SphinxNodes.desc_parameter('', self_param), *parameters_node.children, ]) ) @@ -54,20 +55,20 @@ def xf(name_node, parameters_node): def unary_op_transformer(op): - def xf(name_node, parameters_node): + def xf(name_node, parameters_node, self_param): return ( patch_node(name_node, op, ()), - emphasis('', 'self'), + emphasis('', self_param), ) return xf def binary_op_transformer(op): - def xf(name_node, parameters_node): + def xf(name_node, parameters_node, self_param): return inline( '', '', - emphasis('', 'self'), + emphasis('', self_param), Text(' '), patch_node(name_node, op, ()), Text(' '), @@ -77,9 +78,9 @@ def xf(name_node, parameters_node): return xf -def brackets(parameters_node): +def brackets(parameters_node, self_param): return [ - emphasis('', 'self'), + emphasis('', self_param), SphinxNodes.desc_name('', '', Text('[')), emphasis('', parameters_node.children[0].astext()), SphinxNodes.desc_name('', '', Text(']')), @@ -87,12 +88,12 @@ def brackets(parameters_node): SPECIAL_METHODS = { - '__getitem__': lambda name_node, parameters_node: inline( - '', '', *brackets(parameters_node) + '__getitem__': lambda name_node, parameters_node, self_param: inline( + '', '', *brackets(parameters_node, self_param) ), - '__setitem__': lambda name_node, parameters_node: inline( + '__setitem__': lambda name_node, parameters_node, self_param: inline( '', '', - *brackets(parameters_node), + *brackets(parameters_node, self_param), Text(' '), SphinxNodes.desc_name('', '', Text('=')), Text(' '), @@ -101,26 +102,26 @@ def brackets(parameters_node): if len(parameters_node.children) > 1 else '' )), ), - '__delitem__': lambda name_node, parameters_node: inline( + '__delitem__': lambda name_node, parameters_node, self_param: inline( '', '', SphinxNodes.desc_name('', '', Text('del')), Text(' '), - *brackets(parameters_node), + *brackets(parameters_node, self_param), ), - '__contains__': lambda name_node, parameters_node: inline( + '__contains__': lambda name_node, parameters_node, self_param: inline( '', '', emphasis('', parameters_node.children[0].astext()), Text(' '), SphinxNodes.desc_name('', '', Text('in')), Text(' '), - emphasis('', 'self'), + emphasis('', self_param), ), - '__await__': lambda name_node, parameters_node: inline( + '__await__': lambda name_node, parameters_node, self_param: inline( '', '', SphinxNodes.desc_name('', '', Text('await')), Text(' '), - emphasis('', 'self'), + emphasis('', self_param), ), '__lt__': binary_op_transformer('<'), @@ -156,8 +157,8 @@ def brackets(parameters_node): '__abs__': function_transformer('abs'), '__invert__': unary_op_transformer('~'), - '__call__': lambda name_node, parameters_node: ( - emphasis('', 'self'), + '__call__': lambda name_node, parameters_node, self_param: ( + emphasis('', self_param), patch_node(parameters_node, '', parameters_node.children) ), '__getattr__': function_transformer('getattr'), @@ -186,12 +187,51 @@ def brackets(parameters_node): } +class PendingSelfParamName(pending): + def __init__(self, name): + # type: (str) -> None + super().__init__( + transform=PrettifySpecialMethods, + details={'self_param': name}, + ) + + @property + def name(self): + # type: () -> str + return self.details['self_param'] + + +def is_meta_self_param_info_field(node): + # type: (Node) -> bool + if not isinstance(node, field): + return False + + name = node.next_node(field_name).astext() + return name == 'meta self-param' + + +def convert_meta_self_param(app, domain, objtype, contentnode): + # type: (Sphinx, str, str, Node) -> None + if not domain == 'py' or 'method' not in objtype: + return + + # Note: Using next_node means we only find the first instance + # of selfparam. Additional selfparam fields are ignored and eventually + # deleted by the Python domain's meta filter. + selfparam_field = contentnode.next_node(is_meta_self_param_info_field) + + if selfparam_field: + selfparam: str = selfparam_field.next_node(field_body).astext() + contentnode.append(PendingSelfParamName(selfparam)) + selfparam_field.replace_self(()) + + class PrettifySpecialMethods(SphinxTransform): default_priority = 800 def apply(self): methods = ( - sig for sig in self.document.traverse(SphinxNodes.desc_signature) + sig.parent for sig in self.document.traverse(SphinxNodes.desc_signature) if 'class' in sig ) @@ -200,11 +240,22 @@ def apply(self): method_name = name_node.astext() if method_name in SPECIAL_METHODS: + # Determine name to use for self in new specification + # using first child occurence + pending_self_param = ref.next_node(PendingSelfParamName) + self_param = pending_self_param.name if pending_self_param else 'self' + parameters_node = ref.next_node(SphinxNodes.desc_parameterlist) - name_node.replace_self(SPECIAL_METHODS[method_name](name_node, parameters_node)) + new_sig = SPECIAL_METHODS[method_name](name_node, parameters_node, self_param) + + name_node.replace_self(new_sig) parameters_node.replace_self(()) + # Remove ALL occurrences of PendingSelfParamName + for p in self.document.traverse(PendingSelfParamName): + p.replace_self(()) + def show_special_methods(app, what, name, obj, skip, options): if what == 'class' and name in SPECIAL_METHODS and getattr(obj, '__doc__', None): @@ -214,6 +265,7 @@ def show_special_methods(app, what, name, obj, skip, options): def setup(app): # type: (Sphinx) -> Dict[str, Any] app.add_transform(PrettifySpecialMethods) + app.connect('object-description-transform', convert_meta_self_param, priority=450) app.setup_extension('sphinx.ext.autodoc') app.connect('autodoc-skip-member', show_special_methods) return {'version': __version__, 'parallel_read_safe': True} diff --git a/tests/test-autodoc-self-param/conf.py b/tests/test-autodoc-self-param/conf.py new file mode 100644 index 0000000..ebcc94a --- /dev/null +++ b/tests/test-autodoc-self-param/conf.py @@ -0,0 +1,23 @@ +import sys +import os + + +doc_module_path = os.path.dirname(__file__) +if doc_module_path not in sys.path: + sys.path.append(doc_module_path) + + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinxcontrib.prettyspecialmethods', +] + + +# The suffix of source filenames. +source_suffix = '.rst' + + +autodoc_default_options = { + 'member-order': 'bysource', + 'exclude-members': '__weakref__,__dict__,__module__', +} diff --git a/tests/test-autodoc-self-param/index.rst b/tests/test-autodoc-self-param/index.rst new file mode 100644 index 0000000..766ef9e --- /dev/null +++ b/tests/test-autodoc-self-param/index.rst @@ -0,0 +1,4 @@ +.. automodule:: test_autodoc_self_param_module + :members: + :special-members: + :undoc-members: diff --git a/tests/test-autodoc-self-param/test_autodoc_self_param_module.py b/tests/test-autodoc-self-param/test_autodoc_self_param_module.py new file mode 100644 index 0000000..3582440 --- /dev/null +++ b/tests/test-autodoc-self-param/test_autodoc_self_param_module.py @@ -0,0 +1,225 @@ +class MethodHolder: + # misc (alphabetical) + def __await__(self): + """:meta self-param: obj""" + pass + + def __call__(self, *args, **kwargs): + """:meta self-param: obj""" + pass + + def __dir__(self): + """:meta self-param: obj""" + pass + + def __format__(self, fmt): + """:meta self-param: obj""" + pass + + def __hash__(self): + """:meta self-param: obj""" + pass + + def __repr__(self): + """:meta self-param: obj""" + pass + + def __sizeof__(self): + """:meta self-param: obj""" + pass + + # type coercion + + def __str__(self): + """:meta self-param: obj""" + pass + + def __bytes__(self): + """:meta self-param: obj""" + pass + + def __bool__(self): + """:meta self-param: obj""" + pass + + def __int__(self): + """:meta self-param: obj""" + pass + + def __float__(self): + """:meta self-param: obj""" + pass + + def __complex__(self): + """:meta self-param: obj""" + pass + + def __index__(self): + """:meta self-param: obj""" + pass + + # attribute access + + def __getattr__(self, attr): + """:meta self-param: obj""" + pass + + def __setattr__(self, attr, value): + """:meta self-param: obj""" + pass + + def __delattr__(self, attr): + """:meta self-param: obj""" + pass + + # sequence methods + + def __contains__(self, value): + """:meta self-param: obj""" + pass + + def __getitem__(self, item): + """:meta self-param: obj""" + pass + + def __setitem__(self, item, value): + """:meta self-param: obj""" + pass + + def __delitem__(self, item): + """:meta self-param: obj""" + pass + + def __iter__(self): + """:meta self-param: obj""" + pass + + def __len__(self): + """:meta self-param: obj""" + pass + + def __length_hint__(self): + """:meta self-param: obj""" + pass + + def __reversed__(self): + """:meta self-param: obj""" + pass + + # unary operators (alphabetical) + + def __invert__(self): + """:meta self-param: obj""" + pass + + def __neg__(self): + """:meta self-param: obj""" + pass + + def __pos__(self): + """:meta self-param: obj""" + pass + + # binary operators (alphabetical) + + def __add__(self, other): + """:meta self-param: obj""" + pass + + def __and__(self, other): + """:meta self-param: obj""" + pass + + def __divmod__(self, other): + """:meta self-param: obj""" + pass + + def __eq__(self, other): + """:meta self-param: obj""" + pass + + def __floordiv__(self, other): + """:meta self-param: obj""" + pass + + def __ge__(self, other): + """:meta self-param: obj""" + pass + + def __gt__(self, other): + """:meta self-param: obj""" + pass + + def __le__(self, other): + """:meta self-param: obj""" + pass + + def __lshift__(self, other): + """:meta self-param: obj""" + pass + + def __lt__(self, other): + """:meta self-param: obj""" + pass + + def __matmul__(self, other): + """:meta self-param: obj""" + pass + + def __mod__(self, other): + """:meta self-param: obj""" + pass + + def __mul__(self, other): + """:meta self-param: obj""" + pass + + def __ne__(self, other): + """:meta self-param: obj""" + pass + + def __or__(self, other): + """:meta self-param: obj""" + pass + + def __pow__(self, other): + """:meta self-param: obj""" + pass + + def __rshift__(self, other): + """:meta self-param: obj""" + pass + + def __sub__(self, other): + """:meta self-param: obj""" + pass + + def __truediv__(self, other): + """:meta self-param: obj""" + pass + + def __xor__(self, other): + """:meta self-param: obj""" + pass + + # other math + + def __abs__(self): + """:meta self-param: obj""" + pass + + def __ceil__(self): + """:meta self-param: obj""" + pass + + def __floor__(self): + """:meta self-param: obj""" + pass + + def __round__(self, n): + """:meta self-param: obj""" + pass + + def __trunc__(self): + """:meta self-param: obj""" + pass diff --git a/tests/test-autodoc/conf.py b/tests/test-autodoc/conf.py new file mode 100644 index 0000000..ebcc94a --- /dev/null +++ b/tests/test-autodoc/conf.py @@ -0,0 +1,23 @@ +import sys +import os + + +doc_module_path = os.path.dirname(__file__) +if doc_module_path not in sys.path: + sys.path.append(doc_module_path) + + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinxcontrib.prettyspecialmethods', +] + + +# The suffix of source filenames. +source_suffix = '.rst' + + +autodoc_default_options = { + 'member-order': 'bysource', + 'exclude-members': '__weakref__,__dict__,__module__', +} diff --git a/tests/test-autodoc/index.rst b/tests/test-autodoc/index.rst new file mode 100644 index 0000000..357b730 --- /dev/null +++ b/tests/test-autodoc/index.rst @@ -0,0 +1,4 @@ +.. automodule:: test_autodoc_module + :members: + :special-members: + :undoc-members: diff --git a/tests/test-autodoc/test_autodoc_module.py b/tests/test-autodoc/test_autodoc_module.py new file mode 100644 index 0000000..fe8eafe --- /dev/null +++ b/tests/test-autodoc/test_autodoc_module.py @@ -0,0 +1,228 @@ +# Explicit empty docstrings prevent some kind of automatic or inherited +# docstring for certain methods. + +class MethodHolder: + # misc (alphabetical) + def __await__(self): + """""" + pass + + def __call__(self, *args, **kwargs): + """""" + pass + + def __dir__(self): + """""" + pass + + def __format__(self, fmt): + """""" + pass + + def __hash__(self): + """""" + pass + + def __repr__(self): + """""" + pass + + def __sizeof__(self): + """""" + pass + + # type coercion + + def __str__(self): + """""" + pass + + def __bytes__(self): + """""" + pass + + def __bool__(self): + """""" + pass + + def __int__(self): + """""" + pass + + def __float__(self): + """""" + pass + + def __complex__(self): + """""" + pass + + def __index__(self): + """""" + pass + + # attribute access + + def __getattr__(self, attr): + """""" + pass + + def __setattr__(self, attr, value): + """""" + pass + + def __delattr__(self, attr): + """""" + pass + + # sequence methods + + def __contains__(self, value): + """""" + pass + + def __getitem__(self, item): + """""" + pass + + def __setitem__(self, item, value): + """""" + pass + + def __delitem__(self, item): + """""" + pass + + def __iter__(self): + """""" + pass + + def __len__(self): + """""" + pass + + def __length_hint__(self): + """""" + pass + + def __reversed__(self): + """""" + pass + + # unary operators (alphabetical) + + def __invert__(self): + """""" + pass + + def __neg__(self): + """""" + pass + + def __pos__(self): + """""" + pass + + # binary operators (alphabetical) + + def __add__(self, other): + """""" + pass + + def __and__(self, other): + """""" + pass + + def __divmod__(self, other): + """""" + pass + + def __eq__(self, other): + """""" + pass + + def __floordiv__(self, other): + """""" + pass + + def __ge__(self, other): + """""" + pass + + def __gt__(self, other): + """""" + pass + + def __le__(self, other): + """""" + pass + + def __lshift__(self, other): + """""" + pass + + def __lt__(self, other): + """""" + pass + + def __matmul__(self, other): + """""" + pass + + def __mod__(self, other): + """""" + pass + + def __mul__(self, other): + """""" + pass + + def __ne__(self, other): + """""" + pass + + def __or__(self, other): + """""" + pass + + def __pow__(self, other): + """""" + pass + + def __rshift__(self, other): + """""" + pass + + def __sub__(self, other): + """""" + pass + + def __truediv__(self, other): + """""" + pass + + def __xor__(self, other): + """""" + pass + + # other math + + def __abs__(self): + """""" + pass + + def __ceil__(self): + """""" + pass + + def __floor__(self): + """""" + pass + + def __round__(self, n): + """""" + pass + + def __trunc__(self): + """""" + pass diff --git a/tests/test-simple-self-param/conf.py b/tests/test-simple-self-param/conf.py new file mode 100644 index 0000000..f94cbad --- /dev/null +++ b/tests/test-simple-self-param/conf.py @@ -0,0 +1,4 @@ +extensions = ['sphinxcontrib.prettyspecialmethods'] + +# The suffix of source filenames. +source_suffix = '.rst' diff --git a/tests/test-simple-self-param/index.rst b/tests/test-simple-self-param/index.rst new file mode 100644 index 0000000..00a59c9 --- /dev/null +++ b/tests/test-simple-self-param/index.rst @@ -0,0 +1,225 @@ +# misc (alphabetical) + +.. py:method:: __await__() + + :meta self-param: obj + +.. py:method:: __call__(*args, **kwargs) + + :meta self-param: obj + +.. py:method:: __dir__() + + :meta self-param: obj + +.. py:method:: __format__(fmt) + + :meta self-param: obj + +.. py:method:: __hash__() + + :meta self-param: obj + +.. py:method:: __repr__() + + :meta self-param: obj + +.. py:method:: __sizeof__() + + :meta self-param: obj + +# type coercion + +.. py:method:: __str__() + + :meta self-param: obj + +.. py:method:: __bytes__() + + :meta self-param: obj + +.. py:method:: __bool__() + + :meta self-param: obj + +.. py:method:: __int__() + + :meta self-param: obj + +.. py:method:: __float__() + + :meta self-param: obj + +.. py:method:: __complex__() + + :meta self-param: obj + +.. py:method:: __index__() + + :meta self-param: obj + +# attribute access + +.. py:method:: __getattr__(attr) + + :meta self-param: obj + +.. py:method:: __setattr__(attr, value) + + :meta self-param: obj + +.. py:method:: __delattr__(attr) + + :meta self-param: obj + +# sequence methods + +.. py:method:: __contains__(value) + + :meta self-param: obj + +.. py:method:: __getitem__(item) + + :meta self-param: obj + +.. py:method:: __setitem__(item, value) + + :meta self-param: obj + +.. py:method:: __delitem__(item) + + :meta self-param: obj + +.. py:method:: __iter__() + + :meta self-param: obj + +.. py:method:: __len__() + + :meta self-param: obj + +.. py:method:: __length_hint__() + + :meta self-param: obj + +.. py:method:: __reversed__() + + :meta self-param: obj + +# unary operators (alphabetical) + +.. py:method:: __invert__() + + :meta self-param: obj + +.. py:method:: __neg__() + + :meta self-param: obj + +.. py:method:: __pos__() + + :meta self-param: obj + +# binary operators (alphabetical) + +.. py:method:: __add__(other) + + :meta self-param: obj + +.. py:method:: __and__(other) + + :meta self-param: obj + +.. py:method:: __divmod__(other) + + :meta self-param: obj + +.. py:method:: __eq__(other) + + :meta self-param: obj + +.. py:method:: __floordiv__(other) + + :meta self-param: obj + +.. py:method:: __ge__(other) + + :meta self-param: obj + +.. py:method:: __gt__(other) + + :meta self-param: obj + +.. py:method:: __le__(other) + + :meta self-param: obj + +.. py:method:: __lshift__(other) + + :meta self-param: obj + +.. py:method:: __lt__(other) + + :meta self-param: obj + +.. py:method:: __matmul__(other) + + :meta self-param: obj + +.. py:method:: __mod__(other) + + :meta self-param: obj + +.. py:method:: __mul__(other) + + :meta self-param: obj + +.. py:method:: __ne__(other) + + :meta self-param: obj + +.. py:method:: __or__(other) + + :meta self-param: obj + +.. py:method:: __pow__(other) + + :meta self-param: obj + +.. py:method:: __rshift__(other) + + :meta self-param: obj + +.. py:method:: __sub__(other) + + :meta self-param: obj + +.. py:method:: __truediv__(other) + + :meta self-param: obj + +.. py:method:: __xor__(other) + + :meta self-param: obj + +# other math + +.. py:method:: __abs__() + + :meta self-param: obj + +.. py:method:: __ceil__() + + :meta self-param: obj + +.. py:method:: __floor__() + + :meta self-param: obj + +.. py:method:: __round__(n) + + :meta self-param: obj + +.. py:method:: __trunc__() + + :meta self-param: obj diff --git a/tests/test-simple/index.rst b/tests/test-simple/index.rst index 3c7125a..17b4f72 100644 --- a/tests/test-simple/index.rst +++ b/tests/test-simple/index.rst @@ -1,4 +1,4 @@ -misc (alphabetical) +# misc (alphabetical) .. py:method:: __await__() @@ -14,7 +14,7 @@ misc (alphabetical) .. py:method:: __sizeof__() -type coercion +# type coercion .. py:method:: __str__() @@ -30,7 +30,7 @@ type coercion .. py:method:: __index__() -attribute access +# attribute access .. py:method:: __getattr__(attr) @@ -38,7 +38,7 @@ attribute access .. py:method:: __delattr__(attr) -sequence methods +# sequence methods .. py:method:: __contains__(value) @@ -56,7 +56,7 @@ sequence methods .. py:method:: __reversed__() -unary operators (alphabetical) +# unary operators (alphabetical) .. py:method:: __invert__() @@ -64,7 +64,7 @@ unary operators (alphabetical) .. py:method:: __pos__() -binary operators (alphabetical) +# binary operators (alphabetical) .. py:method:: __add__(other) @@ -106,7 +106,7 @@ binary operators (alphabetical) .. py:method:: __xor__(other) -other math +# other math .. py:method:: __abs__() diff --git a/tests/test_plaintext.py b/tests/test_plaintext.py index 9a1435c..aac9efb 100644 --- a/tests/test_plaintext.py +++ b/tests/test_plaintext.py @@ -1,6 +1,78 @@ import pytest +# SIMPLE_RESULT is shared among all tests as a check to ensure that +# all tests cover all supported special methods. +# Do not switch to separate values for each test without +# developing a replacement mechanism for ensuring this consistency. + +# Non-method lines in result are prefixed with a hash (#) +# to allow for filtering + +SIMPLE_RESULT = [ + '# misc (alphabetical)', + 'await *self*', + '*self*(*args, **kwargs)', + 'dir(self)', + 'format(self, fmt)', + 'hash(self)', + 'repr(self)', + 'sys.getsizeof(self)', + '# type coercion', + 'str(self)', + 'bytes(self)', + 'bool(self)', + 'int(self)', + 'float(self)', + 'complex(self)', + 'operator.index(self)', + '# attribute access', + 'getattr(self, attr)', + 'setattr(self, attr, value)', + 'delattr(self, attr)', + '# sequence methods', + '*value* in *self*', + '*self*[*item*]', + '*self*[*item*] = *value*', + 'del *self*[*item*]', + 'iter(self)', + 'len(self)', + 'operator.length_hint(self)', + 'reversed(self)', + '# unary operators (alphabetical)', + '~*self*', + '-*self*', + '+*self*', + '# binary operators (alphabetical)', + '*self* + *other*', + '*self* & *other*', + 'divmod(self, other)', + '*self* == *other*', + '*self* // *other*', + '*self* >= *other*', + '*self* > *other*', + '*self* <= *other*', + '*self* << *other*', + '*self* < *other*', + '*self* @ *other*', + '*self* % *other*', + '*self* * *other*', + '*self* != *other*', + '*self* | *other*', + '*self* ** *other*', + '*self* >> *other*', + '*self* - *other*', + '*self* / *other*', + '*self* ^ *other*', + '# other math', + 'abs(self)', + 'math.ceil(self)', + 'math.floor(self)', + 'round(self, n)', + 'math.trunc(self)', +] + + @pytest.mark.sphinx(testroot='simple', buildername='text') def test_domain_py_objects(app, status, warning): app.builder.build_all() @@ -8,65 +80,44 @@ def test_domain_py_objects(app, status, warning): lines = [line.rstrip('\n') for line in result.splitlines() if line.strip()] - assert lines == [ - 'misc (alphabetical)', - 'await *self*', - '*self*(*args, **kwargs)', - 'dir(self)', - 'format(self, fmt)', - 'hash(self)', - 'repr(self)', - 'sys.getsizeof(self)', - 'type coercion', - 'str(self)', - 'bytes(self)', - 'bool(self)', - 'int(self)', - 'float(self)', - 'complex(self)', - 'operator.index(self)', - 'attribute access', - 'getattr(self, attr)', - 'setattr(self, attr, value)', - 'delattr(self, attr)', - 'sequence methods', - '*value* in *self*', - '*self*[*item*]', - '*self*[*item*] = *value*', - 'del *self*[*item*]', - 'iter(self)', - 'len(self)', - 'operator.length_hint(self)', - 'reversed(self)', - 'unary operators (alphabetical)', - '~*self*', - '-*self*', - '+*self*', - 'binary operators (alphabetical)', - '*self* + *other*', - '*self* & *other*', - 'divmod(self, other)', - '*self* == *other*', - '*self* // *other*', - '*self* >= *other*', - '*self* > *other*', - '*self* <= *other*', - '*self* << *other*', - '*self* < *other*', - '*self* @ *other*', - '*self* % *other*', - '*self* * *other*', - '*self* != *other*', - '*self* | *other*', - '*self* ** *other*', - '*self* >> *other*', - '*self* - *other*', - '*self* / *other*', - '*self* ^ *other*', - 'other math', - 'abs(self)', - 'math.ceil(self)', - 'math.floor(self)', - 'round(self, n)', - 'math.trunc(self)', + assert lines == SIMPLE_RESULT + + +@pytest.mark.sphinx(testroot='simple-self-param', buildername='text') +def test_domain_py_objects_with_self_param(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'index.txt').read_text() + + lines = [line.rstrip('\n') for line in result.splitlines() if line.strip()] + + assert lines == [line.replace('self', 'obj') for line in SIMPLE_RESULT] + + +@pytest.mark.sphinx(testroot='autodoc', buildername='text') +def test_autodoc_module(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'index.txt').read_text() + + lines = [line.strip() for line in result.splitlines() if line.strip()] + + expected = [line for line in SIMPLE_RESULT if not line.startswith('#')] + expected.insert(0, 'class test_autodoc_module.MethodHolder') + + assert lines == expected + + +@pytest.mark.sphinx(testroot='autodoc-self-param', buildername='text') +def test_autodoc_module_with_self_param(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'index.txt').read_text() + + lines = [line.strip() for line in result.splitlines() if line.strip()] + + expected = [ + line.replace('self', 'obj') + for line in SIMPLE_RESULT + if not line.startswith('#') ] + expected.insert(0, 'class test_autodoc_self_param_module.MethodHolder') + + assert lines == expected