Closes #4520 - acidic: Subpackage not in toc. The rule of skipping folders with only an empty __init__.py has been removed. The reason for this is that it was never working consistently in the first place and made the code unnecessary hard to reason about. Tests for the TOC generation have been added, as well as tests for the exclude mechanism since they are coupled. One test (test_ext_apidoc.py::test_exclude) has also been modified to reflect the new behaviour.

This commit is contained in:
Christer Bystrom 2018-02-24 11:44:29 +01:00 committed by Takeshi KOMIYA
parent ad08b5b409
commit a7aac6956d
6 changed files with 81 additions and 11 deletions

View File

@ -6,6 +6,7 @@ Dependencies
Incompatible changes Incompatible changes
-------------------- --------------------
* apidoc: As a consequence of a bug fix (#4520#) and cleaning up the code, folders with an empty __init__.py are no longer excluded from TOC.
Deprecated Deprecated
---------- ----------
@ -34,6 +35,7 @@ Bugs fixed
* #4754: sphinx/pycode/__init__.py raises AttributeError * #4754: sphinx/pycode/__init__.py raises AttributeError
* #1435: qthelp builder should htmlescape keywords * #1435: qthelp builder should htmlescape keywords
* epub: Fix docTitle elements of toc.ncx is not escaped * epub: Fix docTitle elements of toc.ncx is not escaped
* #4520#: apidoc: Subpackage not in toc (introduced in 1.6.6) now fixed.
Testing Testing
-------- --------
@ -41,6 +43,9 @@ Testing
Release 1.7.1 (released Feb 23, 2018) Release 1.7.1 (released Feb 23, 2018)
===================================== =====================================
Dependencies
------------
Deprecated Deprecated
---------- ----------

View File

@ -194,16 +194,15 @@ def shall_skip(module, opts, excludes=[]):
if not opts.implicit_namespaces and not path.exists(module): if not opts.implicit_namespaces and not path.exists(module):
return True return True
# skip it if there is nothing (or just \n or \r\n) in the file # Are we a package (here defined as __init__.py, not the folder in itself)
if path.exists(module) and path.getsize(module) <= 2: if os.path.basename(module) == INITPY:
if os.path.basename(module) == '__init__.py': # Yes, check if we have any non-excluded modules at all here
# We only want to skip packages if they do not contain any
# .py files other than __init__.py.
basemodule = path.dirname(module) basemodule = path.dirname(module)
for module in glob.glob(path.join(basemodule, '*.py')): for module in glob.glob(path.join(basemodule, '*.py')):
if not is_excluded(path.join(basemodule, module), excludes): if not is_excluded(path.join(basemodule, module), excludes):
return True # There's a non-excluded module here, we won't skip
else: all_skipped = False
if all_skipped:
return True return True
# skip if it has a "private" name and this is selected # skip if it has a "private" name and this is selected

View File

@ -0,0 +1 @@
"foo"

View File

@ -211,7 +211,7 @@ def test_trailing_underscore(make_app, apidoc):
@pytest.mark.apidoc( @pytest.mark.apidoc(
coderoot='test-apidoc-pep420/a', coderoot='test-apidoc-pep420/a',
excludes=["b/c/d.py", "b/e/f.py"], excludes=["b/c/d.py", "b/e/f.py", "b/e/__init__.py"],
options=["--implicit-namespaces", "--separate"], options=["--implicit-namespaces", "--separate"],
) )
def test_excludes(apidoc): def test_excludes(apidoc):
@ -223,6 +223,45 @@ def test_excludes(apidoc):
assert (outdir / 'a.b.x.y.rst').isfile() assert (outdir / 'a.b.x.y.rst').isfile()
@pytest.mark.apidoc(
coderoot='test-apidoc-pep420/a',
excludes=["b/e"],
options=["--implicit-namespaces", "--separate"],
)
def test_excludes_subpackage_should_be_skipped(apidoc):
"""Subpackage exclusion should work."""
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty
assert not (outdir / 'a.b.e.f.rst').isfile() # skipped because 'b/e' subpackage is skipped
@pytest.mark.apidoc(
coderoot='test-apidoc-pep420/a',
excludes=["b/e/f.py"],
options=["--implicit-namespaces", "--separate"],
)
def test_excludes_module_should_be_skipped(apidoc):
"""Module exclusion should work."""
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty
assert not (outdir / 'a.b.e.f.rst').isfile() # skipped because of empty after excludes
@pytest.mark.apidoc(
coderoot='test-apidoc-pep420/a',
excludes=[],
options=["--implicit-namespaces", "--separate"],
)
def test_excludes_module_should_not_be_skipped(apidoc):
"""Module should be included if no excludes are used."""
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty
assert (outdir / 'a.b.e.f.rst').isfile() # skipped because of empty after excludes
@pytest.mark.apidoc( @pytest.mark.apidoc(
coderoot='test-root', coderoot='test-root',
options=[ options=[
@ -339,3 +378,29 @@ def extract_toc(path):
toctree = rst[start_idx + len(toctree_start):end_idx] toctree = rst[start_idx + len(toctree_start):end_idx]
return toctree return toctree
@pytest.mark.apidoc(
coderoot='test-apidoc-subpackage-in-toc',
options=['--separate']
)
def test_subpackage_in_toc(make_app, apidoc):
"""Make sure that empty subpackages with non-empty subpackages in them
are not skipped (issue #4520)
"""
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'parent.rst').isfile()
with open(outdir / 'parent.rst') as f:
parent = f.read()
assert 'parent.child' in parent
assert (outdir / 'parent.child.rst').isfile()
with open(outdir / 'parent.child.rst') as f:
parent_child = f.read()
assert 'parent.child.foo' in parent_child
assert (outdir / 'parent.child.foo.rst').isfile()