Source code for tests.sphinx.test_sphinx_viewcode

# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""Tests for the `sphinx.ext.viewcode` pages a build publishes."""

from __future__ import annotations

import subprocess
import sys
from io import StringIO
from pathlib import Path

import pytest
from sphinx.application import Sphinx
from sphinx.util.docutils import docutils_namespace
from sphinx.util.parallel import parallel_available

FRUITS_MODULE = '''\
import re

SEED = re.compile(r"\\d+")
"""Pattern of a seed count."""


def ripen(fruit):
    """Ripen a fruit."""
'''
"""A module documenting a constant whose value is an instance of `re.Pattern`.

viewcode looks for `SEED` in the module of that class, `re`, and finds nothing
to link to there.
"""

PUBLISHED_PAGES = ["_modules/fruits.html", "_modules/index.html"]
"""The module page of `fruits`, and the index linking to it."""


[docs] def write_project(srcdir: Path) -> None: """Write a project whose last document read documents `fruits`. No later document purges the unused `re` entry, so a serial read keeps it too. """ srcdir.mkdir() (srcdir / "fruits.py").write_text(FRUITS_MODULE, encoding="UTF-8") extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "click_extra.sphinx"] (srcdir / "conf.py").write_text(f"extensions = {extensions!r}", encoding="UTF-8") (srcdir / "index.rst").write_text( "Orchard\n=======\n\n.. toctree::\n\n pears\n plums\n", encoding="UTF-8" ) (srcdir / "pears.rst").write_text("Pears\n=====\n\nPears.\n", encoding="UTF-8") (srcdir / "plums.rst").write_text( "Plums\n=====\n\n.. automodule:: fruits\n :members:\n", encoding="UTF-8" )
[docs] def module_pages(outdir: Path) -> list[str]: """List the viewcode pages of a build, relative to its output directory.""" return sorted( path.relative_to(outdir).as_posix() for path in (outdir / "_modules").rglob("*.html") )
[docs] def test_serial_read_publishes_linked_modules_only(tmp_path, monkeypatch): """A serial read publishes no page for the module it read for nothing.""" srcdir = tmp_path / "source" write_project(srcdir) monkeypatch.syspath_prepend(str(srcdir)) monkeypatch.delitem(sys.modules, "fruits", raising=False) outdir = tmp_path / "build" with docutils_namespace(): app = Sphinx( str(srcdir), str(srcdir), str(outdir), str(outdir / ".doctrees"), "html", status=StringIO(), warning=StringIO(), ) app.build() assert module_pages(outdir) == PUBLISHED_PAGES assert "viewcode-back" in (outdir / "_modules" / "fruits.html").read_text( encoding="UTF-8" )
[docs] @pytest.mark.skipif(not parallel_available, reason="Sphinx reads in parallel on POSIX") def test_parallel_read_publishes_the_pages_of_a_serial_one(tmp_path): """A parallel read publishes the same pages as a serial one. Its workers merge their entries after every purge has run, which is the case of [sphinx-doc/sphinx#14675](https://github.com/sphinx-doc/sphinx/issues/14675). The build runs in a process of its own: forking the test process, which runs other threads, can deadlock the workers. """ srcdir = tmp_path / "source" write_project(srcdir) outdir = tmp_path / "build" # `python -m` puts the working directory first on `sys.path`, which makes # `fruits` importable to autodoc. subprocess.run( [ sys.executable, "-m", "sphinx", "--builder", "html", "--jobs", "2", "--quiet", str(srcdir), str(outdir), ], check=True, cwd=srcdir, ) assert module_pages(outdir) == PUBLISHED_PAGES