Merge pull request #4324 from tk0miya/refactor_test_autodoc2

test_autodoc: Separate testdata (python objects) and testcases
This commit is contained in:
Takeshi KOMIYA 2017-12-22 00:25:44 +09:00 committed by GitHub
commit 389b6d2ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 557 additions and 320 deletions

View File

@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
import enum
from six import StringIO, add_metaclass
from sphinx.ext.autodoc import add_documenter # NOQA
__all__ = ['Class']
#: documentation for the integer
integer = 1
def raises(exc, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
pass
class CustomEx(Exception):
"""My custom exception."""
def f(self):
"""Exception method."""
class CustomDataDescriptor(object):
"""Descriptor class docstring."""
def __init__(self, doc):
self.__doc__ = doc
def __get__(self, obj, type=None):
if obj is None:
return self
return 42
def meth(self):
"""Function."""
return "The Answer"
class CustomDataDescriptorMeta(type):
"""Descriptor metaclass docstring."""
@add_metaclass(CustomDataDescriptorMeta)
class CustomDataDescriptor2(CustomDataDescriptor):
"""Descriptor class with custom metaclass docstring."""
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
from functools import partial
function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
class Derived(Base):
def inheritedmeth(self):
# no docstring here
pass
class Class(Base):
"""Class to document."""
descr = CustomDataDescriptor("Descriptor instance docstring.")
def meth(self):
"""Function."""
def undocmeth(self):
pass
def skipmeth(self):
"""Method that should be skipped."""
def excludemeth(self):
"""Method that should be excluded."""
# should not be documented
skipattr = 'foo'
#: should be documented -- süß
attr = 'bar'
@property
def prop(self):
"""Property."""
docattr = 'baz'
"""should likewise be documented -- süß"""
udocattr = 'quux'
u"""should be documented as well - süß"""
# initialized to any class imported from another module
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
self.inst_attr_inline = None #: an inline documented instance attr
#: a documented instance attribute
self.inst_attr_comment = None
self.inst_attr_string = None
"""a documented instance attribute"""
self._private_inst_attr = None #: a private instance attribute
def __special1__(self):
"""documented special method"""
def __special2__(self):
# undocumented special method
pass
class CustomDict(dict):
"""Docstring."""
def function(foo, *args, **kwds):
"""
Return spam.
"""
pass
class Outer(object):
"""Foo"""
class Inner(object):
"""Foo"""
def meth(self):
"""Foo"""
# should be documented as an alias
factory = dict
class DocstringSig(object):
def meth(self):
"""meth(FOO, BAR=1) -> BAZ
First line of docstring
rest of docstring
"""
def meth2(self):
"""First line, no signature
Second line followed by indentation::
indented line
"""
@property
def prop1(self):
"""DocstringSig.prop1(self)
First line of docstring
"""
return 123
@property
def prop2(self):
"""First line of docstring
Second line of docstring
"""
return 456
class StrRepr(str):
def __repr__(self):
return self
class AttCls(object):
a1 = StrRepr('hello\nworld')
a2 = None
class InstAttCls(object):
"""Class with documented class and instance attributes."""
#: Doc comment for class attribute InstAttCls.ca1.
#: It can have multiple lines.
ca1 = 'a'
ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
ca3 = 'c'
"""Docstring for class attribute InstAttCls.ca3."""
def __init__(self):
#: Doc comment for instance attribute InstAttCls.ia1
self.ia1 = 'd'
self.ia2 = 'e'
"""Docstring for instance attribute InstAttCls.ia2."""
class EnumCls(enum.Enum):
"""
this is enum class
"""
#: doc for val1
val1 = 12
val2 = 23 #: doc for val2
val3 = 34
"""doc for val3"""

View File

@ -5,7 +5,7 @@ Just testing a few autodoc possibilities...
.. automodule:: util .. automodule:: util
.. automodule:: test_autodoc .. automodule:: autodoc_target
:members: :members:
.. autofunction:: function .. autofunction:: function
@ -34,7 +34,7 @@ Just testing a few autodoc possibilities...
.. autoclass:: MarkupError .. autoclass:: MarkupError
.. currentmodule:: test_autodoc .. currentmodule:: autodoc_target
.. autoclass:: InstAttCls .. autoclass:: InstAttCls
:members: :members:

View File

@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
import enum
from six import StringIO, add_metaclass
from sphinx.ext.autodoc import add_documenter # NOQA
__all__ = ['Class']
#: documentation for the integer
integer = 1
def raises(exc, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
pass
class CustomEx(Exception):
"""My custom exception."""
def f(self):
"""Exception method."""
class CustomDataDescriptor(object):
"""Descriptor class docstring."""
def __init__(self, doc):
self.__doc__ = doc
def __get__(self, obj, type=None):
if obj is None:
return self
return 42
def meth(self):
"""Function."""
return "The Answer"
class CustomDataDescriptorMeta(type):
"""Descriptor metaclass docstring."""
@add_metaclass(CustomDataDescriptorMeta)
class CustomDataDescriptor2(CustomDataDescriptor):
"""Descriptor class with custom metaclass docstring."""
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
from functools import partial
function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
class Derived(Base):
def inheritedmeth(self):
# no docstring here
pass
class Class(Base):
"""Class to document."""
descr = CustomDataDescriptor("Descriptor instance docstring.")
def meth(self):
"""Function."""
def undocmeth(self):
pass
def skipmeth(self):
"""Method that should be skipped."""
def excludemeth(self):
"""Method that should be excluded."""
# should not be documented
skipattr = 'foo'
#: should be documented -- süß
attr = 'bar'
@property
def prop(self):
"""Property."""
docattr = 'baz'
"""should likewise be documented -- süß"""
udocattr = 'quux'
u"""should be documented as well - süß"""
# initialized to any class imported from another module
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
self.inst_attr_inline = None #: an inline documented instance attr
#: a documented instance attribute
self.inst_attr_comment = None
self.inst_attr_string = None
"""a documented instance attribute"""
self._private_inst_attr = None #: a private instance attribute
def __special1__(self):
"""documented special method"""
def __special2__(self):
# undocumented special method
pass
class CustomDict(dict):
"""Docstring."""
def function(foo, *args, **kwds):
"""
Return spam.
"""
pass
class Outer(object):
"""Foo"""
class Inner(object):
"""Foo"""
def meth(self):
"""Foo"""
# should be documented as an alias
factory = dict
class DocstringSig(object):
def meth(self):
"""meth(FOO, BAR=1) -> BAZ
First line of docstring
rest of docstring
"""
def meth2(self):
"""First line, no signature
Second line followed by indentation::
indented line
"""
@property
def prop1(self):
"""DocstringSig.prop1(self)
First line of docstring
"""
return 123
@property
def prop2(self):
"""First line of docstring
Second line of docstring
"""
return 456
class StrRepr(str):
def __repr__(self):
return self
class AttCls(object):
a1 = StrRepr('hello\nworld')
a2 = None
class InstAttCls(object):
"""Class with documented class and instance attributes."""
#: Doc comment for class attribute InstAttCls.ca1.
#: It can have multiple lines.
ca1 = 'a'
ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
ca3 = 'c'
"""Docstring for class attribute InstAttCls.ca3."""
def __init__(self):
#: Doc comment for instance attribute InstAttCls.ia1
self.ia1 = 'd'
self.ia2 = 'e'
"""Docstring for instance attribute InstAttCls.ia2."""
class EnumCls(enum.Enum):
"""
this is enum class
"""
#: doc for val1
val1 = 12
val2 = 23 #: doc for val2
val3 = 34
"""doc for val3"""

View File

@ -10,13 +10,12 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys
from six import PY3 from six import PY3
from sphinx.testing.util import SphinxTestApp, Struct # NOQA from sphinx.testing.util import SphinxTestApp, Struct # NOQA
import pytest import pytest
import enum
from six import StringIO, add_metaclass
from docutils.statemachine import ViewList from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \ from sphinx.ext.autodoc import AutoDirective, add_documenter, \
@ -27,18 +26,23 @@ app = None
@pytest.fixture(scope='module', autouse=True) @pytest.fixture(scope='module', autouse=True)
def setup_module(rootdir, sphinx_test_tempdir): def setup_module(rootdir, sphinx_test_tempdir):
global app try:
srcdir = sphinx_test_tempdir / 'autodoc-root' global app
if not srcdir.exists(): srcdir = sphinx_test_tempdir / 'autodoc-root'
(rootdir/'test-root').copytree(srcdir) if not srcdir.exists():
app = SphinxTestApp(srcdir=srcdir) (rootdir / 'test-root').copytree(srcdir)
app.builder.env.app = app testroot = rootdir / 'test-ext-autodoc'
app.builder.env.temp_data['docname'] = 'dummy' sys.path.append(testroot)
app.connect('autodoc-process-docstring', process_docstring) app = SphinxTestApp(srcdir=srcdir)
app.connect('autodoc-process-signature', process_signature) app.builder.env.app = app
app.connect('autodoc-skip-member', skip_member) app.builder.env.temp_data['docname'] = 'dummy'
yield app.connect('autodoc-process-docstring', process_docstring)
app.cleanup() app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
yield
finally:
app.cleanup()
sys.path.remove(testroot)
directive = options = None directive = options = None
@ -431,6 +435,8 @@ def test_get_doc():
directive.env.config.autoclass_content = 'both' directive.env.config.autoclass_content = 'both'
assert getdocl('class', I) == ['Class docstring', '', 'New docstring'] assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
from target import Base, Derived
# NOTE: inspect.getdoc seems not to work with locally defined classes # NOTE: inspect.getdoc seems not to work with locally defined classes
directive.env.config.autodoc_inherit_docstrings = False directive.env.config.autodoc_inherit_docstrings = False
assert getdocl('method', Base.inheritedmeth) == ['Inherited function.'] assert getdocl('method', Base.inheritedmeth) == ['Inherited function.']
@ -508,24 +514,24 @@ def test_docstring_property_processing():
directive.env.config.autodoc_docstring_signature = False directive.env.config.autodoc_docstring_signature = False
results, docstrings = \ results, docstrings = \
genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop1') genarate_docstring('attribute', 'target.DocstringSig.prop1')
assert '.. py:attribute:: DocstringSig.prop1' in results assert '.. py:attribute:: DocstringSig.prop1' in results
assert 'First line of docstring' in docstrings assert 'First line of docstring' in docstrings
assert 'DocstringSig.prop1(self)' in docstrings assert 'DocstringSig.prop1(self)' in docstrings
results, docstrings = \ results, docstrings = \
genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop2') genarate_docstring('attribute', 'target.DocstringSig.prop2')
assert '.. py:attribute:: DocstringSig.prop2' in results assert '.. py:attribute:: DocstringSig.prop2' in results
assert 'First line of docstring' in docstrings assert 'First line of docstring' in docstrings
assert 'Second line of docstring' in docstrings assert 'Second line of docstring' in docstrings
directive.env.config.autodoc_docstring_signature = True directive.env.config.autodoc_docstring_signature = True
results, docstrings = \ results, docstrings = \
genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop1') genarate_docstring('attribute', 'target.DocstringSig.prop1')
assert '.. py:attribute:: DocstringSig.prop1' in results assert '.. py:attribute:: DocstringSig.prop1' in results
assert 'First line of docstring' in docstrings assert 'First line of docstring' in docstrings
assert 'DocstringSig.prop1(self)' not in docstrings assert 'DocstringSig.prop1(self)' not in docstrings
results, docstrings = \ results, docstrings = \
genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop2') genarate_docstring('attribute', 'target.DocstringSig.prop2')
assert '.. py:attribute:: DocstringSig.prop2' in results assert '.. py:attribute:: DocstringSig.prop2' in results
assert 'First line of docstring' in docstrings assert 'First line of docstring' in docstrings
assert 'Second line of docstring' in docstrings assert 'Second line of docstring' in docstrings
@ -556,11 +562,13 @@ def test_new_documenter():
del directive.result[:] del directive.result[:]
options.members = ['integer'] options.members = ['integer']
assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc') assert_result_contains('.. py:data:: integer', 'module', 'target')
@pytest.mark.usefixtures('setup_test') @pytest.mark.usefixtures('setup_test')
def test_attrgetter_using(): def test_attrgetter_using():
from target import Class
def assert_getter_works(objtype, name, obj, attrs=[], **kw): def assert_getter_works(objtype, name, obj, attrs=[], **kw):
getattr_spy = [] getattr_spy = []
@ -585,10 +593,10 @@ def test_attrgetter_using():
options.members = ALL options.members = ALL
options.inherited_members = False options.inherited_members = False
assert_getter_works('class', 'test_autodoc.Class', Class, ['meth']) assert_getter_works('class', 'target.Class', Class, ['meth'])
options.inherited_members = True options.inherited_members = True
assert_getter_works('class', 'test_autodoc.Class', Class, ['meth', 'inheritedmeth']) assert_getter_works('class', 'target.Class', Class, ['meth', 'inheritedmeth'])
@pytest.mark.usefixtures('setup_test') @pytest.mark.usefixtures('setup_test')
@ -655,11 +663,11 @@ def test_generate():
assert_warns("failed to import function 'foobar' from module 'util'", assert_warns("failed to import function 'foobar' from module 'util'",
'function', 'util.foobar', more_content=None) 'function', 'util.foobar', more_content=None)
# method missing # method missing
assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc';", assert_warns("failed to import method 'Class.foobar' from module 'target';",
'method', 'test_autodoc.Class.foobar', more_content=None) 'method', 'target.Class.foobar', more_content=None)
# test auto and given content mixing # test auto and given content mixing
directive.env.ref_context['py:module'] = 'test_autodoc' directive.env.ref_context['py:module'] = 'target'
assert_result_contains(' Function.', 'method', 'Class.meth') assert_result_contains(' Function.', 'method', 'Class.meth')
add_content = ViewList() add_content = ViewList()
add_content.append('Content.', '', 0) add_content.append('Content.', '', 0)
@ -674,63 +682,63 @@ def test_generate():
assert len(directive.result) == 0 assert len(directive.result) == 0
# assert that exceptions can be documented # assert that exceptions can be documented
assert_works('exception', 'test_autodoc.CustomEx', all_members=True) assert_works('exception', 'target.CustomEx', all_members=True)
assert_works('exception', 'test_autodoc.CustomEx') assert_works('exception', 'target.CustomEx')
# test diverse inclusion settings for members # test diverse inclusion settings for members
should = [('class', 'test_autodoc.Class')] should = [('class', 'target.Class')]
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
should.extend([('method', 'test_autodoc.Class.meth')]) should.extend([('method', 'target.Class.meth')])
options.members = ['meth'] options.members = ['meth']
options.exclude_members = set(['excludemeth']) options.exclude_members = set(['excludemeth'])
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
should.extend([('attribute', 'test_autodoc.Class.prop'), should.extend([('attribute', 'target.Class.prop'),
('attribute', 'test_autodoc.Class.descr'), ('attribute', 'target.Class.descr'),
('attribute', 'test_autodoc.Class.attr'), ('attribute', 'target.Class.attr'),
('attribute', 'test_autodoc.Class.docattr'), ('attribute', 'target.Class.docattr'),
('attribute', 'test_autodoc.Class.udocattr'), ('attribute', 'target.Class.udocattr'),
('attribute', 'test_autodoc.Class.mdocattr'), ('attribute', 'target.Class.mdocattr'),
('attribute', 'test_autodoc.Class.inst_attr_comment'), ('attribute', 'target.Class.inst_attr_comment'),
('attribute', 'test_autodoc.Class.inst_attr_inline'), ('attribute', 'target.Class.inst_attr_inline'),
('attribute', 'test_autodoc.Class.inst_attr_string'), ('attribute', 'target.Class.inst_attr_string'),
('method', 'test_autodoc.Class.moore'), ('method', 'target.Class.moore'),
]) ])
options.members = ALL options.members = ALL
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
options.undoc_members = True options.undoc_members = True
should.extend((('attribute', 'test_autodoc.Class.skipattr'), should.extend((('attribute', 'target.Class.skipattr'),
('method', 'test_autodoc.Class.undocmeth'), ('method', 'target.Class.undocmeth'),
('method', 'test_autodoc.Class.roger'))) ('method', 'target.Class.roger')))
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
options.inherited_members = True options.inherited_members = True
should.append(('method', 'test_autodoc.Class.inheritedmeth')) should.append(('method', 'target.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
# test special members # test special members
options.special_members = ['__special1__'] options.special_members = ['__special1__']
should.append(('method', 'test_autodoc.Class.__special1__')) should.append(('method', 'target.Class.__special1__'))
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
options.special_members = ALL options.special_members = ALL
should.append(('method', 'test_autodoc.Class.__special2__')) should.append(('method', 'target.Class.__special2__'))
assert_processes(should, 'class', 'Class') assert_processes(should, 'class', 'Class')
options.special_members = False options.special_members = False
options.members = [] options.members = []
# test module flags # test module flags
assert_result_contains('.. py:module:: test_autodoc', assert_result_contains('.. py:module:: target',
'module', 'test_autodoc') 'module', 'target')
options.synopsis = 'Synopsis' options.synopsis = 'Synopsis'
assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc') assert_result_contains(' :synopsis: Synopsis', 'module', 'target')
options.deprecated = True options.deprecated = True
assert_result_contains(' :deprecated:', 'module', 'test_autodoc') assert_result_contains(' :deprecated:', 'module', 'target')
options.platform = 'Platform' options.platform = 'Platform'
assert_result_contains(' :platform: Platform', 'module', 'test_autodoc') assert_result_contains(' :platform: Platform', 'module', 'target')
# test if __all__ is respected for modules # test if __all__ is respected for modules
options.members = ALL options.members = ALL
assert_result_contains('.. py:class:: Class(arg)', 'module', 'test_autodoc') assert_result_contains('.. py:class:: Class(arg)', 'module', 'target')
try: try:
assert_result_contains('.. py:exception:: CustomEx', assert_result_contains('.. py:exception:: CustomEx',
'module', 'test_autodoc') 'module', 'target')
except AssertionError: except AssertionError:
pass pass
else: else:
@ -739,7 +747,7 @@ def test_generate():
# test noindex flag # test noindex flag
options.members = [] options.members = []
options.noindex = True options.noindex = True
assert_result_contains(' :noindex:', 'module', 'test_autodoc') assert_result_contains(' :noindex:', 'module', 'target')
assert_result_contains(' :noindex:', 'class', 'Base') assert_result_contains(' :noindex:', 'class', 'Base')
# okay, now let's get serious about mixing Python and C signature stuff # okay, now let's get serious about mixing Python and C signature stuff
@ -747,14 +755,14 @@ def test_generate():
all_members=True) all_members=True)
# test inner class handling # test inner class handling
assert_processes([('class', 'test_autodoc.Outer'), assert_processes([('class', 'target.Outer'),
('class', 'test_autodoc.Outer.Inner'), ('class', 'target.Outer.Inner'),
('method', 'test_autodoc.Outer.Inner.meth')], ('method', 'target.Outer.Inner.meth')],
'class', 'Outer', all_members=True) 'class', 'Outer', all_members=True)
# test descriptor docstrings # test descriptor docstrings
assert_result_contains(' Descriptor instance docstring.', assert_result_contains(' Descriptor instance docstring.',
'attribute', 'test_autodoc.Class.descr') 'attribute', 'target.Class.descr')
# test generation for C modules (which have no source file) # test generation for C modules (which have no source file)
directive.env.ref_context['py:module'] = 'time' directive.env.ref_context['py:module'] = 'time'
@ -762,7 +770,7 @@ def test_generate():
assert_processes([('function', 'time.asctime')], 'function', 'asctime') assert_processes([('function', 'time.asctime')], 'function', 'asctime')
# test autodoc_member_order == 'source' # test autodoc_member_order == 'source'
directive.env.ref_context['py:module'] = 'test_autodoc' directive.env.ref_context['py:module'] = 'target'
options.private_members = True options.private_members = True
if PY3: if PY3:
roger_line = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)' roger_line = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
@ -788,7 +796,7 @@ def test_generate():
del directive.env.ref_context['py:module'] del directive.env.ref_context['py:module']
# test attribute initialized to class instance from other module # test attribute initialized to class instance from other module
directive.env.temp_data['autodoc:class'] = 'test_autodoc.Class' directive.env.temp_data['autodoc:class'] = 'target.Class'
assert_result_contains(u' should be documented as well - s\xfc\xdf', assert_result_contains(u' should be documented as well - s\xfc\xdf',
'attribute', 'mdocattr') 'attribute', 'mdocattr')
del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:class']
@ -796,25 +804,25 @@ def test_generate():
# test autodoc_docstring_signature # test autodoc_docstring_signature
assert_result_contains( assert_result_contains(
'.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method', '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method',
'test_autodoc.DocstringSig.meth') 'target.DocstringSig.meth')
assert_result_contains( assert_result_contains(
' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth') ' rest of docstring', 'method', 'target.DocstringSig.meth')
assert_result_contains( assert_result_contains(
'.. py:method:: DocstringSig.meth2()', 'method', '.. py:method:: DocstringSig.meth2()', 'method',
'test_autodoc.DocstringSig.meth2') 'target.DocstringSig.meth2')
assert_result_contains( assert_result_contains(
' indented line', 'method', ' indented line', 'method',
'test_autodoc.DocstringSig.meth2') 'target.DocstringSig.meth2')
assert_result_contains( assert_result_contains(
'.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method', '.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
'test_autodoc.Class.moore') 'target.Class.moore')
# test new attribute documenter behavior # test new attribute documenter behavior
directive.env.ref_context['py:module'] = 'test_autodoc' directive.env.ref_context['py:module'] = 'target'
options.undoc_members = True options.undoc_members = True
assert_processes([('class', 'test_autodoc.AttCls'), assert_processes([('class', 'target.AttCls'),
('attribute', 'test_autodoc.AttCls.a1'), ('attribute', 'target.AttCls.a1'),
('attribute', 'test_autodoc.AttCls.a2'), ('attribute', 'target.AttCls.a2'),
], 'class', 'AttCls') ], 'class', 'AttCls')
assert_result_contains( assert_result_contains(
' :annotation: = hello world', 'attribute', 'AttCls.a1') ' :annotation: = hello world', 'attribute', 'AttCls.a1')
@ -824,40 +832,40 @@ def test_generate():
# test explicit members with instance attributes # test explicit members with instance attributes
del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module'] del directive.env.temp_data['autodoc:module']
directive.env.ref_context['py:module'] = 'test_autodoc' directive.env.ref_context['py:module'] = 'target'
options.inherited_members = False options.inherited_members = False
options.undoc_members = False options.undoc_members = False
options.members = ALL options.members = ALL
assert_processes([ assert_processes([
('class', 'test_autodoc.InstAttCls'), ('class', 'target.InstAttCls'),
('attribute', 'test_autodoc.InstAttCls.ca1'), ('attribute', 'target.InstAttCls.ca1'),
('attribute', 'test_autodoc.InstAttCls.ca2'), ('attribute', 'target.InstAttCls.ca2'),
('attribute', 'test_autodoc.InstAttCls.ca3'), ('attribute', 'target.InstAttCls.ca3'),
('attribute', 'test_autodoc.InstAttCls.ia1'), ('attribute', 'target.InstAttCls.ia1'),
('attribute', 'test_autodoc.InstAttCls.ia2'), ('attribute', 'target.InstAttCls.ia2'),
], 'class', 'InstAttCls') ], 'class', 'InstAttCls')
del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module'] del directive.env.temp_data['autodoc:module']
options.members = ['ca1', 'ia1'] options.members = ['ca1', 'ia1']
assert_processes([ assert_processes([
('class', 'test_autodoc.InstAttCls'), ('class', 'target.InstAttCls'),
('attribute', 'test_autodoc.InstAttCls.ca1'), ('attribute', 'target.InstAttCls.ca1'),
('attribute', 'test_autodoc.InstAttCls.ia1'), ('attribute', 'target.InstAttCls.ia1'),
], 'class', 'InstAttCls') ], 'class', 'InstAttCls')
del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module'] del directive.env.temp_data['autodoc:module']
del directive.env.ref_context['py:module'] del directive.env.ref_context['py:module']
# test members with enum attributes # test members with enum attributes
directive.env.ref_context['py:module'] = 'test_autodoc' directive.env.ref_context['py:module'] = 'target'
options.inherited_members = False options.inherited_members = False
options.undoc_members = False options.undoc_members = False
options.members = ALL options.members = ALL
assert_processes([ assert_processes([
('class', 'test_autodoc.EnumCls'), ('class', 'target.EnumCls'),
('attribute', 'test_autodoc.EnumCls.val1'), ('attribute', 'target.EnumCls.val1'),
('attribute', 'test_autodoc.EnumCls.val2'), ('attribute', 'target.EnumCls.val2'),
('attribute', 'test_autodoc.EnumCls.val3'), ('attribute', 'target.EnumCls.val3'),
], 'class', 'EnumCls') ], 'class', 'EnumCls')
assert_result_contains( assert_result_contains(
' :annotation: = 12', 'attribute', 'EnumCls.val1') ' :annotation: = 12', 'attribute', 'EnumCls.val1')
@ -871,11 +879,11 @@ def test_generate():
# test descriptor class documentation # test descriptor class documentation
options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2'] options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2']
assert_result_contains('.. py:class:: CustomDataDescriptor(doc)', assert_result_contains('.. py:class:: CustomDataDescriptor(doc)',
'module', 'test_autodoc') 'module', 'target')
assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()', assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()',
'module', 'test_autodoc') 'module', 'target')
assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)', assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)',
'module', 'test_autodoc') 'module', 'target')
# test mocked module imports # test mocked module imports
options.members = ['TestAutodoc'] options.members = ['TestAutodoc']
@ -887,224 +895,3 @@ def test_generate():
options.members = ['decoratedFunction'] options.members = ['decoratedFunction']
assert_result_contains('.. py:function:: decoratedFunction()', assert_result_contains('.. py:function:: decoratedFunction()',
'module', 'autodoc_missing_imports') 'module', 'autodoc_missing_imports')
# --- generate fodder ------------
__all__ = ['Class']
#: documentation for the integer
integer = 1
def raises(exc, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
pass
class CustomEx(Exception):
"""My custom exception."""
def f(self):
"""Exception method."""
class CustomDataDescriptor(object):
"""Descriptor class docstring."""
def __init__(self, doc):
self.__doc__ = doc
def __get__(self, obj, type=None):
if obj is None:
return self
return 42
def meth(self):
"""Function."""
return "The Answer"
class CustomDataDescriptorMeta(type):
"""Descriptor metaclass docstring."""
@add_metaclass(CustomDataDescriptorMeta)
class CustomDataDescriptor2(CustomDataDescriptor):
"""Descriptor class with custom metaclass docstring."""
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
from functools import partial
function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
class Derived(Base):
def inheritedmeth(self):
# no docstring here
pass
class Class(Base):
"""Class to document."""
descr = CustomDataDescriptor("Descriptor instance docstring.")
def meth(self):
"""Function."""
def undocmeth(self):
pass
def skipmeth(self):
"""Method that should be skipped."""
def excludemeth(self):
"""Method that should be excluded."""
# should not be documented
skipattr = 'foo'
#: should be documented -- süß
attr = 'bar'
@property
def prop(self):
"""Property."""
docattr = 'baz'
"""should likewise be documented -- süß"""
udocattr = 'quux'
u"""should be documented as well - süß"""
# initialized to any class imported from another module
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
self.inst_attr_inline = None #: an inline documented instance attr
#: a documented instance attribute
self.inst_attr_comment = None
self.inst_attr_string = None
"""a documented instance attribute"""
self._private_inst_attr = None #: a private instance attribute
def __special1__(self):
"""documented special method"""
def __special2__(self):
# undocumented special method
pass
class CustomDict(dict):
"""Docstring."""
def function(foo, *args, **kwds):
"""
Return spam.
"""
pass
class Outer(object):
"""Foo"""
class Inner(object):
"""Foo"""
def meth(self):
"""Foo"""
# should be documented as an alias
factory = dict
class DocstringSig(object):
def meth(self):
"""meth(FOO, BAR=1) -> BAZ
First line of docstring
rest of docstring
"""
def meth2(self):
"""First line, no signature
Second line followed by indentation::
indented line
"""
@property
def prop1(self):
"""DocstringSig.prop1(self)
First line of docstring
"""
return 123
@property
def prop2(self):
"""First line of docstring
Second line of docstring
"""
return 456
class StrRepr(str):
def __repr__(self):
return self
class AttCls(object):
a1 = StrRepr('hello\nworld')
a2 = None
class InstAttCls(object):
"""Class with documented class and instance attributes."""
#: Doc comment for class attribute InstAttCls.ca1.
#: It can have multiple lines.
ca1 = 'a'
ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
ca3 = 'c'
"""Docstring for class attribute InstAttCls.ca3."""
def __init__(self):
#: Doc comment for instance attribute InstAttCls.ia1
self.ia1 = 'd'
self.ia2 = 'e'
"""Docstring for instance attribute InstAttCls.ia2."""
class EnumCls(enum.Enum):
"""
this is enum class
"""
#: doc for val1
val1 = 12
val2 = 23 #: doc for val2
val3 = 34
"""doc for val3"""

View File

@ -182,8 +182,8 @@ def test_html_warnings(app, warning):
r'-| |-'), r'-| |-'),
], ],
'autodoc.html': [ 'autodoc.html': [
(".//dt[@id='test_autodoc.Class']", ''), (".//dt[@id='autodoc_target.Class']", ''),
(".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'), (".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
(".//dd/p", r'Return spam\.'), (".//dd/p", r'Return spam\.'),
], ],
'extapi.html': [ 'extapi.html': [

View File

@ -90,8 +90,8 @@ def cached_etree_parse():
r'-| |-'), r'-| |-'),
], ],
'autodoc.html': [ 'autodoc.html': [
(".//dt[@id='test_autodoc.Class']", ''), (".//dt[@id='autodoc_target.Class']", ''),
(".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'), (".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
(".//dd/p", r'Return spam\.'), (".//dd/p", r'Return spam\.'),
], ],
'extapi.html': [ 'extapi.html': [

View File

@ -21,9 +21,9 @@ def test_build(app, status, warning):
py_undoc = (app.outdir / 'python.txt').text() py_undoc = (app.outdir / 'python.txt').text()
assert py_undoc.startswith('Undocumented Python objects\n' assert py_undoc.startswith('Undocumented Python objects\n'
'===========================\n') '===========================\n')
assert 'test_autodoc\n------------\n' in py_undoc assert 'autodoc_target\n--------------\n' in py_undoc
assert ' * Class -- missing methods:\n' in py_undoc assert ' * Class -- missing methods:\n' in py_undoc
assert ' * process_docstring\n' in py_undoc assert ' * raises\n' in py_undoc
assert ' * function\n' not in py_undoc # these two are documented assert ' * function\n' not in py_undoc # these two are documented
assert ' * Class\n' not in py_undoc # in autodoc.txt assert ' * Class\n' not in py_undoc # in autodoc.txt
@ -40,9 +40,9 @@ def test_build(app, status, warning):
# the key is the full path to the header file, which isn't testable # the key is the full path to the header file, which isn't testable
assert list(undoc_c.values())[0] == set([('function', 'Py_SphinxTest')]) assert list(undoc_c.values())[0] == set([('function', 'Py_SphinxTest')])
assert 'test_autodoc' in undoc_py assert 'autodoc_target' in undoc_py
assert 'funcs' in undoc_py['test_autodoc'] assert 'funcs' in undoc_py['autodoc_target']
assert 'process_docstring' in undoc_py['test_autodoc']['funcs'] assert 'raises' in undoc_py['autodoc_target']['funcs']
assert 'classes' in undoc_py['test_autodoc'] assert 'classes' in undoc_py['autodoc_target']
assert 'Class' in undoc_py['test_autodoc']['classes'] assert 'Class' in undoc_py['autodoc_target']['classes']
assert 'undocmeth' in undoc_py['test_autodoc']['classes']['Class'] assert 'undocmeth' in undoc_py['autodoc_target']['classes']['Class']