From 3e7bee583604e3422c05f807a69d0a73afedfa59 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 23 Mar 2017 00:21:04 +0000 Subject: [PATCH 1/3] Fix issues with trailing underscores in heading names Fixes #1451, using the approach in 8d96c90fc6ca7805ba7a8e8fd80cc74da7caf46c --- sphinx/apidoc.py | 7 +++++-- .../root/trailing_underscore/package_/__init__.py | 1 + .../root/trailing_underscore/package_/module_.py | 7 +++++++ tests/test_apidoc.py | 15 +++++++++++++-- 4 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 tests/root/trailing_underscore/package_/__init__.py create mode 100644 tests/root/trailing_underscore/package_/module_.py diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py index 19a711370..a8e819acd 100644 --- a/sphinx/apidoc.py +++ b/sphinx/apidoc.py @@ -25,6 +25,7 @@ from fnmatch import fnmatch from sphinx.util.osutil import FileAvoidWrite, walk from sphinx import __display_version__ +from sphinx.util import rst # automodule options if 'SPHINX_APIDOC_OPTIONS' in os.environ: @@ -67,8 +68,10 @@ def write_file(name, text, opts): f.write(text) -def format_heading(level, text): +def format_heading(level, text, escape=True): """Create a heading of [1, 2 or 3 supported].""" + if escape: + text = rst.escape(text) underlining = ['=', '-', '~', ][level - 1] * len(text) return '%s\n%s\n\n' % (text, underlining) @@ -149,7 +152,7 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_ def create_modules_toc_file(modules, opts, name='modules'): """Create the module's index.""" - text = format_heading(1, '%s' % opts.header) + text = format_heading(1, '%s' % opts.header, escape=False) text += '.. toctree::\n' text += ' :maxdepth: %s\n\n' % opts.maxdepth diff --git a/tests/root/trailing_underscore/package_/__init__.py b/tests/root/trailing_underscore/package_/__init__.py new file mode 100644 index 000000000..b09612b83 --- /dev/null +++ b/tests/root/trailing_underscore/package_/__init__.py @@ -0,0 +1 @@ +""" A package with trailing underscores """ diff --git a/tests/root/trailing_underscore/package_/module_.py b/tests/root/trailing_underscore/package_/module_.py new file mode 100644 index 000000000..9902551ee --- /dev/null +++ b/tests/root/trailing_underscore/package_/module_.py @@ -0,0 +1,7 @@ +""" A module with a trailing underscore """ + +class SomeClass_: + """ A class with a trailing underscore """ + +def some_function_(some_arg_): + """ A function with a trailing underscore in name and argument """ diff --git a/tests/test_apidoc.py b/tests/test_apidoc.py index d44868aeb..d161fcd05 100644 --- a/tests/test_apidoc.py +++ b/tests/test_apidoc.py @@ -19,6 +19,8 @@ from sphinx.apidoc import main as apidoc_main from util import rootdir, remove_unicode_literals +from sphinx.util.rst import escape as rst_escape + @pytest.fixture() def apidoc(tempdir, apidoc_params): @@ -71,13 +73,13 @@ def test_pep_0420_enabled(make_app, apidoc): with open(outdir / 'a.b.c.rst') as f: rst = f.read() - assert "a.b.c package\n" in rst + assert rst_escape("a.b.c package\n") in rst assert "automodule:: a.b.c.d\n" in rst assert "automodule:: a.b.c\n" in rst with open(outdir / 'a.b.x.rst') as f: rst = f.read() - assert "a.b.x namespace\n" in rst + assert rst_escape("a.b.x namespace\n") in rst assert "automodule:: a.b.x.y\n" in rst assert "automodule:: a.b.x\n" not in rst @@ -118,6 +120,15 @@ def test_pep_0420_disabled_top_level_verify(make_app, apidoc): print(app._status.getvalue()) print(app._warning.getvalue()) +@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'trailing_underscore')) +def test_trailing_underscore(make_app, apidoc): + outdir = apidoc.outdir + assert (outdir / 'conf.py').isfile() + assert (outdir / 'package_.rst').isfile() + with open(outdir / 'package_.rst') as f: + rst = f.read() + assert rst_escape("package_ package\n") in rst + assert rst_escape("package_.module_ module\n") in rst @pytest.mark.apidoc( coderoot=(rootdir / 'root'), From 18d2e2e48be136a38cf39bc0640db893e72587b5 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 24 Mar 2017 09:44:36 +0000 Subject: [PATCH 2/3] Move apidoc tests to subfolder, rather than polluting main test --- .../test-apidoc-pep420}/a/b/c/__init__.py | 0 .../pep_0420 => roots/test-apidoc-pep420}/a/b/c/d.py | 0 .../pep_0420 => roots/test-apidoc-pep420}/a/b/x/y.py | 0 .../package_/__init__.py | 0 .../package_/module_.py | 0 tests/test_apidoc.py | 10 ++++++---- 6 files changed, 6 insertions(+), 4 deletions(-) rename tests/{root/pep_0420 => roots/test-apidoc-pep420}/a/b/c/__init__.py (100%) rename tests/{root/pep_0420 => roots/test-apidoc-pep420}/a/b/c/d.py (100%) rename tests/{root/pep_0420 => roots/test-apidoc-pep420}/a/b/x/y.py (100%) rename tests/{root/trailing_underscore => roots/test-apidoc-trailing-underscore}/package_/__init__.py (100%) rename tests/{root/trailing_underscore => roots/test-apidoc-trailing-underscore}/package_/module_.py (100%) diff --git a/tests/root/pep_0420/a/b/c/__init__.py b/tests/roots/test-apidoc-pep420/a/b/c/__init__.py similarity index 100% rename from tests/root/pep_0420/a/b/c/__init__.py rename to tests/roots/test-apidoc-pep420/a/b/c/__init__.py diff --git a/tests/root/pep_0420/a/b/c/d.py b/tests/roots/test-apidoc-pep420/a/b/c/d.py similarity index 100% rename from tests/root/pep_0420/a/b/c/d.py rename to tests/roots/test-apidoc-pep420/a/b/c/d.py diff --git a/tests/root/pep_0420/a/b/x/y.py b/tests/roots/test-apidoc-pep420/a/b/x/y.py similarity index 100% rename from tests/root/pep_0420/a/b/x/y.py rename to tests/roots/test-apidoc-pep420/a/b/x/y.py diff --git a/tests/root/trailing_underscore/package_/__init__.py b/tests/roots/test-apidoc-trailing-underscore/package_/__init__.py similarity index 100% rename from tests/root/trailing_underscore/package_/__init__.py rename to tests/roots/test-apidoc-trailing-underscore/package_/__init__.py diff --git a/tests/root/trailing_underscore/package_/module_.py b/tests/roots/test-apidoc-trailing-underscore/package_/module_.py similarity index 100% rename from tests/root/trailing_underscore/package_/module_.py rename to tests/roots/test-apidoc-trailing-underscore/package_/module_.py diff --git a/tests/test_apidoc.py b/tests/test_apidoc.py index d161fcd05..313ee9844 100644 --- a/tests/test_apidoc.py +++ b/tests/test_apidoc.py @@ -62,7 +62,7 @@ def test_simple(make_app, apidoc): @pytest.mark.apidoc( - coderoot=(rootdir / 'root' / 'pep_0420'), + coderoot=(rootdir / 'roots' / 'test-apidoc-pep420'), options=["--implicit-namespaces"], ) def test_pep_0420_enabled(make_app, apidoc): @@ -89,7 +89,7 @@ def test_pep_0420_enabled(make_app, apidoc): print(app._warning.getvalue()) -@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'pep_0420')) +@pytest.mark.apidoc(coderoot=(rootdir / 'roots' / 'test-apidoc-pep420')) def test_pep_0420_disabled(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() @@ -102,7 +102,8 @@ def test_pep_0420_disabled(make_app, apidoc): print(app._warning.getvalue()) -@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'pep_0420' / 'a' / 'b')) +@pytest.mark.apidoc( + coderoot=(rootdir / 'roots' / 'test-apidoc-pep420' / 'a' / 'b')) def test_pep_0420_disabled_top_level_verify(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() @@ -120,7 +121,8 @@ def test_pep_0420_disabled_top_level_verify(make_app, apidoc): print(app._status.getvalue()) print(app._warning.getvalue()) -@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'trailing_underscore')) +@pytest.mark.apidoc( + coderoot=(rootdir / 'roots' / 'test-apidoc-trailing-underscore')) def test_trailing_underscore(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() From a7397a21fe2079ae2727ac987221d015b424c38a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 24 Mar 2017 09:53:01 +0000 Subject: [PATCH 3/3] Test whether the escaping is correct by looking at the plaintext Otherwise we're just testing that the headers are produced with format_header --- tests/test_apidoc.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/test_apidoc.py b/tests/test_apidoc.py index 313ee9844..396e04ec3 100644 --- a/tests/test_apidoc.py +++ b/tests/test_apidoc.py @@ -19,8 +19,6 @@ from sphinx.apidoc import main as apidoc_main from util import rootdir, remove_unicode_literals -from sphinx.util.rst import escape as rst_escape - @pytest.fixture() def apidoc(tempdir, apidoc_params): @@ -73,13 +71,11 @@ def test_pep_0420_enabled(make_app, apidoc): with open(outdir / 'a.b.c.rst') as f: rst = f.read() - assert rst_escape("a.b.c package\n") in rst assert "automodule:: a.b.c.d\n" in rst assert "automodule:: a.b.c\n" in rst with open(outdir / 'a.b.x.rst') as f: rst = f.read() - assert rst_escape("a.b.x namespace\n") in rst assert "automodule:: a.b.x.y\n" in rst assert "automodule:: a.b.x\n" not in rst @@ -88,6 +84,18 @@ def test_pep_0420_enabled(make_app, apidoc): print(app._status.getvalue()) print(app._warning.getvalue()) + builddir = outdir / '_build' / 'text' + assert (builddir / 'a.b.c.txt').isfile() + assert (builddir / 'a.b.x.txt').isfile() + + with open(builddir / 'a.b.c.txt') as f: + txt = f.read() + assert "a.b.c package\n" in txt + + with open(builddir / 'a.b.x.txt') as f: + txt = f.read() + assert "a.b.x namespace\n" in txt + @pytest.mark.apidoc(coderoot=(rootdir / 'roots' / 'test-apidoc-pep420')) def test_pep_0420_disabled(make_app, apidoc): @@ -127,10 +135,17 @@ def test_trailing_underscore(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() assert (outdir / 'package_.rst').isfile() - with open(outdir / 'package_.rst') as f: + + app = make_app('text', srcdir=outdir) + app.build() + print(app._status.getvalue()) + print(app._warning.getvalue()) + + builddir = outdir / '_build' / 'text' + with open(builddir / 'package_.txt') as f: rst = f.read() - assert rst_escape("package_ package\n") in rst - assert rst_escape("package_.module_ module\n") in rst + assert "package_ package\n" in rst + assert "package_.module_ module\n" in rst @pytest.mark.apidoc( coderoot=(rootdir / 'root'),