Fix #7844: autodoc: Failed to detect module when relative module name given

This commit is contained in:
Takeshi KOMIYA 2020-06-22 01:33:41 +09:00
parent 0e271f28a4
commit e860903cd8
4 changed files with 82 additions and 23 deletions

View File

@ -16,6 +16,8 @@ Features added
Bugs fixed
----------
* #7844: autodoc: Failed to detect module when relative module name given
Testing
--------

View File

@ -977,19 +977,29 @@ class ModuleLevelDocumenter(Documenter):
if path:
stripped = path.rstrip('.')
modname, qualname = split_full_qualified_name(stripped)
if modname is None:
# if documenting a toplevel object without explicit module,
# it can be contained in another auto directive ...
modname = self.env.temp_data.get('autodoc:module')
# ... or in the scope of a module directive
if not modname:
modname = self.env.ref_context.get('py:module')
if modname:
stripped = ".".join([modname, stripped])
modname, qualname = split_full_qualified_name(stripped)
if qualname:
parents = qualname.split(".")
else:
parents = []
if modname is None:
# if documenting a toplevel object without explicit module,
# it can be contained in another auto directive ...
else:
modname = self.env.temp_data.get('autodoc:module')
# ... or in the scope of a module directive
parents = []
if not modname:
modname = self.env.ref_context.get('py:module')
# ... else, it stays None, which means invalid
return modname, parents + [base]
@ -1016,18 +1026,24 @@ class ClassLevelDocumenter(Documenter):
if mod_cls is None:
return None, []
try:
modname, qualname = split_full_qualified_name(mod_cls)
parents = qualname.split(".") if qualname else []
except ImportError:
parents = mod_cls.split(".")
# if the module name is still missing, get it like above
if not modname:
modname, qualname = split_full_qualified_name(mod_cls)
if modname is None:
# if documenting a toplevel object without explicit module,
# it can be contained in another auto directive ...
modname = self.env.temp_data.get('autodoc:module')
if not modname:
modname = self.env.ref_context.get('py:module')
# ... else, it stays None, which means invalid
# ... or in the scope of a module directive
if not modname:
modname = self.env.ref_context.get('py:module')
if modname:
stripped = ".".join([modname, mod_cls])
modname, qualname = split_full_qualified_name(stripped)
if qualname:
parents = qualname.split(".")
else:
parents = []
return modname, parents + [base]

View File

@ -634,7 +634,10 @@ def split_full_qualified_name(name: str) -> Tuple[str, str]:
if not inspect.ismodule(value):
return ".".join(parts[:i]), ".".join(parts[i:])
except ImportError:
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
if parts[:i - 1]:
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
else:
return None, ".".join(parts)
except IndexError:
pass

View File

@ -121,15 +121,16 @@ def test_parse_name(app):
verify('class', 'Base', ('test_ext_autodoc', ['Base'], None, None))
# for members
directive.env.ref_context['py:module'] = 'foo'
directive.env.ref_context['py:module'] = 'sphinx.testing'
verify('method', 'util.SphinxTestApp.cleanup',
('foo', ['util', 'SphinxTestApp', 'cleanup'], None, None))
directive.env.ref_context['py:module'] = 'util'
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
directive.env.ref_context['py:module'] = 'sphinx.testing.util'
directive.env.ref_context['py:class'] = 'Foo'
directive.env.temp_data['autodoc:class'] = 'SphinxTestApp'
verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None))
verify('method', 'cleanup',
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
verify('method', 'SphinxTestApp.cleanup',
('util', ['SphinxTestApp', 'cleanup'], None, None))
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
def test_format_signature(app):
@ -1881,6 +1882,43 @@ def test_overload(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_pymodule_for_ModuleLevelDocumenter(app):
app.env.ref_context['py:module'] = 'target'
actual = do_autodoc(app, 'class', 'classes.Foo')
assert list(actual) == [
'',
'.. py:class:: Foo()',
' :module: target.classes',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_pymodule_for_ClassLevelDocumenter(app):
app.env.ref_context['py:module'] = 'target'
actual = do_autodoc(app, 'method', 'methods.Base.meth')
assert list(actual) == [
'',
'.. py:method:: Base.meth()',
' :module: target.methods',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_pyclass_for_ClassLevelDocumenter(app):
app.env.ref_context['py:module'] = 'target.methods'
app.env.ref_context['py:class'] = 'Base'
actual = do_autodoc(app, 'method', 'meth')
assert list(actual) == [
'',
'.. py:method:: Base.meth()',
' :module: target.methods',
'',
]
@pytest.mark.sphinx('dummy', testroot='ext-autodoc')
def test_autodoc(app, status, warning):
app.builder.build_all()