Start using `pathlib.Path and deprecate sphinx.testing.path` (#11526)

This commit is contained in:
Adam Turner 2023-07-28 00:39:12 +01:00 committed by GitHub
parent 23c7fdde75
commit 49d8304670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 490 additions and 630 deletions

View File

@ -14,10 +14,15 @@ Deprecated
* #11512: Deprecate ``sphinx.util.md5`` and ``sphinx.util.sha1``. * #11512: Deprecate ``sphinx.util.md5`` and ``sphinx.util.sha1``.
Use ``hashlib`` instead. Use ``hashlib`` instead.
* #11526: Deprecate ``sphinx.testing.path``.
Use ``os.path`` or ``pathlib`` instead.
Features added Features added
-------------- --------------
* #11526: Support ``os.PathLike`` types and ``pathlib.Path`` objects
in many more places.
Bugs fixed Bugs fixed
---------- ----------

View File

@ -22,6 +22,11 @@ The following is a list of deprecated interfaces.
- Removed - Removed
- Alternatives - Alternatives
* - ``sphinx.testing.path``
- 7.2
- 9.0
- ``os.path`` or ``pathlib``
* - ``sphinx.util.md5`` * - ``sphinx.util.md5``
- 7.2 - 7.2
- 9.0 - 9.0

View File

@ -11,6 +11,7 @@ import sys
from collections import deque from collections import deque
from io import StringIO from io import StringIO
from os import path from os import path
from pathlib import Path
from typing import IO, TYPE_CHECKING, Any, Callable from typing import IO, TYPE_CHECKING, Any, Callable
from docutils import nodes from docutils import nodes
@ -41,7 +42,7 @@ from sphinx.util.console import bold # type: ignore
from sphinx.util.display import progress_message from sphinx.util.display import progress_message
from sphinx.util.i18n import CatalogRepository from sphinx.util.i18n import CatalogRepository
from sphinx.util.logging import prefixed_warnings from sphinx.util.logging import prefixed_warnings
from sphinx.util.osutil import abspath, ensuredir, relpath from sphinx.util.osutil import ensuredir, relpath
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.typing import RoleFunction, TitleGetter from sphinx.util.typing import RoleFunction, TitleGetter
@ -132,7 +133,8 @@ class Sphinx:
warningiserror: bool warningiserror: bool
_warncount: int _warncount: int
def __init__(self, srcdir: str, confdir: str | None, outdir: str, doctreedir: str, def __init__(self, srcdir: str | os.PathLike[str], confdir: str | os.PathLike[str] | None,
outdir: str | os.PathLike[str], doctreedir: str | os.PathLike[str],
buildername: str, confoverrides: dict | None = None, buildername: str, confoverrides: dict | None = None,
status: IO | None = sys.stdout, warning: IO | None = sys.stderr, status: IO | None = sys.stdout, warning: IO | None = sys.stderr,
freshenv: bool = False, warningiserror: bool = False, freshenv: bool = False, warningiserror: bool = False,
@ -145,9 +147,9 @@ class Sphinx:
self.registry = SphinxComponentRegistry() self.registry = SphinxComponentRegistry()
# validate provided directories # validate provided directories
self.srcdir = abspath(srcdir) self.srcdir = Path(srcdir).resolve()
self.outdir = abspath(outdir) self.outdir = Path(outdir).resolve()
self.doctreedir = abspath(doctreedir) self.doctreedir = Path(doctreedir).resolve()
if not path.isdir(self.srcdir): if not path.isdir(self.srcdir):
raise ApplicationError(__('Cannot find source directory (%s)') % raise ApplicationError(__('Cannot find source directory (%s)') %
@ -203,7 +205,7 @@ class Sphinx:
self.confdir = self.srcdir self.confdir = self.srcdir
self.config = Config({}, confoverrides or {}) self.config = Config({}, confoverrides or {})
else: else:
self.confdir = abspath(confdir) self.confdir = Path(confdir).resolve()
self.config = Config.read(self.confdir, confoverrides or {}, self.tags) self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
# initialize some limited config variables before initialize i18n and loading # initialize some limited config variables before initialize i18n and loading

View File

@ -262,7 +262,7 @@ class Builder:
filename) filename)
continue continue
if not filename.startswith(self.srcdir): if not filename.startswith(str(self.srcdir)):
logger.warning(__('file %r given on command line is not under the ' logger.warning(__('file %r given on command line is not under the '
'source directory, ignoring'), filename) 'source directory, ignoring'), filename)
continue continue

View File

@ -21,7 +21,7 @@ from sphinx.locale import __
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.display import status_iterator from sphinx.util.display import status_iterator
from sphinx.util.fileutil import copy_asset_file from sphinx.util.fileutil import copy_asset_file
from sphinx.util.osutil import copyfile, ensuredir from sphinx.util.osutil import copyfile, ensuredir, relpath
try: try:
from PIL import Image from PIL import Image
@ -508,9 +508,6 @@ class EpubBuilder(StandaloneHTMLBuilder):
metadata = self.content_metadata() metadata = self.content_metadata()
# files # files
if not self.outdir.endswith(os.sep):
self.outdir += os.sep
olen = len(self.outdir)
self.files: list[str] = [] self.files: list[str] = []
self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf', self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf',
'toc.ncx', 'META-INF/container.xml', 'toc.ncx', 'META-INF/container.xml',
@ -522,7 +519,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
for root, dirs, files in os.walk(self.outdir): for root, dirs, files in os.walk(self.outdir):
dirs.sort() dirs.sort()
for fn in sorted(files): for fn in sorted(files):
filename = path.join(root, fn)[olen:] filename = relpath(path.join(root, fn), self.outdir)
if filename in self.ignored_files: if filename in self.ignored_files:
continue continue
ext = path.splitext(filename)[-1] ext = path.splitext(filename)[-1]

View File

@ -34,7 +34,7 @@ class ChangesBuilder(Builder):
self.templates.init(self, self.theme) self.templates.init(self, self.theme)
def get_outdated_docs(self) -> str: def get_outdated_docs(self) -> str:
return self.outdir return str(self.outdir)
typemap = { typemap = {
'versionadded': 'added', 'versionadded': 'added',

View File

@ -28,6 +28,7 @@ from sphinx.util.tags import Tags
from sphinx.util.template import SphinxRenderer from sphinx.util.template import SphinxRenderer
if TYPE_CHECKING: if TYPE_CHECKING:
import os
from collections.abc import Generator, Iterable from collections.abc import Generator, Iterable
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -84,11 +85,12 @@ class MsgOrigin:
class GettextRenderer(SphinxRenderer): class GettextRenderer(SphinxRenderer):
def __init__( def __init__(
self, template_path: str | None = None, outdir: str | None = None, self, template_path: list[str | os.PathLike[str]] | None = None,
outdir: str | os.PathLike[str] | None = None,
) -> None: ) -> None:
self.outdir = outdir self.outdir = outdir
if template_path is None: if template_path is None:
template_path = path.join(package_dir, 'templates', 'gettext') template_path = [path.join(package_dir, 'templates', 'gettext')]
super().__init__(template_path) super().__init__(template_path)
def escape(s: str) -> str: def escape(s: str) -> str:

View File

@ -786,7 +786,7 @@ class StandaloneHTMLBuilder(Builder):
def copy_download_files(self) -> None: def copy_download_files(self) -> None:
def to_relpath(f: str) -> str: def to_relpath(f: str) -> str:
return relative_path(self.srcdir, f) return relative_path(self.srcdir, f) # type: ignore[arg-type]
# copy downloadable files # copy downloadable files
if self.env.dlfiles: if self.env.dlfiles:
@ -1254,9 +1254,9 @@ def setup_js_tag_helper(app: Sphinx, pagename: str, templatename: str,
context['js_tag'] = js_tag context['js_tag'] = js_tag
def _file_checksum(outdir: str, filename: str) -> str: def _file_checksum(outdir: str | os.PathLike[str], filename: str | os.PathLike[str]) -> str:
# Don't generate checksums for HTTP URIs # Don't generate checksums for HTTP URIs
if '://' in filename: if '://' in str(filename):
return '' return ''
try: try:
# Ensure universal newline mode is used to avoid checksum differences # Ensure universal newline mode is used to avoid checksum differences
@ -1305,7 +1305,7 @@ def validate_html_extra_path(app: Sphinx, config: Config) -> None:
logger.warning(__('html_extra_path entry %r does not exist'), entry) logger.warning(__('html_extra_path entry %r does not exist'), entry)
config.html_extra_path.remove(entry) config.html_extra_path.remove(entry)
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0] and elif (path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0] and
path.commonpath([app.outdir, extra_path]) == app.outdir): path.commonpath((app.outdir, extra_path)) == path.normpath(app.outdir)):
logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry) logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry)
config.html_extra_path.remove(entry) config.html_extra_path.remove(entry)
@ -1318,7 +1318,7 @@ def validate_html_static_path(app: Sphinx, config: Config) -> None:
logger.warning(__('html_static_path entry %r does not exist'), entry) logger.warning(__('html_static_path entry %r does not exist'), entry)
config.html_static_path.remove(entry) config.html_static_path.remove(entry)
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0] and elif (path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0] and
path.commonpath([app.outdir, static_path]) == app.outdir): path.commonpath((app.outdir, static_path)) == path.normpath(app.outdir)):
logger.warning(__('html_static_path entry %r is placed inside outdir'), entry) logger.warning(__('html_static_path entry %r is placed inside outdir'), entry)
config.html_static_path.remove(entry) config.html_static_path.remove(entry)

View File

@ -24,7 +24,7 @@ from sphinx.util import Tee
from sphinx.util.console import color_terminal, nocolor, red, terminal_safe # type: ignore from sphinx.util.console import color_terminal, nocolor, red, terminal_safe # type: ignore
from sphinx.util.docutils import docutils_namespace, patch_docutils from sphinx.util.docutils import docutils_namespace, patch_docutils
from sphinx.util.exceptions import format_exception_cut_frames, save_traceback from sphinx.util.exceptions import format_exception_cut_frames, save_traceback
from sphinx.util.osutil import abspath, ensuredir from sphinx.util.osutil import ensuredir
def handle_exception( def handle_exception(
@ -234,7 +234,7 @@ def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:
if warning and args.warnfile: if warning and args.warnfile:
try: try:
warnfile = abspath(args.warnfile) warnfile = path.abspath(args.warnfile)
ensuredir(path.dirname(warnfile)) ensuredir(path.dirname(warnfile))
warnfp = open(args.warnfile, 'w', encoding="utf-8") warnfp = open(args.warnfile, 'w', encoding="utf-8")
except Exception as exc: except Exception as exc:

View File

@ -21,6 +21,7 @@ except ImportError:
from sphinx.util.osutil import _chdir as chdir from sphinx.util.osutil import _chdir as chdir
if TYPE_CHECKING: if TYPE_CHECKING:
import os
from collections.abc import Generator, Iterator, Sequence from collections.abc import Generator, Iterator, Sequence
from sphinx.application import Sphinx from sphinx.application import Sphinx
@ -168,9 +169,8 @@ class Config:
self.extensions: list[str] = config.get('extensions', []) self.extensions: list[str] = config.get('extensions', [])
@classmethod @classmethod
def read( def read(cls, confdir: str | os.PathLike[str], overrides: dict | None = None,
cls, confdir: str, overrides: dict | None = None, tags: Tags | None = None, tags: Tags | None = None) -> Config:
) -> Config:
"""Create a Config object from configuration file.""" """Create a Config object from configuration file."""
filename = path.join(confdir, CONFIG_FILENAME) filename = path.join(confdir, CONFIG_FILENAME)
if not path.isfile(filename): if not path.isfile(filename):

View File

@ -244,7 +244,7 @@ class LiteralIncludeReader:
new_lines = self.read_file(self.filename) new_lines = self.read_file(self.filename)
old_filename = self.options['diff'] old_filename = self.options['diff']
old_lines = self.read_file(old_filename) old_lines = self.read_file(old_filename)
diff = unified_diff(old_lines, new_lines, old_filename, self.filename) diff = unified_diff(old_lines, new_lines, str(old_filename), str(self.filename))
return list(diff) return list(diff)
def pyobject_filter( def pyobject_filter(

View File

@ -31,6 +31,7 @@ from sphinx.util.osutil import canon_path, os_path
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator, Iterator from collections.abc import Generator, Iterator
from pathlib import Path
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.builders import Builder from sphinx.builders import Builder
@ -147,8 +148,8 @@ class BuildEnvironment:
def __init__(self, app: Sphinx): def __init__(self, app: Sphinx):
self.app: Sphinx = app self.app: Sphinx = app
self.doctreedir: str = app.doctreedir self.doctreedir: Path = app.doctreedir
self.srcdir: str = app.srcdir self.srcdir: Path = app.srcdir
self.config: Config = None # type: ignore[assignment] self.config: Config = None # type: ignore[assignment]
self.config_status: int = CONFIG_UNSET self.config_status: int = CONFIG_UNSET
self.config_status_extra: str = '' self.config_status_extra: str = ''
@ -387,7 +388,7 @@ class BuildEnvironment:
domain.merge_domaindata(docnames, other.domaindata[domainname]) domain.merge_domaindata(docnames, other.domaindata[domainname])
self.events.emit('env-merge-info', self, docnames, other) self.events.emit('env-merge-info', self, docnames, other)
def path2doc(self, filename: str) -> str | None: def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
"""Return the docname for the filename if the file is document. """Return the docname for the filename if the file is document.
*filename* should be absolute or relative to the source directory. *filename* should be absolute or relative to the source directory.

View File

@ -424,8 +424,10 @@ def _get_modules(
return public, items return public, items
def generate_autosummary_docs(sources: list[str], output_dir: str | None = None, def generate_autosummary_docs(sources: list[str],
suffix: str = '.rst', base_path: str | None = None, output_dir: str | os.PathLike[str] | None = None,
suffix: str = '.rst',
base_path: str | os.PathLike[str] | None = None,
imported_members: bool = False, app: Any = None, imported_members: bool = False, app: Any = None,
overwrite: bool = True, encoding: str = 'utf-8') -> None: overwrite: bool = True, encoding: str = 'utf-8') -> None:
showed_sources = sorted(sources) showed_sources = sorted(sources)

View File

@ -10,7 +10,7 @@ import tempfile
from hashlib import sha1 from hashlib import sha1
from os import path from os import path
from subprocess import CalledProcessError from subprocess import CalledProcessError
from typing import Any from typing import TYPE_CHECKING, Any
from docutils import nodes from docutils import nodes
from docutils.nodes import Element from docutils.nodes import Element
@ -29,6 +29,9 @@ from sphinx.util.png import read_png_depth, write_png_depth
from sphinx.util.template import LaTeXRenderer from sphinx.util.template import LaTeXRenderer
from sphinx.writers.html import HTML5Translator from sphinx.writers.html import HTML5Translator
if TYPE_CHECKING:
import os
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
templates_path = path.join(package_dir, 'templates', 'imgmath') templates_path = path.join(package_dir, 'templates', 'imgmath')
@ -83,7 +86,7 @@ def write_svg_depth(filename: str, depth: int) -> None:
def generate_latex_macro(image_format: str, def generate_latex_macro(image_format: str,
math: str, math: str,
config: Config, config: Config,
confdir: str = '') -> str: confdir: str | os.PathLike[str] = '') -> str:
"""Generate LaTeX macro.""" """Generate LaTeX macro."""
variables = { variables = {
'fontsize': config.imgmath_font_size, 'fontsize': config.imgmath_font_size,

View File

@ -21,7 +21,7 @@ EXCLUDE_PATHS = ['**/_sources', '.#*', '**/.#*', '*.lproj/**']
class Project: class Project:
"""A project is the source code set of the Sphinx document(s).""" """A project is the source code set of the Sphinx document(s)."""
def __init__(self, srcdir: str, source_suffix: dict[str, str]) -> None: def __init__(self, srcdir: str | os.PathLike[str], source_suffix: dict[str, str]) -> None:
#: Source directory. #: Source directory.
self.srcdir = srcdir self.srcdir = srcdir
@ -61,15 +61,15 @@ class Project:
return self.docnames return self.docnames
def path2doc(self, filename: str) -> str | None: def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
"""Return the docname for the filename if the file is a document. """Return the docname for the filename if the file is a document.
*filename* should be absolute or relative to the source directory. *filename* should be absolute or relative to the source directory.
""" """
if filename.startswith(self.srcdir): if str(filename).startswith(str(self.srcdir)):
filename = relpath(filename, self.srcdir) filename = relpath(filename, self.srcdir)
for suffix in self.source_suffix: for suffix in self.source_suffix:
if filename.endswith(suffix): if str(filename).endswith(suffix):
filename = path_stabilize(filename) filename = path_stabilize(filename)
return filename[:-len(suffix)] return filename[:-len(suffix)]

View File

@ -1,97 +0,0 @@
"""Sphinx test comparer for pytest"""
from __future__ import annotations
import difflib
import pathlib
from typing import Any
class PathComparer:
"""
OS-independent path comparison.
Windows path sep and posix path sep:
>>> '\\to\\index' == PathComparer('/to/index')
True
>>> '\\to\\index' == PathComparer('/to/index2')
False
Windows path with drive letters
>>> 'C:\\to\\index' == PathComparer('/to/index')
True
>>> 'C:\\to\\index' == PathComparer('C:/to/index')
True
>>> 'C:\\to\\index' == PathComparer('D:/to/index')
False
"""
def __init__(self, path: str | pathlib.Path):
"""
:param str path: path string, it will be cast as pathlib.Path.
"""
self.path = pathlib.Path(path)
def __str__(self) -> str:
return self.path.as_posix()
def __repr__(self) -> str:
return f"<{self.__class__.__name__}: '{self}'>"
def __eq__(self, other: str | pathlib.Path) -> bool: # type: ignore
return not bool(self.ldiff(other))
def diff(self, other: str | pathlib.Path) -> list[str]:
"""compare self and other.
When different is not exist, return empty list.
>>> PathComparer('/to/index').diff('C:\\to\\index')
[]
When different is exist, return unified diff style list as:
>>> PathComparer('/to/index').diff('C:\\to\\index2')
[
'- C:/to/index'
'+ C:/to/index2'
'? +'
]
"""
return self.ldiff(other)
def ldiff(self, other: str | pathlib.Path) -> list[str]:
return self._diff(
self.path,
pathlib.Path(other),
)
def rdiff(self, other: str | pathlib.Path) -> list[str]:
return self._diff(
pathlib.Path(other),
self.path,
)
def _diff(self, lhs: pathlib.Path, rhs: pathlib.Path) -> list[str]:
if lhs == rhs:
return []
if lhs.drive or rhs.drive:
# If either has a drive letter compare by absolute path
s_path, o_path = lhs.absolute().as_posix(), rhs.absolute().as_posix()
else:
s_path, o_path = lhs.as_posix(), rhs.as_posix()
if s_path == o_path:
return []
return [line.strip() for line in difflib.Differ().compare([s_path], [o_path])]
def pytest_assertrepr_compare(op: str, left: Any, right: Any) -> list[str]:
if isinstance(left, PathComparer) and op == "==":
return ['Comparing path:'] + left.ldiff(right)
elif isinstance(right, PathComparer) and op == "==":
return ['Comparing path:'] + right.rdiff(left)
else:
return []

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import shutil
import subprocess import subprocess
import sys import sys
from collections import namedtuple from collections import namedtuple
@ -10,11 +11,11 @@ from typing import TYPE_CHECKING, Any, Callable
import pytest import pytest
from sphinx.testing import util
from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator from collections.abc import Generator
from pathlib import Path
DEFAULT_ENABLED_MARKERS = [ DEFAULT_ENABLED_MARKERS = [
( (
@ -96,7 +97,7 @@ def app_params(request: Any, test_params: dict, shared_result: SharedResult,
# special support for sphinx/tests # special support for sphinx/tests
if rootdir and not srcdir.exists(): if rootdir and not srcdir.exists():
testroot_path = rootdir / ('test-' + testroot) testroot_path = rootdir / ('test-' + testroot)
testroot_path.copytree(srcdir) shutil.copytree(testroot_path, srcdir)
return namedtuple('app_params', 'args,kwargs')(args, kwargs) # type: ignore return namedtuple('app_params', 'args,kwargs')(args, kwargs) # type: ignore
@ -169,8 +170,6 @@ def make_app(test_params: dict, monkeypatch: Any) -> Generator[Callable, None, N
if you want to initialize 'app' in your test function. please use this if you want to initialize 'app' in your test function. please use this
instead of using SphinxTestApp class directory. instead of using SphinxTestApp class directory.
""" """
monkeypatch.setattr('sphinx.application.abspath', lambda x: x)
apps = [] apps = []
syspath = sys.path[:] syspath = sys.path[:]
@ -218,21 +217,9 @@ def if_graphviz_found(app: SphinxTestApp) -> None: # NoQA: PT004
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def sphinx_test_tempdir(tmpdir_factory: Any) -> util.path: def sphinx_test_tempdir(tmp_path_factory: Any) -> Path:
""" """Temporary directory."""
Temporary directory wrapped with `path` class. return tmp_path_factory.getbasetemp()
"""
tmpdir = tmpdir_factory.getbasetemp()
return util.path(tmpdir).abspath()
@pytest.fixture()
def tempdir(tmpdir: str) -> util.path:
"""
Temporary directory wrapped with `path` class.
This fixture is for back-compatibility with old test implementation.
"""
return util.path(tmpdir)
@pytest.fixture() @pytest.fixture()

View File

@ -3,11 +3,18 @@ from __future__ import annotations
import os import os
import shutil import shutil
import sys import sys
import warnings
from typing import IO, TYPE_CHECKING, Any, Callable from typing import IO, TYPE_CHECKING, Any, Callable
from sphinx.deprecation import RemovedInSphinx90Warning
if TYPE_CHECKING: if TYPE_CHECKING:
import builtins import builtins
warnings.warn("'sphinx.testing.path' is deprecated. "
"Use 'os.path' or 'pathlib' instead.",
RemovedInSphinx90Warning, stacklevel=2)
FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()

View File

@ -15,12 +15,10 @@ from docutils.parsers.rst import directives, roles
from sphinx import application, locale from sphinx import application, locale
from sphinx.pycode import ModuleAnalyzer from sphinx.pycode import ModuleAnalyzer
from sphinx.testing.path import path
from sphinx.util.osutil import relpath
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator
from io import StringIO from io import StringIO
from pathlib import Path
__all__ = [ __all__ = [
'Struct', 'SphinxTestApp', 'SphinxTestAppWrapperForSkipBuilding', 'Struct', 'SphinxTestApp', 'SphinxTestAppWrapperForSkipBuilding',
@ -102,8 +100,8 @@ class SphinxTestApp(application.Sphinx):
def __init__( def __init__(
self, self,
buildername: str = 'html', buildername: str = 'html',
srcdir: path | None = None, srcdir: Path | None = None,
builddir: path | None = None, builddir: Path | None = None,
freshenv: bool = False, freshenv: bool = False,
confoverrides: dict | None = None, confoverrides: dict | None = None,
status: IO | None = None, status: IO | None = None,
@ -123,9 +121,9 @@ class SphinxTestApp(application.Sphinx):
confdir = srcdir confdir = srcdir
outdir = builddir.joinpath(buildername) outdir = builddir.joinpath(buildername)
outdir.makedirs(exist_ok=True) outdir.mkdir(parents=True, exist_ok=True)
doctreedir = builddir.joinpath('doctrees') doctreedir = builddir.joinpath('doctrees')
doctreedir.makedirs(exist_ok=True) doctreedir.mkdir(parents=True, exist_ok=True)
if confoverrides is None: if confoverrides is None:
confoverrides = {} confoverrides = {}
warningiserror = False warningiserror = False
@ -184,7 +182,7 @@ class SphinxTestAppWrapperForSkipBuilding:
return getattr(self.app, name) return getattr(self.app, name)
def build(self, *args: Any, **kwargs: Any) -> None: def build(self, *args: Any, **kwargs: Any) -> None:
if not self.app.outdir.listdir(): # type: ignore if not os.listdir(self.app.outdir):
# if listdir is empty, do build. # if listdir is empty, do build.
self.app.build(*args, **kwargs) self.app.build(*args, **kwargs)
# otherwise, we can use built cache # otherwise, we can use built cache
@ -193,14 +191,6 @@ class SphinxTestAppWrapperForSkipBuilding:
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')') _unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
def find_files(root: str, suffix: str | None = None) -> Generator[str, None, None]:
for dirpath, _dirs, files in os.walk(root, followlinks=True):
dirpath = path(dirpath)
for f in [f for f in files if not suffix or f.endswith(suffix)]:
fpath = dirpath / f
yield relpath(fpath, root)
def strip_escseq(text: str) -> str: def strip_escseq(text: str) -> str:
return re.sub('\x1b.*?m', '', text) return re.sub('\x1b.*?m', '', text)

View File

@ -15,7 +15,7 @@ if TYPE_CHECKING:
from sphinx.util.template import BaseRenderer from sphinx.util.template import BaseRenderer
def copy_asset_file(source: str, destination: str, def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLike[str],
context: dict | None = None, context: dict | None = None,
renderer: BaseRenderer | None = None) -> None: renderer: BaseRenderer | None = None) -> None:
"""Copy an asset file to destination. """Copy an asset file to destination.
@ -34,8 +34,10 @@ def copy_asset_file(source: str, destination: str,
if os.path.isdir(destination): if os.path.isdir(destination):
# Use source filename if destination points a directory # Use source filename if destination points a directory
destination = os.path.join(destination, os.path.basename(source)) destination = os.path.join(destination, os.path.basename(source))
else:
destination = str(destination)
if source.lower().endswith('_t') and context is not None: if os.path.splitext(source)[1].lower().endswith('_t') and context is not None:
if renderer is None: if renderer is None:
from sphinx.util.template import SphinxRenderer from sphinx.util.template import SphinxRenderer
renderer = SphinxRenderer() renderer = SphinxRenderer()
@ -49,7 +51,8 @@ def copy_asset_file(source: str, destination: str,
copyfile(source, destination) copyfile(source, destination)
def copy_asset(source: str, destination: str, excluded: PathMatcher = lambda path: False, def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[str],
excluded: PathMatcher = lambda path: False,
context: dict | None = None, renderer: BaseRenderer | None = None, context: dict | None = None, renderer: BaseRenderer | None = None,
onerror: Callable[[str, Exception], None] | None = None) -> None: onerror: Callable[[str, Exception], None] | None = None) -> None:
"""Copy asset files to destination recursively. """Copy asset files to destination recursively.
@ -77,7 +80,7 @@ def copy_asset(source: str, destination: str, excluded: PathMatcher = lambda pat
return return
for root, dirs, files in os.walk(source, followlinks=True): for root, dirs, files in os.walk(source, followlinks=True):
reldir = relative_path(source, root) reldir = relative_path(source, root) # type: ignore[arg-type]
for dir in dirs[:]: for dir in dirs[:]:
if excluded(posixpath.join(reldir, dir)): if excluded(posixpath.join(reldir, dir)):
dirs.remove(dir) dirs.remove(dir)

View File

@ -73,7 +73,7 @@ class CatalogInfo(LocaleFileInfoBase):
class CatalogRepository: class CatalogRepository:
"""A repository for message catalogs.""" """A repository for message catalogs."""
def __init__(self, basedir: str, locale_dirs: list[str], def __init__(self, basedir: str | os.PathLike[str], locale_dirs: list[str],
language: str, encoding: str) -> None: language: str, encoding: str) -> None:
self.basedir = basedir self.basedir = basedir
self._locale_dirs = locale_dirs self._locale_dirs = locale_dirs

View File

@ -110,7 +110,7 @@ def patfilter(names: Iterable[str], pat: str) -> list[str]:
def get_matching_files( def get_matching_files(
dirname: str, dirname: str | os.PathLike[str],
include_patterns: Iterable[str] = ("**",), include_patterns: Iterable[str] = ("**",),
exclude_patterns: Iterable[str] = (), exclude_patterns: Iterable[str] = (),
) -> Iterator[str]: ) -> Iterator[str]:

View File

@ -18,13 +18,6 @@ from sphinx.deprecation import _deprecation_warning
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Iterator from collections.abc import Iterator
try:
# for ALT Linux (#6712)
from sphinx.testing.path import path as Path
except ImportError:
Path = None # type: ignore
# SEP separates path elements in the canonical file names # SEP separates path elements in the canonical file names
# #
# Define SEP as a manifest constant, not so much because we expect it to change # Define SEP as a manifest constant, not so much because we expect it to change
@ -37,12 +30,12 @@ def os_path(canonicalpath: str) -> str:
return canonicalpath.replace(SEP, path.sep) return canonicalpath.replace(SEP, path.sep)
def canon_path(nativepath: str) -> str: def canon_path(nativepath: str | os.PathLike[str]) -> str:
"""Return path in OS-independent form""" """Return path in OS-independent form"""
return nativepath.replace(path.sep, SEP) return str(nativepath).replace(path.sep, SEP)
def path_stabilize(filepath: str) -> str: def path_stabilize(filepath: str | os.PathLike[str]) -> str:
"Normalize path separator and unicode string" "Normalize path separator and unicode string"
new_path = canon_path(filepath) new_path = canon_path(filepath)
return unicodedata.normalize('NFC', new_path) return unicodedata.normalize('NFC', new_path)
@ -71,9 +64,9 @@ def relative_uri(base: str, to: str) -> str:
return ('..' + SEP) * (len(b2) - 1) + SEP.join(t2) return ('..' + SEP) * (len(b2) - 1) + SEP.join(t2)
def ensuredir(path: str) -> None: def ensuredir(file: str | os.PathLike[str]) -> None:
"""Ensure that a path exists.""" """Ensure that a path exists."""
os.makedirs(path, exist_ok=True) os.makedirs(file, exist_ok=True)
def mtimes_of_files(dirnames: list[str], suffix: str) -> Iterator[float]: def mtimes_of_files(dirnames: list[str], suffix: str) -> Iterator[float]:
@ -87,14 +80,14 @@ def mtimes_of_files(dirnames: list[str], suffix: str) -> Iterator[float]:
pass pass
def copytimes(source: str, dest: str) -> None: def copytimes(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> None:
"""Copy a file's modification times.""" """Copy a file's modification times."""
st = os.stat(source) st = os.stat(source)
if hasattr(os, 'utime'): if hasattr(os, 'utime'):
os.utime(dest, (st.st_atime, st.st_mtime)) os.utime(dest, (st.st_atime, st.st_mtime))
def copyfile(source: str, dest: str) -> None: def copyfile(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> None:
"""Copy a file and its modification times, if possible. """Copy a file and its modification times, if possible.
Note: ``copyfile`` skips copying if the file has not been changed""" Note: ``copyfile`` skips copying if the file has not been changed"""
@ -119,7 +112,8 @@ def make_filename_from_project(project: str) -> str:
return make_filename(project_suffix_re.sub('', project)).lower() return make_filename(project_suffix_re.sub('', project)).lower()
def relpath(path: str, start: str | None = os.curdir) -> str: def relpath(path: str | os.PathLike[str],
start: str | os.PathLike[str] | None = os.curdir) -> str:
"""Return a relative filepath to *path* either from the current directory or """Return a relative filepath to *path* either from the current directory or
from an optional *start* directory. from an optional *start* directory.
@ -129,26 +123,14 @@ def relpath(path: str, start: str | None = os.curdir) -> str:
try: try:
return os.path.relpath(path, start) return os.path.relpath(path, start)
except ValueError: except ValueError:
return path return str(path)
safe_relpath = relpath # for compatibility safe_relpath = relpath # for compatibility
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def abspath(pathdir: str) -> str: abspath = path.abspath
if Path is not None and isinstance(pathdir, Path):
return pathdir.abspath()
else:
pathdir = path.abspath(pathdir)
if isinstance(pathdir, bytes):
try:
pathdir = pathdir.decode(fs_encoding)
except UnicodeDecodeError as exc:
raise UnicodeDecodeError('multibyte filename not supported on '
'this filesystem encoding '
'(%r)' % fs_encoding) from exc
return pathdir
class _chdir: class _chdir:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
import os import os
from functools import partial from functools import partial
from os import path from os import path
from typing import Any, Callable from typing import TYPE_CHECKING, Any, Callable
from jinja2 import TemplateNotFound from jinja2 import TemplateNotFound
from jinja2.environment import Environment from jinja2.environment import Environment
@ -17,6 +17,9 @@ from sphinx.jinja2glue import SphinxFileSystemLoader
from sphinx.locale import get_translator from sphinx.locale import get_translator
from sphinx.util import rst, texescape from sphinx.util import rst, texescape
if TYPE_CHECKING:
from collections.abc import Sequence
class BaseRenderer: class BaseRenderer:
def __init__(self, loader: BaseLoader | None = None) -> None: def __init__(self, loader: BaseLoader | None = None) -> None:
@ -32,8 +35,8 @@ class BaseRenderer:
class FileRenderer(BaseRenderer): class FileRenderer(BaseRenderer):
def __init__(self, search_path: str | list[str]) -> None: def __init__(self, search_path: Sequence[str | os.PathLike[str]]) -> None:
if isinstance(search_path, str): if isinstance(search_path, (str, os.PathLike)):
search_path = [search_path] search_path = [search_path]
else: else:
# filter "None" paths # filter "None" paths
@ -50,7 +53,7 @@ class FileRenderer(BaseRenderer):
class SphinxRenderer(FileRenderer): class SphinxRenderer(FileRenderer):
def __init__(self, template_path: None | str | list[str] = None) -> None: def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None) -> None:
if template_path is None: if template_path is None:
template_path = os.path.join(package_dir, 'templates') template_path = os.path.join(package_dir, 'templates')
super().__init__(template_path) super().__init__(template_path)
@ -61,11 +64,10 @@ class SphinxRenderer(FileRenderer):
class LaTeXRenderer(SphinxRenderer): class LaTeXRenderer(SphinxRenderer):
def __init__( def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None,
self, template_path: str | None = None, latex_engine: str | None = None, latex_engine: str | None = None) -> None:
) -> None:
if template_path is None: if template_path is None:
template_path = os.path.join(package_dir, 'templates', 'latex') template_path = [os.path.join(package_dir, 'templates', 'latex')]
super().__init__(template_path) super().__init__(template_path)
# use texescape as escape filter # use texescape as escape filter
@ -85,9 +87,8 @@ class LaTeXRenderer(SphinxRenderer):
class ReSTRenderer(SphinxRenderer): class ReSTRenderer(SphinxRenderer):
def __init__( def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None,
self, template_path: None | str | list[str] = None, language: str | None = None, language: str | None = None) -> None:
) -> None:
super().__init__(template_path) super().__init__(template_path)
# add language to environment # add language to environment
@ -102,8 +103,9 @@ class ReSTRenderer(SphinxRenderer):
class SphinxTemplateLoader(BaseLoader): class SphinxTemplateLoader(BaseLoader):
"""A loader supporting template inheritance""" """A loader supporting template inheritance"""
def __init__(self, confdir: str, templates_paths: list[str], def __init__(self, confdir: str | os.PathLike[str],
system_templates_paths: list[str]) -> None: templates_paths: Sequence[str | os.PathLike[str]],
system_templates_paths: Sequence[str | os.PathLike[str]]) -> None:
self.loaders = [] self.loaders = []
self.sysloaders = [] self.sysloaders = []

View File

@ -1,13 +1,10 @@
import os from pathlib import Path
import shutil
import docutils import docutils
import pytest import pytest
import sphinx import sphinx
import sphinx.locale import sphinx.locale
from sphinx.testing import comparer
from sphinx.testing.path import path
def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'): def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):
@ -30,32 +27,11 @@ collect_ignore = ['roots']
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def rootdir(): def rootdir():
return path(__file__).parent.abspath() / 'roots' return Path(__file__).parent.absolute() / 'roots'
def pytest_report_header(config): def pytest_report_header(config):
header = ("libraries: Sphinx-%s, docutils-%s" % header = f"libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}"
(sphinx.__display_version__, docutils.__version__))
if hasattr(config, '_tmp_path_factory'): if hasattr(config, '_tmp_path_factory'):
header += "\nbase tempdir: %s" % config._tmp_path_factory.getbasetemp() header += f"\nbase tmp_path: {config._tmp_path_factory.getbasetemp()}"
return header return header
def pytest_assertrepr_compare(op, left, right):
comparer.pytest_assertrepr_compare(op, left, right)
def _initialize_test_directory(session):
if 'SPHINX_TEST_TEMPDIR' in os.environ:
tempdir = os.path.abspath(os.getenv('SPHINX_TEST_TEMPDIR'))
print('Temporary files will be placed in %s.' % tempdir)
if os.path.exists(tempdir):
shutil.rmtree(tempdir)
os.makedirs(tempdir)
def pytest_sessionstart(session):
_initialize_test_directory(session)

View File

@ -11,7 +11,6 @@ from docutils import nodes
import sphinx.application import sphinx.application
from sphinx.errors import ExtensionError from sphinx.errors import ExtensionError
from sphinx.testing.path import path
from sphinx.testing.util import SphinxTestApp, strip_escseq from sphinx.testing.util import SphinxTestApp, strip_escseq
from sphinx.util import logging from sphinx.util import logging
@ -24,13 +23,11 @@ def test_instantiation(tmp_path_factory, rootdir: str, monkeypatch):
if rootdir and not src_dir.exists(): if rootdir and not src_dir.exists():
shutil.copytree(Path(str(rootdir)) / 'test-root', src_dir) shutil.copytree(Path(str(rootdir)) / 'test-root', src_dir)
monkeypatch.setattr('sphinx.application.abspath', lambda x: x)
syspath = sys.path[:] syspath = sys.path[:]
# When # When
app_ = SphinxTestApp( app_ = SphinxTestApp(
srcdir=path(src_dir), srcdir=src_dir,
status=StringIO(), status=StringIO(),
warning=StringIO(), warning=StringIO(),
) )

View File

@ -1,5 +1,7 @@
"""Test all builders.""" """Test all builders."""
import os
import shutil
import sys import sys
from unittest import mock from unittest import mock
@ -23,7 +25,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
basedir = sphinx_test_tempdir / request.node.originalname basedir = sphinx_test_tempdir / request.node.originalname
srcdir = basedir / test_name srcdir = basedir / test_name
if not srcdir.exists(): if not srcdir.exists():
(rootdir / 'test-root').copytree(srcdir) shutil.copytree(rootdir / 'test-root', srcdir)
# add a doc with a non-ASCII file name to the source dir # add a doc with a non-ASCII file name to the source dir
(srcdir / (test_name + '.txt')).write_text(""" (srcdir / (test_name + '.txt')).write_text("""
@ -54,11 +56,11 @@ def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
app.build() app.build()
def test_root_doc_not_found(tempdir, make_app): def test_root_doc_not_found(tmp_path, make_app):
(tempdir / 'conf.py').write_text('', encoding='utf8') (tmp_path / 'conf.py').write_text('', encoding='utf8')
assert tempdir.listdir() == ['conf.py'] assert os.listdir(tmp_path) == ['conf.py']
app = make_app('dummy', srcdir=tempdir) app = make_app('dummy', srcdir=tmp_path)
with pytest.raises(SphinxError): with pytest.raises(SphinxError):
app.builder.build_all() # no index.rst app.builder.build_all() # no index.rst

View File

@ -42,9 +42,9 @@ def test_build_gettext(app):
# Do messages end up in the correct location? # Do messages end up in the correct location?
# top-level documents end up in a message catalog # top-level documents end up in a message catalog
assert (app.outdir / 'extapi.pot').isfile() assert (app.outdir / 'extapi.pot').is_file()
# directory items are grouped into sections # directory items are grouped into sections
assert (app.outdir / 'subdir.pot').isfile() assert (app.outdir / 'subdir.pot').is_file()
# regression test for issue #960 # regression test for issue #960
catalog = (app.outdir / 'markup.pot').read_text(encoding='utf8') catalog = (app.outdir / 'markup.pot').read_text(encoding='utf8')
@ -54,7 +54,7 @@ def test_build_gettext(app):
@pytest.mark.sphinx('gettext', srcdir='root-gettext') @pytest.mark.sphinx('gettext', srcdir='root-gettext')
def test_msgfmt(app): def test_msgfmt(app):
app.builder.build_all() app.builder.build_all()
(app.outdir / 'en' / 'LC_MESSAGES').makedirs() (app.outdir / 'en' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
with chdir(app.outdir): with chdir(app.outdir):
try: try:
args = ['msginit', '--no-translator', '-i', 'markup.pot', '--locale', 'en_US'] args = ['msginit', '--no-translator', '-i', 'markup.pot', '--locale', 'en_US']
@ -66,7 +66,7 @@ def test_msgfmt(app):
print(exc.stderr) print(exc.stderr)
raise AssertionError('msginit exited with return code %s' % exc.returncode) raise AssertionError('msginit exited with return code %s' % exc.returncode)
assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' assert (app.outdir / 'en_US.po').is_file(), 'msginit failed'
try: try:
args = ['msgfmt', 'en_US.po', args = ['msgfmt', 'en_US.po',
'-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')] '-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')]
@ -79,7 +79,7 @@ def test_msgfmt(app):
raise AssertionError('msgfmt exited with return code %s' % exc.returncode) raise AssertionError('msgfmt exited with return code %s' % exc.returncode)
mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo' mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo'
assert mo.isfile(), 'msgfmt failed' assert mo.is_file(), 'msgfmt failed'
_ = gettext.translation('test_root', app.outdir, languages=['en']).gettext _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
assert _("Testing various markup") == "Testing various markup" assert _("Testing various markup") == "Testing various markup"
@ -164,7 +164,7 @@ def test_gettext_disable_index_entries(app):
@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext') @pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
def test_gettext_template(app): def test_gettext_template(app):
app.build() app.build()
assert (app.outdir / 'sphinx.pot').isfile() assert (app.outdir / 'sphinx.pot').is_file()
result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8') result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
assert "Welcome" in result assert "Welcome" in result
@ -174,7 +174,7 @@ def test_gettext_template(app):
@pytest.mark.sphinx('gettext', testroot='gettext-template') @pytest.mark.sphinx('gettext', testroot='gettext-template')
def test_gettext_template_msgid_order_in_sphinxpot(app): def test_gettext_template_msgid_order_in_sphinxpot(app):
app.builder.build_all() app.builder.build_all()
assert (app.outdir / 'sphinx.pot').isfile() assert (app.outdir / 'sphinx.pot').is_file()
result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8') result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
assert re.search( assert re.search(
@ -192,7 +192,7 @@ def test_gettext_template_msgid_order_in_sphinxpot(app):
def test_build_single_pot(app): def test_build_single_pot(app):
app.builder.build_all() app.builder.build_all()
assert (app.outdir / 'documentation.pot').isfile() assert (app.outdir / 'documentation.pot').is_file()
result = (app.outdir / 'documentation.pot').read_text(encoding='utf8') result = (app.outdir / 'documentation.pot').read_text(encoding='utf8')
assert re.search( assert re.search(

View File

@ -116,22 +116,22 @@ def test_html_warnings(app, warning):
app.build() app.build()
html_warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue())) html_warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
html_warnings_exp = HTML_WARNINGS % { html_warnings_exp = HTML_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.as_posix())}
assert re.match(html_warnings_exp + '$', html_warnings), \ assert re.match(html_warnings_exp + '$', html_warnings), \
"Warnings don't match:\n" + \ "Warnings don't match:\n" + \
'--- Expected (regex):\n' + html_warnings_exp + \ '--- Expected (regex):\n' + html_warnings_exp + \
'--- Got:\n' + html_warnings '--- Got:\n' + html_warnings
def test_html4_error(make_app, tempdir): def test_html4_error(make_app, tmp_path):
(tempdir / 'conf.py').write_text('', encoding='utf-8') (tmp_path / 'conf.py').write_text('', encoding='utf-8')
with pytest.raises( with pytest.raises(
ConfigError, ConfigError,
match=r'HTML 4 is no longer supported by Sphinx', match=r'HTML 4 is no longer supported by Sphinx',
): ):
make_app( make_app(
buildername='html', buildername='html',
srcdir=tempdir, srcdir=tmp_path,
confoverrides={'html4_writer': True}, confoverrides={'html4_writer': True},
) )
@ -753,7 +753,7 @@ def test_numfig_without_numbered_toctree(app, cached_etree_parse, fname, expect)
index = re.sub(':numbered:.*', '', index) index = re.sub(':numbered:.*', '', index)
(app.srcdir / 'index.rst').write_text(index, encoding='utf8') (app.srcdir / 'index.rst').write_text(index, encoding='utf8')
if not app.outdir.listdir(): if not os.listdir(app.outdir):
app.build() app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
@ -1371,7 +1371,7 @@ def test_html_remote_images(app, status, warning):
def test_html_encoded_image(app, status, warning): def test_html_encoded_image(app, status, warning):
app.builder.build_all() app.builder.build_all()
result = (app.outdir / 'index.html').read_text() result = (app.outdir / 'index.html').read_text(encoding='utf8')
assert ('<img alt="_images/img_%231.png" src="_images/img_%231.png" />' in result) assert ('<img alt="_images/img_%231.png" src="_images/img_%231.png" />' in result)
assert (app.outdir / '_images/img_#1.png').exists() assert (app.outdir / '_images/img_#1.png').exists()
@ -1563,7 +1563,7 @@ def test_html_dark_pygments_style_default(app):
@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_extra_path') @pytest.mark.sphinx(testroot='basic', srcdir='validate_html_extra_path')
def test_validate_html_extra_path(app): def test_validate_html_extra_path(app):
(app.confdir / '_static').makedirs() (app.confdir / '_static').mkdir(parents=True, exist_ok=True)
app.config.html_extra_path = [ app.config.html_extra_path = [
'/path/to/not_found', # not found '/path/to/not_found', # not found
'_static', '_static',
@ -1576,7 +1576,7 @@ def test_validate_html_extra_path(app):
@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_static_path') @pytest.mark.sphinx(testroot='basic', srcdir='validate_html_static_path')
def test_validate_html_static_path(app): def test_validate_html_static_path(app):
(app.confdir / '_static').makedirs() (app.confdir / '_static').mkdir(parents=True, exist_ok=True)
app.config.html_static_path = [ app.config.html_static_path = [
'/path/to/not_found', # not found '/path/to/not_found', # not found
'_static', '_static',
@ -1691,7 +1691,7 @@ def test_html_signaturereturn_icon(app):
@pytest.mark.sphinx('html', testroot='reST-code-role') @pytest.mark.sphinx('html', testroot='reST-code-role')
def test_html_code_role(app): def test_html_code_role(app):
app.build() app.build()
content = (app.outdir / 'index.html').read_text() content = (app.outdir / 'index.html').read_text(encoding='utf8')
common_content = ( common_content = (
'<span class="k">def</span> <span class="nf">foo</span>' '<span class="k">def</span> <span class="nf">foo</span>'
@ -1716,7 +1716,7 @@ def test_html_code_role(app):
confoverrides={'option_emphasise_placeholders': True}) confoverrides={'option_emphasise_placeholders': True})
def test_option_emphasise_placeholders(app, status, warning): def test_option_emphasise_placeholders(app, status, warning):
app.build() app.build()
content = (app.outdir / 'objects.html').read_text() content = (app.outdir / 'objects.html').read_text(encoding='utf8')
assert '<em><span class="pre">TYPE</span></em>' in content assert '<em><span class="pre">TYPE</span></em>' in content
assert '{TYPE}' not in content assert '{TYPE}' not in content
assert ('<em><span class="pre">WHERE</span></em>' assert ('<em><span class="pre">WHERE</span></em>'
@ -1730,7 +1730,7 @@ def test_option_emphasise_placeholders(app, status, warning):
@pytest.mark.sphinx('html', testroot='root') @pytest.mark.sphinx('html', testroot='root')
def test_option_emphasise_placeholders_default(app, status, warning): def test_option_emphasise_placeholders_default(app, status, warning):
app.build() app.build()
content = (app.outdir / 'objects.html').read_text() content = (app.outdir / 'objects.html').read_text(encoding='utf8')
assert '<span class="pre">={TYPE}</span>' in content assert '<span class="pre">={TYPE}</span>' in content
assert '<span class="pre">={WHERE}-{COUNT}</span></span>' in content assert '<span class="pre">={WHERE}-{COUNT}</span></span>' in content
assert '<span class="pre">{client_name}</span>' in content assert '<span class="pre">{client_name}</span>' in content
@ -1742,7 +1742,7 @@ def test_option_emphasise_placeholders_default(app, status, warning):
@pytest.mark.sphinx('html', testroot='root') @pytest.mark.sphinx('html', testroot='root')
def test_option_reference_with_value(app, status, warning): def test_option_reference_with_value(app, status, warning):
app.build() app.build()
content = (app.outdir / 'objects.html').read_text() content = (app.outdir / 'objects.html').read_text(encoding='utf-8')
assert ('<span class="pre">-mapi</span></span><span class="sig-prename descclassname">' assert ('<span class="pre">-mapi</span></span><span class="sig-prename descclassname">'
'</span><a class="headerlink" href="#cmdoption-git-commit-mapi"') in content '</span><a class="headerlink" href="#cmdoption-git-commit-mapi"') in content
assert 'first option <a class="reference internal" href="#cmdoption-git-commit-mapi">' in content assert 'first option <a class="reference internal" href="#cmdoption-git-commit-mapi">' in content

View File

@ -124,7 +124,7 @@ def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_
app.builder.build_all() app.builder.build_all()
# file from latex_additional_files # file from latex_additional_files
assert (app.outdir / 'svgimg.svg').isfile() assert (app.outdir / 'svgimg.svg').is_file()
compile_latex_document(app, 'sphinxtests.tex', docclass) compile_latex_document(app, 'sphinxtests.tex', docclass)
@ -179,7 +179,7 @@ def test_latex_warnings(app, status, warning):
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue())) warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
warnings_exp = LATEX_WARNINGS % { warnings_exp = LATEX_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.as_posix())}
assert re.match(warnings_exp + '$', warnings), \ assert re.match(warnings_exp + '$', warnings), \
"Warnings don't match:\n" + \ "Warnings don't match:\n" + \
'--- Expected (regex):\n' + warnings_exp + \ '--- Expected (regex):\n' + warnings_exp + \
@ -1671,7 +1671,7 @@ def test_latex_container(app, status, warning):
@pytest.mark.sphinx('latex', testroot='reST-code-role') @pytest.mark.sphinx('latex', testroot='reST-code-role')
def test_latex_code_role(app): def test_latex_code_role(app):
app.build() app.build()
content = (app.outdir / 'python.tex').read_text() content = (app.outdir / 'python.tex').read_text(encoding='utf8')
common_content = ( common_content = (
r'\PYG{k}{def} ' r'\PYG{k}{def} '
@ -1715,7 +1715,7 @@ def test_copy_images(app, status, warning):
@pytest.mark.sphinx('latex', testroot='latex-labels-before-module') @pytest.mark.sphinx('latex', testroot='latex-labels-before-module')
def test_duplicated_labels_before_module(app, status, warning): def test_duplicated_labels_before_module(app, status, warning):
app.build() app.build()
content: str = (app.outdir / 'python.tex').read_text() content: str = (app.outdir / 'python.tex').read_text(encoding='utf8')
def count_label(name): def count_label(name):
text = r'\phantomsection\label{\detokenize{%s}}' % name text = r'\phantomsection\label{\detokenize{%s}}' % name

View File

@ -31,7 +31,7 @@ def test_texinfo_warnings(app, status, warning):
app.builder.build_all() app.builder.build_all()
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue())) warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
warnings_exp = TEXINFO_WARNINGS % { warnings_exp = TEXINFO_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.as_posix())}
assert re.match(warnings_exp + '$', warnings), \ assert re.match(warnings_exp + '$', warnings), \
"Warnings don't match:\n" + \ "Warnings don't match:\n" + \
'--- Expected (regex):\n' + warnings_exp + \ '--- Expected (regex):\n' + warnings_exp + \

View File

@ -1,28 +1,28 @@
"""Test the base build process.""" """Test the base build process."""
import shutil import shutil
from pathlib import Path
import pytest import pytest
from sphinx.testing.util import find_files
@pytest.fixture() @pytest.fixture()
def _setup_test(app_params): def _setup_test(app_params):
assert isinstance(app_params.kwargs['srcdir'], Path)
srcdir = app_params.kwargs['srcdir'] srcdir = app_params.kwargs['srcdir']
src_locale_dir = srcdir / 'xx' / 'LC_MESSAGES' src_locale_dir = srcdir / 'xx' / 'LC_MESSAGES'
dest_locale_dir = srcdir / 'locale' dest_locale_dir = srcdir / 'locale'
# copy all catalogs into locale layout directory # copy all catalogs into locale layout directory
for po in find_files(src_locale_dir, '.po'): for po in src_locale_dir.rglob('*.po'):
copy_po = (dest_locale_dir / 'en' / 'LC_MESSAGES' / po) copy_po = (dest_locale_dir / 'en' / 'LC_MESSAGES' / po.relative_to(src_locale_dir))
if not copy_po.parent.exists(): if not copy_po.parent.exists():
copy_po.parent.makedirs() copy_po.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src_locale_dir / po, copy_po) shutil.copy(po, copy_po)
yield yield
# delete remnants left over after failed build # delete remnants left over after failed build
dest_locale_dir.rmtree(True) shutil.rmtree(dest_locale_dir, ignore_errors=True)
(srcdir / '_build').rmtree(True) shutil.rmtree(srcdir / '_build', ignore_errors=True)
@pytest.mark.usefixtures('_setup_test') @pytest.mark.usefixtures('_setup_test')
@ -35,11 +35,8 @@ def test_compile_all_catalogs(app, status, warning):
locale_dir = app.srcdir / 'locale' locale_dir = app.srcdir / 'locale'
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
expect = { expect = {x.with_suffix('.mo') for x in catalog_dir.rglob('*.po')}
x.replace('.po', '.mo') actual = set(catalog_dir.rglob('*.mo'))
for x in find_files(catalog_dir, '.po')
}
actual = set(find_files(catalog_dir, '.mo'))
assert actual # not empty assert actual # not empty
assert actual == expect assert actual == expect
@ -53,12 +50,11 @@ def test_compile_specific_catalogs(app, status, warning):
locale_dir = app.srcdir / 'locale' locale_dir = app.srcdir / 'locale'
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
def get_actual(): actual_on_boot = set(catalog_dir.rglob('*.mo')) # sphinx.mo might be included
return set(find_files(catalog_dir, '.mo'))
actual_on_boot = get_actual() # sphinx.mo might be included
app.builder.compile_specific_catalogs([app.srcdir / 'admonitions.txt']) app.builder.compile_specific_catalogs([app.srcdir / 'admonitions.txt'])
actual = get_actual() - actual_on_boot actual = {str(x.relative_to(catalog_dir))
for x in catalog_dir.rglob('*.mo')
if x not in actual_on_boot}
assert actual == {'admonitions.mo'} assert actual == {'admonitions.mo'}
@ -72,10 +68,7 @@ def test_compile_update_catalogs(app, status, warning):
locale_dir = app.srcdir / 'locale' locale_dir = app.srcdir / 'locale'
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
expect = { expect = {x.with_suffix('.mo') for x in set(catalog_dir.rglob('*.po'))}
x.replace('.po', '.mo') actual = set(catalog_dir.rglob('*.mo'))
for x in find_files(catalog_dir, '.po')
}
actual = set(find_files(catalog_dir, '.mo'))
assert actual # not empty assert actual # not empty
assert actual == expect assert actual == expect

View File

@ -1,6 +1,7 @@
"""Test the sphinx.config.Config class.""" """Test the sphinx.config.Config class."""
import time import time
from pathlib import Path
from unittest import mock from unittest import mock
import pytest import pytest
@ -8,7 +9,6 @@ import pytest
import sphinx import sphinx
from sphinx.config import ENUM, Config, check_confval_types from sphinx.config import ENUM, Config, check_confval_types
from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError
from sphinx.testing.path import path
@pytest.mark.sphinx(testroot='config', confoverrides={ @pytest.mark.sphinx(testroot='config', confoverrides={
@ -66,9 +66,9 @@ def test_core_config(app, status, warning):
assert cfg['project'] == cfg.project == 'Sphinx Tests' assert cfg['project'] == cfg.project == 'Sphinx Tests'
def test_config_not_found(tempdir): def test_config_not_found(tmp_path):
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
Config.read(tempdir) Config.read(tmp_path)
def test_extension_values(): def test_extension_values():
@ -131,35 +131,35 @@ def test_overrides_boolean():
@mock.patch("sphinx.config.logger") @mock.patch("sphinx.config.logger")
def test_errors_warnings(logger, tempdir): def test_errors_warnings(logger, tmp_path):
# test the error for syntax errors in the config file # test the error for syntax errors in the config file
(tempdir / 'conf.py').write_text('project = \n', encoding='ascii') (tmp_path / 'conf.py').write_text('project = \n', encoding='ascii')
with pytest.raises(ConfigError) as excinfo: with pytest.raises(ConfigError) as excinfo:
Config.read(tempdir, {}, None) Config.read(tmp_path, {}, None)
assert 'conf.py' in str(excinfo.value) assert 'conf.py' in str(excinfo.value)
# test the automatic conversion of 2.x only code in configs # test the automatic conversion of 2.x only code in configs
(tempdir / 'conf.py').write_text('project = u"Jägermeister"\n', encoding='utf8') (tmp_path / 'conf.py').write_text('project = u"Jägermeister"\n', encoding='utf8')
cfg = Config.read(tempdir, {}, None) cfg = Config.read(tmp_path, {}, None)
cfg.init_values() cfg.init_values()
assert cfg.project == 'Jägermeister' assert cfg.project == 'Jägermeister'
assert logger.called is False assert logger.called is False
def test_errors_if_setup_is_not_callable(tempdir, make_app): def test_errors_if_setup_is_not_callable(tmp_path, make_app):
# test the error to call setup() in the config file # test the error to call setup() in the config file
(tempdir / 'conf.py').write_text('setup = 1', encoding='utf8') (tmp_path / 'conf.py').write_text('setup = 1', encoding='utf8')
with pytest.raises(ConfigError) as excinfo: with pytest.raises(ConfigError) as excinfo:
make_app(srcdir=tempdir) make_app(srcdir=tmp_path)
assert 'callable' in str(excinfo.value) assert 'callable' in str(excinfo.value)
@pytest.fixture() @pytest.fixture()
def make_app_with_empty_project(make_app, tempdir): def make_app_with_empty_project(make_app, tmp_path):
(tempdir / 'conf.py').write_text('', encoding='utf8') (tmp_path / 'conf.py').write_text('', encoding='utf8')
def _make_app(*args, **kw): def _make_app(*args, **kw):
kw.setdefault('srcdir', path(tempdir)) kw.setdefault('srcdir', Path(tmp_path))
return make_app(*args, **kw) return make_app(*args, **kw)
return _make_app return _make_app
@ -187,12 +187,12 @@ def test_needs_sphinx(make_app_with_empty_project):
@mock.patch("sphinx.config.logger") @mock.patch("sphinx.config.logger")
def test_config_eol(logger, tempdir): def test_config_eol(logger, tmp_path):
# test config file's eol patterns: LF, CRLF # test config file's eol patterns: LF, CRLF
configfile = tempdir / 'conf.py' configfile = tmp_path / 'conf.py'
for eol in (b'\n', b'\r\n'): for eol in (b'\n', b'\r\n'):
configfile.write_bytes(b'project = "spam"' + eol) configfile.write_bytes(b'project = "spam"' + eol)
cfg = Config.read(tempdir, {}, None) cfg = Config.read(tmp_path, {}, None)
cfg.init_values() cfg.init_values()
assert cfg.project == 'spam' assert cfg.project == 'spam'
assert logger.called is False assert logger.called is False
@ -384,14 +384,14 @@ def test_nitpick_ignore_regex_fullmatch(app, status, warning):
assert expected in actual assert expected in actual
def test_conf_py_language_none(tempdir): def test_conf_py_language_none(tmp_path):
"""Regression test for #10474.""" """Regression test for #10474."""
# Given a conf.py file with language = None # Given a conf.py file with language = None
(tempdir / 'conf.py').write_text("language = None", encoding='utf-8') (tmp_path / 'conf.py').write_text("language = None", encoding='utf-8')
# When we load conf.py into a Config object # When we load conf.py into a Config object
cfg = Config.read(tempdir, {}, None) cfg = Config.read(tmp_path, {}, None)
cfg.init_values() cfg.init_values()
# Then the language is coerced to English # Then the language is coerced to English
@ -399,14 +399,14 @@ def test_conf_py_language_none(tempdir):
@mock.patch("sphinx.config.logger") @mock.patch("sphinx.config.logger")
def test_conf_py_language_none_warning(logger, tempdir): def test_conf_py_language_none_warning(logger, tmp_path):
"""Regression test for #10474.""" """Regression test for #10474."""
# Given a conf.py file with language = None # Given a conf.py file with language = None
(tempdir / 'conf.py').write_text("language = None", encoding='utf-8') (tmp_path / 'conf.py').write_text("language = None", encoding='utf-8')
# When we load conf.py into a Config object # When we load conf.py into a Config object
Config.read(tempdir, {}, None) Config.read(tmp_path, {}, None)
# Then a warning is raised # Then a warning is raised
assert logger.warning.called assert logger.warning.called
@ -416,28 +416,28 @@ def test_conf_py_language_none_warning(logger, tempdir):
"Falling back to 'en' (English).") "Falling back to 'en' (English).")
def test_conf_py_no_language(tempdir): def test_conf_py_no_language(tmp_path):
"""Regression test for #10474.""" """Regression test for #10474."""
# Given a conf.py file with no language attribute # Given a conf.py file with no language attribute
(tempdir / 'conf.py').write_text("", encoding='utf-8') (tmp_path / 'conf.py').write_text("", encoding='utf-8')
# When we load conf.py into a Config object # When we load conf.py into a Config object
cfg = Config.read(tempdir, {}, None) cfg = Config.read(tmp_path, {}, None)
cfg.init_values() cfg.init_values()
# Then the language is coerced to English # Then the language is coerced to English
assert cfg.language == "en" assert cfg.language == "en"
def test_conf_py_nitpick_ignore_list(tempdir): def test_conf_py_nitpick_ignore_list(tmp_path):
"""Regression test for #11355.""" """Regression test for #11355."""
# Given a conf.py file with no language attribute # Given a conf.py file with no language attribute
(tempdir / 'conf.py').write_text("", encoding='utf-8') (tmp_path / 'conf.py').write_text("", encoding='utf-8')
# When we load conf.py into a Config object # When we load conf.py into a Config object
cfg = Config.read(tempdir, {}, None) cfg = Config.read(tmp_path, {}, None)
cfg.init_values() cfg.init_values()
# Then the default nitpick_ignore[_regex] is an empty list # Then the default nitpick_ignore[_regex] is an empty list

View File

@ -297,8 +297,8 @@ def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
options = {'diff': testroot / 'literal-diff.inc'} options = {'diff': testroot / 'literal-diff.inc'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG) reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read() content, lines = reader.read()
assert content == ("--- " + testroot + "/literal-diff.inc\n" assert content == ("--- " + str(testroot) + "/literal-diff.inc\n"
"+++ " + testroot + "/literal.inc\n" "+++ " + str(testroot) + "/literal.inc\n"
"@@ -6,8 +6,8 @@\n" "@@ -6,8 +6,8 @@\n"
" pass\n" " pass\n"
" \n" " \n"

View File

@ -737,7 +737,7 @@ def _get_obj(app, queryName):
@pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True}) @pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True})
def test_domain_c_build_intersphinx(tempdir, app, status, warning): def test_domain_c_build_intersphinx(tmp_path, app, status, warning):
# a splitting of test_ids_vs_tags0 into the primary directives in a remote project, # a splitting of test_ids_vs_tags0 into the primary directives in a remote project,
# and then the references in the test project # and then the references in the test project
origSource = """\ origSource = """\
@ -754,7 +754,7 @@ def test_domain_c_build_intersphinx(tempdir, app, status, warning):
.. c:type:: _type .. c:type:: _type
.. c:function:: void _functionParam(int param) .. c:function:: void _functionParam(int param)
""" # noqa: F841 """ # noqa: F841
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(b'''\ inv_file.write_bytes(b'''\
# Sphinx inventory version 2 # Sphinx inventory version 2
# Project: C Intersphinx Test # Project: C Intersphinx Test
@ -775,7 +775,7 @@ _union c:union 1 index.html#c.$ -
_var c:member 1 index.html#c.$ - _var c:member 1 index.html#c.$ -
''')) # noqa: W291 ''')) # noqa: W291
app.config.intersphinx_mapping = { app.config.intersphinx_mapping = {
'https://localhost/intersphinx/c/': inv_file, 'https://localhost/intersphinx/c/': str(inv_file),
} }
app.config.intersphinx_cache_limit = 0 app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly

View File

@ -1363,7 +1363,7 @@ def test_domain_cpp_build_field_role(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp-intersphinx', confoverrides={'nitpicky': True}) @pytest.mark.sphinx(testroot='domain-cpp-intersphinx', confoverrides={'nitpicky': True})
def test_domain_cpp_build_intersphinx(tempdir, app, status, warning): def test_domain_cpp_build_intersphinx(tmp_path, app, status, warning):
origSource = """\ origSource = """\
.. cpp:class:: _class .. cpp:class:: _class
.. cpp:struct:: _struct .. cpp:struct:: _struct
@ -1385,7 +1385,7 @@ def test_domain_cpp_build_intersphinx(tempdir, app, status, warning):
.. cpp:function:: void _functionParam(int param) .. cpp:function:: void _functionParam(int param)
.. cpp:function:: template<typename TParam> void _templateParam() .. cpp:function:: template<typename TParam> void _templateParam()
""" # noqa: F841 """ # noqa: F841
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(b'''\ inv_file.write_bytes(b'''\
# Sphinx inventory version 2 # Sphinx inventory version 2
# Project: C Intersphinx Test # Project: C Intersphinx Test
@ -1413,7 +1413,7 @@ _union cpp:union 1 index.html#_CPPv46$ -
_var cpp:member 1 index.html#_CPPv44$ - _var cpp:member 1 index.html#_CPPv44$ -
''')) # noqa: W291 ''')) # noqa: W291
app.config.intersphinx_mapping = { app.config.intersphinx_mapping = {
'https://localhost/intersphinx/cpp/': inv_file, 'https://localhost/intersphinx/cpp/': str(inv_file),
} }
app.config.intersphinx_cache_limit = 0 app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly

View File

@ -1,13 +1,13 @@
"""Test the BuildEnvironment class.""" """Test the BuildEnvironment class."""
import os import os
import shutil import shutil
from pathlib import Path
import pytest import pytest
from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder from sphinx.builders.latex import LaTeXBuilder
from sphinx.environment import CONFIG_CHANGED, CONFIG_EXTENSIONS_CHANGED, CONFIG_NEW, CONFIG_OK from sphinx.environment import CONFIG_CHANGED, CONFIG_EXTENSIONS_CHANGED, CONFIG_NEW, CONFIG_OK
from sphinx.testing.comparer import PathComparer
@pytest.mark.sphinx('dummy', testroot='basic') @pytest.mark.sphinx('dummy', testroot='basic')
@ -101,43 +101,43 @@ def test_env_relfn2path(app):
# relative filename and root document # relative filename and root document
relfn, absfn = app.env.relfn2path('logo.jpg', 'index') relfn, absfn = app.env.relfn2path('logo.jpg', 'index')
assert relfn == 'logo.jpg' assert relfn == 'logo.jpg'
assert absfn == app.srcdir / 'logo.jpg' assert absfn == str(app.srcdir / 'logo.jpg')
# absolute filename and root document # absolute filename and root document
relfn, absfn = app.env.relfn2path('/logo.jpg', 'index') relfn, absfn = app.env.relfn2path('/logo.jpg', 'index')
assert relfn == 'logo.jpg' assert relfn == 'logo.jpg'
assert absfn == app.srcdir / 'logo.jpg' assert absfn == str(app.srcdir / 'logo.jpg')
# relative filename and a document in subdir # relative filename and a document in subdir
relfn, absfn = app.env.relfn2path('logo.jpg', 'subdir/index') relfn, absfn = app.env.relfn2path('logo.jpg', 'subdir/index')
assert relfn == PathComparer('subdir/logo.jpg') assert Path(relfn) == Path('subdir/logo.jpg')
assert absfn == app.srcdir / 'subdir' / 'logo.jpg' assert absfn == str(app.srcdir / 'subdir' / 'logo.jpg')
# absolute filename and a document in subdir # absolute filename and a document in subdir
relfn, absfn = app.env.relfn2path('/logo.jpg', 'subdir/index') relfn, absfn = app.env.relfn2path('/logo.jpg', 'subdir/index')
assert relfn == 'logo.jpg' assert relfn == 'logo.jpg'
assert absfn == app.srcdir / 'logo.jpg' assert absfn == str(app.srcdir / 'logo.jpg')
# relative filename having subdir # relative filename having subdir
relfn, absfn = app.env.relfn2path('images/logo.jpg', 'index') relfn, absfn = app.env.relfn2path('images/logo.jpg', 'index')
assert relfn == 'images/logo.jpg' assert relfn == 'images/logo.jpg'
assert absfn == app.srcdir / 'images' / 'logo.jpg' assert absfn == str(app.srcdir / 'images' / 'logo.jpg')
# relative path traversal # relative path traversal
relfn, absfn = app.env.relfn2path('../logo.jpg', 'index') relfn, absfn = app.env.relfn2path('../logo.jpg', 'index')
assert relfn == '../logo.jpg' assert relfn == '../logo.jpg'
assert absfn == app.srcdir.parent / 'logo.jpg' assert absfn == str(app.srcdir.parent / 'logo.jpg')
# relative path traversal # relative path traversal
relfn, absfn = app.env.relfn2path('subdir/../logo.jpg', 'index') relfn, absfn = app.env.relfn2path('subdir/../logo.jpg', 'index')
assert relfn == 'logo.jpg' assert relfn == 'logo.jpg'
assert absfn == app.srcdir / 'logo.jpg' assert absfn == str(app.srcdir / 'logo.jpg')
# omit docname (w/ current docname) # omit docname (w/ current docname)
app.env.temp_data['docname'] = 'subdir/document' app.env.temp_data['docname'] = 'subdir/document'
relfn, absfn = app.env.relfn2path('images/logo.jpg') relfn, absfn = app.env.relfn2path('images/logo.jpg')
assert relfn == PathComparer('subdir/images/logo.jpg') assert Path(relfn) == Path('subdir/images/logo.jpg')
assert absfn == app.srcdir / 'subdir' / 'images' / 'logo.jpg' assert absfn == str(app.srcdir / 'subdir' / 'images' / 'logo.jpg')
# omit docname (w/o current docname) # omit docname (w/o current docname)
app.env.temp_data.clear() app.env.temp_data.clear()

View File

@ -1,21 +1,21 @@
"""Test the sphinx.apidoc module.""" """Test the sphinx.apidoc module."""
import os.path
from collections import namedtuple from collections import namedtuple
import pytest import pytest
import sphinx.ext.apidoc import sphinx.ext.apidoc
from sphinx.ext.apidoc import main as apidoc_main from sphinx.ext.apidoc import main as apidoc_main
from sphinx.testing.path import path
@pytest.fixture() @pytest.fixture()
def apidoc(rootdir, tempdir, apidoc_params): def apidoc(rootdir, tmp_path, apidoc_params):
_, kwargs = apidoc_params _, kwargs = apidoc_params
coderoot = rootdir / kwargs.get('coderoot', 'test-root') coderoot = rootdir / kwargs.get('coderoot', 'test-root')
outdir = tempdir / 'out' outdir = tmp_path / 'out'
excludes = [coderoot / e for e in kwargs.get('excludes', [])] excludes = [str(coderoot / e) for e in kwargs.get('excludes', [])]
args = ['-o', outdir, '-F', coderoot] + excludes + kwargs.get('options', []) args = ['-o', str(outdir), '-F', str(coderoot)] + excludes + kwargs.get('options', [])
apidoc_main(args) apidoc_main(args)
return namedtuple('apidoc', 'coderoot,outdir')(coderoot, outdir) return namedtuple('apidoc', 'coderoot,outdir')(coderoot, outdir)
@ -37,8 +37,8 @@ def apidoc_params(request):
@pytest.mark.apidoc(coderoot='test-root') @pytest.mark.apidoc(coderoot='test-root')
def test_simple(make_app, apidoc): def test_simple(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'index.rst').isfile() assert (outdir / 'index.rst').is_file()
app = make_app('text', srcdir=outdir) app = make_app('text', srcdir=outdir)
app.build() app.build()
@ -52,10 +52,10 @@ def test_simple(make_app, apidoc):
) )
def test_pep_0420_enabled(make_app, apidoc): def test_pep_0420_enabled(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.b.c.rst').isfile() assert (outdir / 'a.b.c.rst').is_file()
assert (outdir / 'a.b.e.rst').isfile() assert (outdir / 'a.b.e.rst').is_file()
assert (outdir / 'a.b.x.rst').isfile() assert (outdir / 'a.b.x.rst').is_file()
with open(outdir / 'a.b.c.rst', encoding='utf-8') as f: with open(outdir / 'a.b.c.rst', encoding='utf-8') as f:
rst = f.read() rst = f.read()
@ -77,9 +77,9 @@ def test_pep_0420_enabled(make_app, apidoc):
print(app._warning.getvalue()) print(app._warning.getvalue())
builddir = outdir / '_build' / 'text' builddir = outdir / '_build' / 'text'
assert (builddir / 'a.b.c.txt').isfile() assert (builddir / 'a.b.c.txt').is_file()
assert (builddir / 'a.b.e.txt').isfile() assert (builddir / 'a.b.e.txt').is_file()
assert (builddir / 'a.b.x.txt').isfile() assert (builddir / 'a.b.x.txt').is_file()
with open(builddir / 'a.b.c.txt', encoding='utf-8') as f: with open(builddir / 'a.b.c.txt', encoding='utf-8') as f:
txt = f.read() txt = f.read()
@ -100,12 +100,12 @@ def test_pep_0420_enabled(make_app, apidoc):
) )
def test_pep_0420_enabled_separate(make_app, apidoc): def test_pep_0420_enabled_separate(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.b.c.rst').isfile() assert (outdir / 'a.b.c.rst').is_file()
assert (outdir / 'a.b.e.rst').isfile() assert (outdir / 'a.b.e.rst').is_file()
assert (outdir / 'a.b.e.f.rst').isfile() assert (outdir / 'a.b.e.f.rst').is_file()
assert (outdir / 'a.b.x.rst').isfile() assert (outdir / 'a.b.x.rst').is_file()
assert (outdir / 'a.b.x.y.rst').isfile() assert (outdir / 'a.b.x.y.rst').is_file()
with open(outdir / 'a.b.c.rst', encoding='utf-8') as f: with open(outdir / 'a.b.c.rst', encoding='utf-8') as f:
rst = f.read() rst = f.read()
@ -125,11 +125,11 @@ def test_pep_0420_enabled_separate(make_app, apidoc):
print(app._warning.getvalue()) print(app._warning.getvalue())
builddir = outdir / '_build' / 'text' builddir = outdir / '_build' / 'text'
assert (builddir / 'a.b.c.txt').isfile() assert (builddir / 'a.b.c.txt').is_file()
assert (builddir / 'a.b.e.txt').isfile() assert (builddir / 'a.b.e.txt').is_file()
assert (builddir / 'a.b.e.f.txt').isfile() assert (builddir / 'a.b.e.f.txt').is_file()
assert (builddir / 'a.b.x.txt').isfile() assert (builddir / 'a.b.x.txt').is_file()
assert (builddir / 'a.b.x.y.txt').isfile() assert (builddir / 'a.b.x.y.txt').is_file()
with open(builddir / 'a.b.c.txt', encoding='utf-8') as f: with open(builddir / 'a.b.c.txt', encoding='utf-8') as f:
txt = f.read() txt = f.read()
@ -147,7 +147,7 @@ def test_pep_0420_enabled_separate(make_app, apidoc):
@pytest.mark.apidoc(coderoot='test-apidoc-pep420/a') @pytest.mark.apidoc(coderoot='test-apidoc-pep420/a')
def test_pep_0420_disabled(make_app, apidoc): def test_pep_0420_disabled(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert not (outdir / 'a.b.c.rst').exists() assert not (outdir / 'a.b.c.rst').exists()
assert not (outdir / 'a.b.x.rst').exists() assert not (outdir / 'a.b.x.rst').exists()
@ -161,8 +161,8 @@ def test_pep_0420_disabled(make_app, apidoc):
coderoot='test-apidoc-pep420/a/b') coderoot='test-apidoc-pep420/a/b')
def test_pep_0420_disabled_top_level_verify(make_app, apidoc): def test_pep_0420_disabled_top_level_verify(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'c.rst').isfile() assert (outdir / 'c.rst').is_file()
assert not (outdir / 'x.rst').exists() assert not (outdir / 'x.rst').exists()
with open(outdir / 'c.rst', encoding='utf-8') as f: with open(outdir / 'c.rst', encoding='utf-8') as f:
@ -181,8 +181,8 @@ def test_pep_0420_disabled_top_level_verify(make_app, apidoc):
coderoot='test-apidoc-trailing-underscore') coderoot='test-apidoc-trailing-underscore')
def test_trailing_underscore(make_app, apidoc): def test_trailing_underscore(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'package_.rst').isfile() assert (outdir / 'package_.rst').is_file()
app = make_app('text', srcdir=outdir) app = make_app('text', srcdir=outdir)
app.build() app.build()
@ -203,13 +203,13 @@ def test_trailing_underscore(make_app, apidoc):
) )
def test_excludes(apidoc): def test_excludes(apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.rst').isfile() assert (outdir / 'a.rst').is_file()
assert (outdir / 'a.b.rst').isfile() assert (outdir / 'a.b.rst').is_file()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty assert (outdir / 'a.b.c.rst').is_file() # generated because not empty
assert not (outdir / 'a.b.e.rst').isfile() # skipped because of empty after excludes assert not (outdir / 'a.b.e.rst').is_file() # skipped because of empty after excludes
assert (outdir / 'a.b.x.rst').isfile() assert (outdir / 'a.b.x.rst').is_file()
assert (outdir / 'a.b.x.y.rst').isfile() assert (outdir / 'a.b.x.y.rst').is_file()
@pytest.mark.apidoc( @pytest.mark.apidoc(
@ -220,11 +220,11 @@ def test_excludes(apidoc):
def test_excludes_subpackage_should_be_skipped(apidoc): def test_excludes_subpackage_should_be_skipped(apidoc):
"""Subpackage exclusion should work.""" """Subpackage exclusion should work."""
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.rst').isfile() assert (outdir / 'a.rst').is_file()
assert (outdir / 'a.b.rst').isfile() assert (outdir / 'a.b.rst').is_file()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty assert (outdir / 'a.b.c.rst').is_file() # generated because not empty
assert not (outdir / 'a.b.e.f.rst').isfile() # skipped because 'b/e' subpackage is skipped assert not (outdir / 'a.b.e.f.rst').is_file() # skipped because 'b/e' subpackage is skipped
@pytest.mark.apidoc( @pytest.mark.apidoc(
@ -235,11 +235,11 @@ def test_excludes_subpackage_should_be_skipped(apidoc):
def test_excludes_module_should_be_skipped(apidoc): def test_excludes_module_should_be_skipped(apidoc):
"""Module exclusion should work.""" """Module exclusion should work."""
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.rst').isfile() assert (outdir / 'a.rst').is_file()
assert (outdir / 'a.b.rst').isfile() assert (outdir / 'a.b.rst').is_file()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty assert (outdir / 'a.b.c.rst').is_file() # generated because not empty
assert not (outdir / 'a.b.e.f.rst').isfile() # skipped because of empty after excludes assert not (outdir / 'a.b.e.f.rst').is_file() # skipped because of empty after excludes
@pytest.mark.apidoc( @pytest.mark.apidoc(
@ -250,11 +250,11 @@ def test_excludes_module_should_be_skipped(apidoc):
def test_excludes_module_should_not_be_skipped(apidoc): def test_excludes_module_should_not_be_skipped(apidoc):
"""Module should be included if no excludes are used.""" """Module should be included if no excludes are used."""
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'a.rst').isfile() assert (outdir / 'a.rst').is_file()
assert (outdir / 'a.b.rst').isfile() assert (outdir / 'a.b.rst').is_file()
assert (outdir / 'a.b.c.rst').isfile() # generated because not empty assert (outdir / 'a.b.c.rst').is_file() # generated because not empty
assert (outdir / 'a.b.e.f.rst').isfile() # skipped because of empty after excludes assert (outdir / 'a.b.e.f.rst').is_file() # skipped because of empty after excludes
@pytest.mark.apidoc( @pytest.mark.apidoc(
@ -268,8 +268,8 @@ def test_excludes_module_should_not_be_skipped(apidoc):
) )
def test_multibyte_parameters(make_app, apidoc): def test_multibyte_parameters(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'index.rst').isfile() assert (outdir / 'index.rst').is_file()
conf_py = (outdir / 'conf.py').read_text(encoding='utf8') conf_py = (outdir / 'conf.py').read_text(encoding='utf8')
assert "project = 'プロジェクト名'" in conf_py assert "project = 'プロジェクト名'" in conf_py
@ -289,7 +289,7 @@ def test_multibyte_parameters(make_app, apidoc):
) )
def test_extension_parsed(make_app, apidoc): def test_extension_parsed(make_app, apidoc):
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
with open(outdir / 'conf.py', encoding='utf-8') as f: with open(outdir / 'conf.py', encoding='utf-8') as f:
rst = f.read() rst = f.read()
@ -307,7 +307,7 @@ def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
and what is created. This is the variant with pep420 enabled. and what is created. This is the variant with pep420 enabled.
""" """
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
toc = extract_toc(outdir / 'mypackage.rst') toc = extract_toc(outdir / 'mypackage.rst')
@ -319,7 +319,7 @@ def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
continue continue
found_refs.append(ref) found_refs.append(ref)
filename = f"{ref}.rst" filename = f"{ref}.rst"
if not (outdir / filename).isfile(): if not (outdir / filename).is_file():
missing_files.append(filename) missing_files.append(filename)
assert len(missing_files) == 0, \ assert len(missing_files) == 0, \
@ -337,7 +337,7 @@ def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
and what is created. This is the variant with pep420 disabled. and what is created. This is the variant with pep420 disabled.
""" """
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
toc = extract_toc(outdir / 'mypackage.rst') toc = extract_toc(outdir / 'mypackage.rst')
@ -349,7 +349,7 @@ def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
continue continue
filename = f"{ref}.rst" filename = f"{ref}.rst"
found_refs.append(ref) found_refs.append(ref)
if not (outdir / filename).isfile(): if not (outdir / filename).is_file():
missing_files.append(filename) missing_files.append(filename)
assert len(missing_files) == 0, \ assert len(missing_files) == 0, \
@ -382,44 +382,44 @@ def test_subpackage_in_toc(make_app, apidoc):
are not skipped (issue #4520) are not skipped (issue #4520)
""" """
outdir = apidoc.outdir outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile() assert (outdir / 'conf.py').is_file()
assert (outdir / 'parent.rst').isfile() assert (outdir / 'parent.rst').is_file()
with open(outdir / 'parent.rst', encoding='utf-8') as f: with open(outdir / 'parent.rst', encoding='utf-8') as f:
parent = f.read() parent = f.read()
assert 'parent.child' in parent assert 'parent.child' in parent
assert (outdir / 'parent.child.rst').isfile() assert (outdir / 'parent.child.rst').is_file()
with open(outdir / 'parent.child.rst', encoding='utf-8') as f: with open(outdir / 'parent.child.rst', encoding='utf-8') as f:
parent_child = f.read() parent_child = f.read()
assert 'parent.child.foo' in parent_child assert 'parent.child.foo' in parent_child
assert (outdir / 'parent.child.foo.rst').isfile() assert (outdir / 'parent.child.foo.rst').is_file()
def test_private(tempdir): def test_private(tmp_path):
(tempdir / 'hello.py').write_text('', encoding='utf8') (tmp_path / 'hello.py').write_text('', encoding='utf8')
(tempdir / '_world.py').write_text('', encoding='utf8') (tmp_path / '_world.py').write_text('', encoding='utf8')
# without --private option # without --private option
apidoc_main(['-o', tempdir, tempdir]) apidoc_main(['-o', str(tmp_path), str(tmp_path)])
assert (tempdir / 'hello.rst').exists() assert (tmp_path / 'hello.rst').exists()
assert ':private-members:' not in (tempdir / 'hello.rst').read_text(encoding='utf8') assert ':private-members:' not in (tmp_path / 'hello.rst').read_text(encoding='utf8')
assert not (tempdir / '_world.rst').exists() assert not (tmp_path / '_world.rst').exists()
# with --private option # with --private option
apidoc_main(['--private', '-f', '-o', tempdir, tempdir]) apidoc_main(['--private', '-f', '-o', str(tmp_path), str(tmp_path)])
assert (tempdir / 'hello.rst').exists() assert (tmp_path / 'hello.rst').exists()
assert ':private-members:' in (tempdir / 'hello.rst').read_text(encoding='utf8') assert ':private-members:' in (tmp_path / 'hello.rst').read_text(encoding='utf8')
assert (tempdir / '_world.rst').exists() assert (tmp_path / '_world.rst').exists()
def test_toc_file(tempdir): def test_toc_file(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'module').makedirs() (outdir / 'module').mkdir(parents=True, exist_ok=True)
(outdir / 'example.py').write_text('', encoding='utf8') (outdir / 'example.py').write_text('', encoding='utf8')
(outdir / 'module' / 'example.py').write_text('', encoding='utf8') (outdir / 'module' / 'example.py').write_text('', encoding='utf8')
apidoc_main(['-o', tempdir, tempdir]) apidoc_main(['-o', str(tmp_path), str(tmp_path)])
assert (outdir / 'modules.rst').exists() assert (outdir / 'modules.rst').exists()
content = (outdir / 'modules.rst').read_text(encoding='utf8') content = (outdir / 'modules.rst').read_text(encoding='utf8')
@ -432,10 +432,10 @@ def test_toc_file(tempdir):
" example\n") " example\n")
def test_module_file(tempdir): def test_module_file(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'example.py').write_text('', encoding='utf8') (outdir / 'example.py').write_text('', encoding='utf8')
apidoc_main(['-o', tempdir, tempdir]) apidoc_main(['-o', str(tmp_path), str(tmp_path)])
assert (outdir / 'example.rst').exists() assert (outdir / 'example.rst').exists()
content = (outdir / 'example.rst').read_text(encoding='utf8') content = (outdir / 'example.rst').read_text(encoding='utf8')
@ -448,10 +448,10 @@ def test_module_file(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_module_file_noheadings(tempdir): def test_module_file_noheadings(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'example.py').write_text('', encoding='utf8') (outdir / 'example.py').write_text('', encoding='utf8')
apidoc_main(['--no-headings', '-o', tempdir, tempdir]) apidoc_main(['--no-headings', '-o', str(tmp_path), str(tmp_path)])
assert (outdir / 'example.rst').exists() assert (outdir / 'example.rst').exists()
content = (outdir / 'example.rst').read_text(encoding='utf8') content = (outdir / 'example.rst').read_text(encoding='utf8')
@ -461,15 +461,15 @@ def test_module_file_noheadings(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_package_file(tempdir): def test_package_file(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'testpkg').makedirs() (outdir / 'testpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8') (outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8')
(outdir / 'testpkg' / 'hello.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'hello.py').write_text('', encoding='utf8')
(outdir / 'testpkg' / 'world.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'world.py').write_text('', encoding='utf8')
(outdir / 'testpkg' / 'subpkg').makedirs() (outdir / 'testpkg' / 'subpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / 'subpkg' / '__init__.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'subpkg' / '__init__.py').write_text('', encoding='utf8')
apidoc_main(['-o', tempdir, tempdir / 'testpkg']) apidoc_main(['-o', str(outdir), str(outdir / 'testpkg')])
assert (outdir / 'testpkg.rst').exists() assert (outdir / 'testpkg.rst').exists()
assert (outdir / 'testpkg.subpkg.rst').exists() assert (outdir / 'testpkg.subpkg.rst').exists()
@ -525,12 +525,12 @@ def test_package_file(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_package_file_separate(tempdir): def test_package_file_separate(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'testpkg').makedirs() (outdir / 'testpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8') (outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8')
(outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8')
apidoc_main(['--separate', '-o', tempdir, tempdir / 'testpkg']) apidoc_main(['--separate', '-o', str(tmp_path), str(tmp_path / 'testpkg')])
assert (outdir / 'testpkg.rst').exists() assert (outdir / 'testpkg.rst').exists()
assert (outdir / 'testpkg.example.rst').exists() assert (outdir / 'testpkg.example.rst').exists()
@ -564,12 +564,12 @@ def test_package_file_separate(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_package_file_module_first(tempdir): def test_package_file_module_first(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'testpkg').makedirs() (outdir / 'testpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8') (outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8')
(outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8')
apidoc_main(['--module-first', '-o', tempdir, tempdir]) apidoc_main(['--module-first', '-o', str(tmp_path), str(tmp_path)])
content = (outdir / 'testpkg.rst').read_text(encoding='utf8') content = (outdir / 'testpkg.rst').read_text(encoding='utf8')
assert content == ("testpkg package\n" assert content == ("testpkg package\n"
@ -592,11 +592,11 @@ def test_package_file_module_first(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_package_file_without_submodules(tempdir): def test_package_file_without_submodules(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'testpkg').makedirs() (outdir / 'testpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8') (outdir / 'testpkg' / '__init__.py').write_text('', encoding='utf8')
apidoc_main(['-o', tempdir, tempdir / 'testpkg']) apidoc_main(['-o', str(tmp_path), str(tmp_path / 'testpkg')])
assert (outdir / 'testpkg.rst').exists() assert (outdir / 'testpkg.rst').exists()
content = (outdir / 'testpkg.rst').read_text(encoding='utf8') content = (outdir / 'testpkg.rst').read_text(encoding='utf8')
@ -612,11 +612,11 @@ def test_package_file_without_submodules(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_namespace_package_file(tempdir): def test_namespace_package_file(tmp_path):
outdir = path(tempdir) outdir = tmp_path
(outdir / 'testpkg').makedirs() (outdir / 'testpkg').mkdir(parents=True, exist_ok=True)
(outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8') (outdir / 'testpkg' / 'example.py').write_text('', encoding='utf8')
apidoc_main(['--implicit-namespace', '-o', tempdir, tempdir / 'testpkg']) apidoc_main(['--implicit-namespace', '-o', str(tmp_path), str(tmp_path / 'testpkg')])
assert (outdir / 'testpkg.rst').exists() assert (outdir / 'testpkg.rst').exists()
content = (outdir / 'testpkg.rst').read_text(encoding='utf8') content = (outdir / 'testpkg.rst').read_text(encoding='utf8')
@ -637,7 +637,7 @@ def test_namespace_package_file(tempdir):
" :show-inheritance:\n") " :show-inheritance:\n")
def test_no_duplicates(rootdir, tempdir): def test_no_duplicates(rootdir, tmp_path):
"""Make sure that a ".pyx" and ".so" don't cause duplicate listings. """Make sure that a ".pyx" and ".so" don't cause duplicate listings.
We can't use pytest.mark.apidoc here as we use a different set of arguments We can't use pytest.mark.apidoc here as we use a different set of arguments
@ -650,11 +650,11 @@ def test_no_duplicates(rootdir, tempdir):
sphinx.ext.apidoc.PY_SUFFIXES += ('.so',) sphinx.ext.apidoc.PY_SUFFIXES += ('.so',)
package = rootdir / 'test-apidoc-duplicates' / 'fish_licence' package = rootdir / 'test-apidoc-duplicates' / 'fish_licence'
outdir = tempdir / 'out' outdir = tmp_path / 'out'
apidoc_main(['-o', outdir, "-T", package, "--implicit-namespaces"]) apidoc_main(['-o', str(outdir), "-T", str(package), "--implicit-namespaces"])
# Ensure the module has been documented # Ensure the module has been documented
assert (outdir / 'fish_licence.rst').isfile() assert os.path.isfile(outdir / 'fish_licence.rst')
# Ensure the submodule only appears once # Ensure the submodule only appears once
text = (outdir / 'fish_licence.rst').read_text(encoding="utf-8") text = (outdir / 'fish_licence.rst').read_text(encoding="utf-8")

View File

@ -2,6 +2,7 @@
import sys import sys
from io import StringIO from io import StringIO
from pathlib import Path
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
import pytest import pytest
@ -199,7 +200,7 @@ def str_content(elem):
def test_escaping(app, status, warning): def test_escaping(app, status, warning):
app.builder.build_all() app.builder.build_all()
outdir = app.builder.outdir outdir = Path(app.builder.outdir)
docpage = outdir / 'underscore_module_.xml' docpage = outdir / 'underscore_module_.xml'
assert docpage.exists() assert docpage.exists()
@ -452,7 +453,7 @@ def test_autosummary_generate_overwrite1(app_params, make_app):
args, kwargs = app_params args, kwargs = app_params
srcdir = kwargs.get('srcdir') srcdir = kwargs.get('srcdir')
(srcdir / 'generated').makedirs(exist_ok=True) (srcdir / 'generated').mkdir(parents=True, exist_ok=True)
(srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('', encoding='utf8') (srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('', encoding='utf8')
app = make_app(*args, **kwargs) app = make_app(*args, **kwargs)
@ -467,7 +468,7 @@ def test_autosummary_generate_overwrite2(app_params, make_app):
args, kwargs = app_params args, kwargs = app_params
srcdir = kwargs.get('srcdir') srcdir = kwargs.get('srcdir')
(srcdir / 'generated').makedirs(exist_ok=True) (srcdir / 'generated').mkdir(parents=True, exist_ok=True)
(srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('', encoding='utf8') (srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('', encoding='utf8')
app = make_app(*args, **kwargs) app = make_app(*args, **kwargs)
@ -669,8 +670,8 @@ def test_invalid_autosummary_generate(app, status, warning):
assert 'WARNING: autosummary_generate: file not found: unknown.rst' in warning.getvalue() assert 'WARNING: autosummary_generate: file not found: unknown.rst' in warning.getvalue()
def test_autogen(rootdir, tempdir): def test_autogen(rootdir, tmp_path):
with chdir(rootdir / 'test-templating'): with chdir(rootdir / 'test-templating'):
args = ['-o', tempdir, '-t', '.', 'autosummary_templating.txt'] args = ['-o', str(tmp_path), '-t', '.', 'autosummary_templating.txt']
autogen_main(args) autogen_main(args)
assert (tempdir / 'sphinx.application.TemplateBridge.rst').exists() assert (tmp_path / 'sphinx.application.TemplateBridge.rst').exists()

View File

@ -214,7 +214,7 @@ def test_import_classes(rootdir):
from sphinx.util.i18n import CatalogInfo from sphinx.util.i18n import CatalogInfo
try: try:
sys.path.append(rootdir / 'test-ext-inheritance_diagram') sys.path.append(str(rootdir / 'test-ext-inheritance_diagram'))
from example.sphinx import DummyClass from example.sphinx import DummyClass
# got exception for unknown class or module # got exception for unknown class or module

View File

@ -91,14 +91,14 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue") @pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
def test_missing_reference(tempdir, app, status, warning): def test_missing_reference(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
'py3k': ('https://docs.python.org/py3k/', inv_file), 'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
'py3krel': ('py3k', inv_file), # relative path 'py3krel': ('py3k', str(inv_file)), # relative path
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir 'py3krelparent': ('../../py3k', str(inv_file)), # relative path, parent dir
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -169,11 +169,11 @@ def test_missing_reference(tempdir, app, status, warning):
assert rn['refuri'] == 'https://docs.python.org/docname.html' assert rn['refuri'] == 'https://docs.python.org/docname.html'
def test_missing_reference_pydomain(tempdir, app, status, warning): def test_missing_reference_pydomain(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -209,11 +209,11 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
assert rn.astext() == 'A TERM' assert rn.astext() == 'A TERM'
def test_missing_reference_stddomain(tempdir, app, status, warning): def test_missing_reference_stddomain(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'cmd': ('https://docs.python.org/', inv_file), 'cmd': ('https://docs.python.org/', str(inv_file)),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -240,11 +240,11 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain') @pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
def test_missing_reference_cppdomain(tempdir, app, status, warning): def test_missing_reference_cppdomain(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -266,11 +266,11 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
' title="(in foo v2.0)"><span class="n"><span class="pre">bartype</span></span></a>' in html) ' title="(in foo v2.0)"><span class="n"><span class="pre">bartype</span></span></a>' in html)
def test_missing_reference_jsdomain(tempdir, app, status, warning): def test_missing_reference_jsdomain(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -290,11 +290,11 @@ def test_missing_reference_jsdomain(tempdir, app, status, warning):
assert rn.astext() == 'baz()' assert rn.astext() == 'baz()'
def test_missing_reference_disabled_domain(tempdir, app, status, warning): def test_missing_reference_disabled_domain(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'inv': ('https://docs.python.org/', inv_file), 'inv': ('https://docs.python.org/', str(inv_file)),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -353,11 +353,11 @@ def test_missing_reference_disabled_domain(tempdir, app, status, warning):
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue") @pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
def test_inventory_not_having_version(tempdir, app, status, warning): def test_inventory_not_having_version(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2_not_having_version) inv_file.write_bytes(inventory_v2_not_having_version)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -371,20 +371,20 @@ def test_inventory_not_having_version(tempdir, app, status, warning):
assert rn[0].astext() == 'Long Module desc' assert rn[0].astext() == 'Long Module desc'
def test_load_mappings_warnings(tempdir, app, status, warning): def test_load_mappings_warnings(tmp_path, app, status, warning):
""" """
load_mappings issues a warning if new-style mapping load_mappings issues a warning if new-style mapping
identifiers are not string identifiers are not string
""" """
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, { set_config(app, {
'https://docs.python.org/': inv_file, 'https://docs.python.org/': str(inv_file),
'py3k': ('https://docs.python.org/py3k/', inv_file), 'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file), 'repoze.workflow': ('http://docs.repoze.org/workflow/', str(inv_file)),
'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/', 'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
inv_file), str(inv_file)),
12345: ('http://www.sphinx-doc.org/en/stable/', inv_file), 12345: ('http://www.sphinx-doc.org/en/stable/', str(inv_file)),
}) })
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
@ -396,8 +396,8 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
assert 'intersphinx identifier 12345 is not string. Ignored' in warnings[1] assert 'intersphinx identifier 12345 is not string. Ignored' in warnings[1]
def test_load_mappings_fallback(tempdir, app, status, warning): def test_load_mappings_fallback(tmp_path, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
set_config(app, {}) set_config(app, {})
@ -419,7 +419,7 @@ def test_load_mappings_fallback(tempdir, app, status, warning):
# add fallbacks to mapping # add fallbacks to mapping
app.config.intersphinx_mapping = { app.config.intersphinx_mapping = {
'fallback': ('https://docs.python.org/py3k/', ('/invalid/inventory/path', 'fallback': ('https://docs.python.org/py3k/', ('/invalid/inventory/path',
inv_file)), str(inv_file))),
} }
normalize_intersphinx_mapping(app, app.config) normalize_intersphinx_mapping(app, app.config)
load_mappings(app) load_mappings(app)
@ -493,9 +493,9 @@ def test_inspect_main_noargs(capsys):
assert stderr == expected + "\n" assert stderr == expected + "\n"
def test_inspect_main_file(capsys, tempdir): def test_inspect_main_file(capsys, tmp_path):
"""inspect_main interface, with file argument""" """inspect_main interface, with file argument"""
inv_file = tempdir / 'inventory' inv_file = tmp_path / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
inspect_main([str(inv_file)]) inspect_main([str(inv_file)])
@ -532,7 +532,7 @@ def test_intersphinx_role(app, warning):
inv_file = app.srcdir / 'inventory' inv_file = app.srcdir / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = { app.config.intersphinx_mapping = {
'inv': ('http://example.org/', inv_file), 'inv': ('http://example.org/', str(inv_file)),
} }
app.config.intersphinx_cache_limit = 0 app.config.intersphinx_cache_limit = 0
app.config.nitpicky = True app.config.nitpicky = True

View File

@ -4,8 +4,11 @@ Runs the text builder in the test root.
""" """
import os import os
import os.path
import re import re
import shutil
import time import time
from pathlib import Path
import pygments import pygments
import pytest import pytest
@ -20,7 +23,6 @@ from sphinx.testing.util import (
assert_re_search, assert_re_search,
assert_startswith, assert_startswith,
etree_parse, etree_parse,
path,
strip_escseq, strip_escseq,
) )
from sphinx.util.nodes import NodeMatcher from sphinx.util.nodes import NodeMatcher
@ -35,28 +37,29 @@ sphinx_intl = pytest.mark.sphinx(
def read_po(pathname): def read_po(pathname):
with pathname.open(encoding='utf-8') as f: with open(pathname, encoding='utf-8') as f:
return pofile.read_po(f) return pofile.read_po(f)
def write_mo(pathname, po): def write_mo(pathname, po):
with pathname.open('wb') as f: with open(pathname, 'wb') as f:
return mofile.write_mo(f, po) return mofile.write_mo(f, po)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def _setup_intl(app_params): def _setup_intl(app_params):
srcdir = path(app_params.kwargs['srcdir']) assert isinstance(app_params.kwargs['srcdir'], Path)
srcdir = app_params.kwargs['srcdir']
for dirpath, _dirs, files in os.walk(srcdir): for dirpath, _dirs, files in os.walk(srcdir):
dirpath = path(dirpath) dirpath = Path(dirpath)
for f in [f for f in files if f.endswith('.po')]: for f in [f for f in files if f.endswith('.po')]:
po = dirpath / f po = str(dirpath / f)
mo = srcdir / 'xx' / 'LC_MESSAGES' / ( mo = srcdir / 'xx' / 'LC_MESSAGES' / (
os.path.relpath(po[:-3], srcdir) + '.mo') os.path.relpath(po[:-3], srcdir) + '.mo')
if not mo.parent.exists(): if not mo.parent.exists():
mo.parent.makedirs() mo.parent.mkdir(parents=True, exist_ok=True)
if not mo.exists() or mo.stat().st_mtime < po.stat().st_mtime: if not mo.exists() or os.stat(mo).st_mtime < os.stat(po).st_mtime:
# compile .mo file only if needed # compile .mo file only if needed
write_mo(mo, read_po(po)) write_mo(mo, read_po(po))
@ -697,13 +700,13 @@ def test_gettext_dont_rebuild_mo(make_app, app_params):
# When rewriting the timestamp of mo file, the number of documents to be # When rewriting the timestamp of mo file, the number of documents to be
# updated will be changed. # updated will be changed.
mtime = (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime mtime = (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime
(app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 5, mtime + 5)) os.utime(app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo', (mtime + 5, mtime + 5))
update_targets = get_update_targets(app0) update_targets = get_update_targets(app0)
assert update_targets[1] == {'bom'}, update_targets assert update_targets[1] == {'bom'}, update_targets
# Because doctree for gettext builder can not be shared with other builders, # Because doctree for gettext builder can not be shared with other builders,
# erase doctreedir before gettext build. # erase doctreedir before gettext build.
app0.doctreedir.rmtree() shutil.rmtree(app0.doctreedir)
# phase2: build document with gettext builder. # phase2: build document with gettext builder.
# The mo file in the srcdir directory is retained. # The mo file in the srcdir directory is retained.
@ -715,7 +718,7 @@ def test_gettext_dont_rebuild_mo(make_app, app_params):
assert update_targets[1] == set(), update_targets assert update_targets[1] == set(), update_targets
# Even if the timestamp of the mo file is updated, the number of documents # Even if the timestamp of the mo file is updated, the number of documents
# to be updated is 0. gettext builder does not rebuild because of mo update. # to be updated is 0. gettext builder does not rebuild because of mo update.
(app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 10, mtime + 10)) os.utime(app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo', (mtime + 10, mtime + 10))
update_targets = get_update_targets(app) update_targets = get_update_targets(app)
assert update_targets[1] == set(), update_targets assert update_targets[1] == set(), update_targets
@ -867,7 +870,7 @@ def test_html_rebuild_mo(app):
assert len(updated) == 0 assert len(updated) == 0
mtime = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime mtime = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime
(app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 5, mtime + 5)) os.utime(app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo', (mtime + 5, mtime + 5))
app.env.find_files(app.config, app.builder) app.env.find_files(app.config, app.builder)
_, updated, _ = app.env.get_outdated_files(config_changed=False) _, updated, _ = app.env.get_outdated_files(config_changed=False)
assert len(updated) == 1 assert len(updated) == 1
@ -1401,7 +1404,7 @@ def getwarning(warnings):
}) })
def test_gettext_allow_fuzzy_translations(app): def test_gettext_allow_fuzzy_translations(app):
locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES' locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES'
locale_dir.makedirs() locale_dir.mkdir(parents=True, exist_ok=True)
with (locale_dir / 'index.po').open('wb') as f: with (locale_dir / 'index.po').open('wb') as f:
catalog = Catalog() catalog = Catalog()
catalog.add('features', 'FEATURES', flags=('fuzzy',)) catalog.add('features', 'FEATURES', flags=('fuzzy',))
@ -1420,7 +1423,7 @@ def test_gettext_allow_fuzzy_translations(app):
}) })
def test_gettext_disallow_fuzzy_translations(app): def test_gettext_disallow_fuzzy_translations(app):
locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES' locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES'
locale_dir.makedirs() locale_dir.mkdir(parents=True, exist_ok=True)
with (locale_dir / 'index.po').open('wb') as f: with (locale_dir / 'index.po').open('wb') as f:
catalog = Catalog() catalog = Catalog()
catalog.add('features', 'FEATURES', flags=('fuzzy',)) catalog.add('features', 'FEATURES', flags=('fuzzy',))
@ -1439,7 +1442,7 @@ def test_customize_system_message(make_app, app_params, sphinx_test_tempdir):
# prepare message catalog (.po) # prepare message catalog (.po)
locale_dir = sphinx_test_tempdir / 'basic' / 'locales' / 'de' / 'LC_MESSAGES' locale_dir = sphinx_test_tempdir / 'basic' / 'locales' / 'de' / 'LC_MESSAGES'
locale_dir.makedirs() locale_dir.mkdir(parents=True, exist_ok=True)
with (locale_dir / 'sphinx.po').open('wb') as f: with (locale_dir / 'sphinx.po').open('wb') as f:
catalog = Catalog() catalog = Catalog()
catalog.add('Quick search', 'QUICK SEARCH') catalog.add('Quick search', 'QUICK SEARCH')

View File

@ -6,7 +6,7 @@ from sphinx.project import Project
def test_project_discover(rootdir): def test_project_discover(rootdir):
project = Project(rootdir / 'test-root', {}) project = Project(str(rootdir / 'test-root'), {})
docnames = {'autodoc', 'bom', 'extapi', 'extensions', 'footnote', 'images', docnames = {'autodoc', 'bom', 'extapi', 'extensions', 'footnote', 'images',
'includes', 'index', 'lists', 'markup', 'math', 'objects', 'includes', 'index', 'lists', 'markup', 'math', 'objects',
@ -48,7 +48,7 @@ def test_project_path2doc(app):
assert project.path2doc('index.foo.rst') == 'index.foo' assert project.path2doc('index.foo.rst') == 'index.foo'
assert project.path2doc('index') is None assert project.path2doc('index') is None
assert project.path2doc('path/to/index.rst') == 'path/to/index' assert project.path2doc('path/to/index.rst') == 'path/to/index'
assert project.path2doc(app.srcdir / 'to/index.rst') == 'to/index' assert project.path2doc(str(app.srcdir / 'to/index.rst')) == 'to/index'
@pytest.mark.sphinx(srcdir='project_doc2path', testroot='basic') @pytest.mark.sphinx(srcdir='project_doc2path', testroot='basic')
@ -56,17 +56,17 @@ def test_project_doc2path(app):
source_suffix = {'.rst': 'restructuredtext', '.txt': 'restructuredtext'} source_suffix = {'.rst': 'restructuredtext', '.txt': 'restructuredtext'}
project = Project(app.srcdir, source_suffix) project = Project(app.srcdir, source_suffix)
assert project.doc2path('index') == (app.srcdir / 'index.rst') assert project.doc2path('index') == str(app.srcdir / 'index.rst')
# first source_suffix is used for missing file # first source_suffix is used for missing file
assert project.doc2path('foo') == (app.srcdir / 'foo.rst') assert project.doc2path('foo') == str(app.srcdir / 'foo.rst')
# matched source_suffix is used if exists # matched source_suffix is used if exists
(app.srcdir / 'foo.txt').write_text('', encoding='utf8') (app.srcdir / 'foo.txt').write_text('', encoding='utf8')
assert project.doc2path('foo') == (app.srcdir / 'foo.txt') assert project.doc2path('foo') == str(app.srcdir / 'foo.txt')
# absolute path # absolute path
assert project.doc2path('index', basedir=True) == (app.srcdir / 'index.rst') assert project.doc2path('index', basedir=True) == str(app.srcdir / 'index.rst')
# relative path # relative path
assert project.doc2path('index', basedir=False) == 'index.rst' assert project.doc2path('index', basedir=False) == 'index.rst'

View File

@ -40,7 +40,7 @@ def test_ModuleAnalyzer_for_module(rootdir):
assert analyzer.srcname in (SPHINX_MODULE_PATH, assert analyzer.srcname in (SPHINX_MODULE_PATH,
os.path.abspath(SPHINX_MODULE_PATH)) os.path.abspath(SPHINX_MODULE_PATH))
path = rootdir / 'test-pycode' path = str(rootdir / 'test-pycode')
sys.path.insert(0, path) sys.path.insert(0, path)
try: try:
analyzer = ModuleAnalyzer.for_module('cp_1251_coded') analyzer = ModuleAnalyzer.for_module('cp_1251_coded')

View File

@ -85,9 +85,9 @@ def test_do_prompt_with_nonascii():
assert result == '\u30c9\u30a4\u30c4' assert result == '\u30c9\u30a4\u30c4'
def test_quickstart_defaults(tempdir): def test_quickstart_defaults(tmp_path):
answers = { answers = {
'Root path': tempdir, 'Root path': str(tmp_path),
'Project name': 'Sphinx Test', 'Project name': 'Sphinx Test',
'Author name': 'Georg Brandl', 'Author name': 'Georg Brandl',
'Project version': '0.1', 'Project version': '0.1',
@ -97,8 +97,8 @@ def test_quickstart_defaults(tempdir):
qs.ask_user(d) qs.ask_user(d)
qs.generate(d) qs.generate(d)
conffile = tempdir / 'conf.py' conffile = tmp_path / 'conf.py'
assert conffile.isfile() assert conffile.is_file()
ns = {} ns = {}
exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102
assert ns['extensions'] == [] assert ns['extensions'] == []
@ -109,16 +109,16 @@ def test_quickstart_defaults(tempdir):
assert ns['release'] == '0.1' assert ns['release'] == '0.1'
assert ns['html_static_path'] == ['_static'] assert ns['html_static_path'] == ['_static']
assert (tempdir / '_static').isdir() assert (tmp_path / '_static').is_dir()
assert (tempdir / '_templates').isdir() assert (tmp_path / '_templates').is_dir()
assert (tempdir / 'index.rst').isfile() assert (tmp_path / 'index.rst').is_file()
assert (tempdir / 'Makefile').isfile() assert (tmp_path / 'Makefile').is_file()
assert (tempdir / 'make.bat').isfile() assert (tmp_path / 'make.bat').is_file()
def test_quickstart_all_answers(tempdir): def test_quickstart_all_answers(tmp_path):
answers = { answers = {
'Root path': tempdir, 'Root path': str(tmp_path),
'Separate source and build': 'y', 'Separate source and build': 'y',
'Name prefix for templates': '.', 'Name prefix for templates': '.',
'Project name': 'STASI™', 'Project name': 'STASI™',
@ -147,8 +147,8 @@ def test_quickstart_all_answers(tempdir):
qs.ask_user(d) qs.ask_user(d)
qs.generate(d) qs.generate(d)
conffile = tempdir / 'source' / 'conf.py' conffile = tmp_path / 'source' / 'conf.py'
assert conffile.isfile() assert conffile.is_file()
ns = {} ns = {}
exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102
assert ns['extensions'] == [ assert ns['extensions'] == [
@ -165,15 +165,15 @@ def test_quickstart_all_answers(tempdir):
assert ns['todo_include_todos'] is True assert ns['todo_include_todos'] is True
assert ns['html_static_path'] == ['.static'] assert ns['html_static_path'] == ['.static']
assert (tempdir / 'build').isdir() assert (tmp_path / 'build').is_dir()
assert (tempdir / 'source' / '.static').isdir() assert (tmp_path / 'source' / '.static').is_dir()
assert (tempdir / 'source' / '.templates').isdir() assert (tmp_path / 'source' / '.templates').is_dir()
assert (tempdir / 'source' / 'contents.txt').isfile() assert (tmp_path / 'source' / 'contents.txt').is_file()
def test_generated_files_eol(tempdir): def test_generated_files_eol(tmp_path):
answers = { answers = {
'Root path': tempdir, 'Root path': str(tmp_path),
'Project name': 'Sphinx Test', 'Project name': 'Sphinx Test',
'Author name': 'Georg Brandl', 'Author name': 'Georg Brandl',
'Project version': '0.1', 'Project version': '0.1',
@ -187,13 +187,13 @@ def test_generated_files_eol(tempdir):
content = filename.read_bytes().decode() content = filename.read_bytes().decode()
assert all(l[-len(eol):] == eol for l in content.splitlines(keepends=True)) assert all(l[-len(eol):] == eol for l in content.splitlines(keepends=True))
assert_eol(tempdir / 'make.bat', '\r\n') assert_eol(tmp_path / 'make.bat', '\r\n')
assert_eol(tempdir / 'Makefile', '\n') assert_eol(tmp_path / 'Makefile', '\n')
def test_quickstart_and_build(tempdir): def test_quickstart_and_build(tmp_path):
answers = { answers = {
'Root path': tempdir, 'Root path': str(tmp_path),
'Project name': 'Fullwidth characters: \u30c9\u30a4\u30c4', 'Project name': 'Fullwidth characters: \u30c9\u30a4\u30c4',
'Author name': 'Georg Brandl', 'Author name': 'Georg Brandl',
'Project version': '0.1', 'Project version': '0.1',
@ -204,10 +204,10 @@ def test_quickstart_and_build(tempdir):
qs.generate(d) qs.generate(d)
app = application.Sphinx( app = application.Sphinx(
tempdir, # srcdir tmp_path, # srcdir
tempdir, # confdir tmp_path, # confdir
(tempdir / '_build' / 'html'), # outdir (tmp_path / '_build' / 'html'), # outdir
(tempdir / '_build' / '.doctree'), # doctreedir (tmp_path / '_build' / '.doctree'), # doctreedir
'html', # buildername 'html', # buildername
status=StringIO(), status=StringIO(),
warning=warnfile) warning=warnfile)
@ -216,9 +216,9 @@ def test_quickstart_and_build(tempdir):
assert not warnings assert not warnings
def test_default_filename(tempdir): def test_default_filename(tmp_path):
answers = { answers = {
'Root path': tempdir, 'Root path': str(tmp_path),
'Project name': '\u30c9\u30a4\u30c4', # Fullwidth characters only 'Project name': '\u30c9\u30a4\u30c4', # Fullwidth characters only
'Author name': 'Georg Brandl', 'Author name': 'Georg Brandl',
'Project version': '0.1', 'Project version': '0.1',
@ -228,25 +228,25 @@ def test_default_filename(tempdir):
qs.ask_user(d) qs.ask_user(d)
qs.generate(d) qs.generate(d)
conffile = tempdir / 'conf.py' conffile = tmp_path / 'conf.py'
assert conffile.isfile() assert conffile.is_file()
ns = {} ns = {}
exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102
def test_extensions(tempdir): def test_extensions(tmp_path):
qs.main(['-q', '-p', 'project_name', '-a', 'author', qs.main(['-q', '-p', 'project_name', '-a', 'author',
'--extensions', 'foo,bar,baz', tempdir]) '--extensions', 'foo,bar,baz', str(tmp_path)])
conffile = tempdir / 'conf.py' conffile = tmp_path / 'conf.py'
assert conffile.isfile() assert conffile.is_file()
ns = {} ns = {}
exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102
assert ns['extensions'] == ['foo', 'bar', 'baz'] assert ns['extensions'] == ['foo', 'bar', 'baz']
def test_exits_when_existing_confpy(monkeypatch): def test_exits_when_existing_confpy(monkeypatch):
# The code detects existing conf.py with path.isfile() # The code detects existing conf.py with path.is_file()
# so we mock it as True with pytest's monkeypatch # so we mock it as True with pytest's monkeypatch
def mock_isfile(path): def mock_isfile(path):
return True return True

View File

@ -27,9 +27,9 @@ def test_theme_api(app, status, warning):
# test Theme class API # test Theme class API
assert set(app.registry.html_themes.keys()) == set(themes) assert set(app.registry.html_themes.keys()) == set(themes)
assert app.registry.html_themes['test-theme'] == app.srcdir / 'test_theme' / 'test-theme' assert app.registry.html_themes['test-theme'] == str(app.srcdir / 'test_theme' / 'test-theme')
assert app.registry.html_themes['ziptheme'] == app.srcdir / 'ziptheme.zip' assert app.registry.html_themes['ziptheme'] == str(app.srcdir / 'ziptheme.zip')
assert app.registry.html_themes['staticfiles'] == app.srcdir / 'test_theme' / 'staticfiles' assert app.registry.html_themes['staticfiles'] == str(app.srcdir / 'test_theme' / 'staticfiles')
# test Theme instance API # test Theme instance API
theme = app.builder.theme theme = app.builder.theme

View File

@ -33,10 +33,6 @@ def test_ensuredir():
ensuredir(path) ensuredir(path)
assert os.path.isdir(path) assert os.path.isdir(path)
with tempfile.NamedTemporaryFile() as tmp:
with pytest.raises(OSError):
ensuredir(tmp.name)
def test_import_object(): def test_import_object():
module = import_object('sphinx') module = import_object('sphinx')

View File

@ -15,42 +15,42 @@ class DummyTemplateLoader(BuiltinTemplateLoader):
self.init(builder) self.init(builder)
def test_copy_asset_file(tempdir): def test_copy_asset_file(tmp_path):
renderer = DummyTemplateLoader() renderer = DummyTemplateLoader()
# copy normal file # copy normal file
src = (tempdir / 'asset.txt') src = (tmp_path / 'asset.txt')
src.write_text('# test data') src.write_text('# test data')
dest = (tempdir / 'output.txt') dest = (tmp_path / 'output.txt')
copy_asset_file(src, dest) copy_asset_file(src, dest)
assert dest.exists() assert dest.exists()
assert src.read_text(encoding='utf8') == dest.read_text(encoding='utf8') assert src.read_text(encoding='utf8') == dest.read_text(encoding='utf8')
# copy template file # copy template file
src = (tempdir / 'asset.txt_t') src = (tmp_path / 'asset.txt_t')
src.write_text('# {{var1}} data') src.write_text('# {{var1}} data')
dest = (tempdir / 'output.txt_t') dest = (tmp_path / 'output.txt_t')
copy_asset_file(src, dest, {'var1': 'template'}, renderer) copy_asset_file(str(src), str(dest), {'var1': 'template'}, renderer)
assert not dest.exists() assert not dest.exists()
assert (tempdir / 'output.txt').exists() assert (tmp_path / 'output.txt').exists()
assert (tempdir / 'output.txt').read_text(encoding='utf8') == '# template data' assert (tmp_path / 'output.txt').read_text(encoding='utf8') == '# template data'
# copy template file to subdir # copy template file to subdir
src = (tempdir / 'asset.txt_t') src = (tmp_path / 'asset.txt_t')
src.write_text('# {{var1}} data') src.write_text('# {{var1}} data')
subdir1 = (tempdir / 'subdir') subdir1 = (tmp_path / 'subdir')
subdir1.makedirs() subdir1.mkdir(parents=True, exist_ok=True)
copy_asset_file(src, subdir1, {'var1': 'template'}, renderer) copy_asset_file(src, subdir1, {'var1': 'template'}, renderer)
assert (subdir1 / 'asset.txt').exists() assert (subdir1 / 'asset.txt').exists()
assert (subdir1 / 'asset.txt').read_text(encoding='utf8') == '# template data' assert (subdir1 / 'asset.txt').read_text(encoding='utf8') == '# template data'
# copy template file without context # copy template file without context
src = (tempdir / 'asset.txt_t') src = (tmp_path / 'asset.txt_t')
subdir2 = (tempdir / 'subdir2') subdir2 = (tmp_path / 'subdir2')
subdir2.makedirs() subdir2.mkdir(parents=True, exist_ok=True)
copy_asset_file(src, subdir2) copy_asset_file(src, subdir2)
assert not (subdir2 / 'asset.txt').exists() assert not (subdir2 / 'asset.txt').exists()
@ -58,28 +58,28 @@ def test_copy_asset_file(tempdir):
assert (subdir2 / 'asset.txt_t').read_text(encoding='utf8') == '# {{var1}} data' assert (subdir2 / 'asset.txt_t').read_text(encoding='utf8') == '# {{var1}} data'
def test_copy_asset(tempdir): def test_copy_asset(tmp_path):
renderer = DummyTemplateLoader() renderer = DummyTemplateLoader()
# prepare source files # prepare source files
source = (tempdir / 'source') source = (tmp_path / 'source')
source.makedirs() source.mkdir(parents=True, exist_ok=True)
(source / 'index.rst').write_text('index.rst', encoding='utf8') (source / 'index.rst').write_text('index.rst', encoding='utf8')
(source / 'foo.rst_t').write_text('{{var1}}.rst', encoding='utf8') (source / 'foo.rst_t').write_text('{{var1}}.rst', encoding='utf8')
(source / '_static').makedirs() (source / '_static').mkdir(parents=True, exist_ok=True)
(source / '_static' / 'basic.css').write_text('basic.css', encoding='utf8') (source / '_static' / 'basic.css').write_text('basic.css', encoding='utf8')
(source / '_templates').makedirs() (source / '_templates').mkdir(parents=True, exist_ok=True)
(source / '_templates' / 'layout.html').write_text('layout.html', encoding='utf8') (source / '_templates' / 'layout.html').write_text('layout.html', encoding='utf8')
(source / '_templates' / 'sidebar.html_t').write_text('sidebar: {{var2}}', encoding='utf8') (source / '_templates' / 'sidebar.html_t').write_text('sidebar: {{var2}}', encoding='utf8')
# copy a single file # copy a single file
assert not (tempdir / 'test1').exists() assert not (tmp_path / 'test1').exists()
copy_asset(source / 'index.rst', tempdir / 'test1') copy_asset(source / 'index.rst', tmp_path / 'test1')
assert (tempdir / 'test1').exists() assert (tmp_path / 'test1').exists()
assert (tempdir / 'test1/index.rst').exists() assert (tmp_path / 'test1/index.rst').exists()
# copy directories # copy directories
destdir = tempdir / 'test2' destdir = tmp_path / 'test2'
copy_asset(source, destdir, context={'var1': 'bar', 'var2': 'baz'}, renderer=renderer) copy_asset(source, destdir, context={'var1': 'bar', 'var2': 'baz'}, renderer=renderer)
assert (destdir / 'index.rst').exists() assert (destdir / 'index.rst').exists()
assert (destdir / 'foo.rst').exists() assert (destdir / 'foo.rst').exists()
@ -93,7 +93,7 @@ def test_copy_asset(tempdir):
def excluded(path): def excluded(path):
return ('sidebar.html' in path or 'basic.css' in path) return ('sidebar.html' in path or 'basic.css' in path)
destdir = tempdir / 'test3' destdir = tmp_path / 'test3'
copy_asset(source, destdir, excluded, copy_asset(source, destdir, excluded,
context={'var1': 'bar', 'var2': 'baz'}, renderer=renderer) context={'var1': 'bar', 'var2': 'baz'}, renderer=renderer)
assert (destdir / 'index.rst').exists() assert (destdir / 'index.rst').exists()

View File

@ -29,12 +29,12 @@ def test_catalog_info_for_sub_domain_file_and_path():
assert cat.mo_path == os.path.join('path', 'sub/domain.mo') assert cat.mo_path == os.path.join('path', 'sub/domain.mo')
def test_catalog_outdated(tempdir): def test_catalog_outdated(tmp_path):
(tempdir / 'test.po').write_text('#', encoding='utf8') (tmp_path / 'test.po').write_text('#', encoding='utf8')
cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8') cat = i18n.CatalogInfo(tmp_path, 'test', 'utf-8')
assert cat.is_outdated() # if mo is not exist assert cat.is_outdated() # if mo is not exist
mo_file = (tempdir / 'test.mo') mo_file = (tmp_path / 'test.mo')
mo_file.write_text('#', encoding='utf8') mo_file.write_text('#', encoding='utf8')
assert not cat.is_outdated() # if mo is exist and newer than po assert not cat.is_outdated() # if mo is exist and newer than po
@ -42,9 +42,9 @@ def test_catalog_outdated(tempdir):
assert cat.is_outdated() # if mo is exist and older than po assert cat.is_outdated() # if mo is exist and older than po
def test_catalog_write_mo(tempdir): def test_catalog_write_mo(tmp_path):
(tempdir / 'test.po').write_text('#', encoding='utf8') (tmp_path / 'test.po').write_text('#', encoding='utf8')
cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8') cat = i18n.CatalogInfo(tmp_path, 'test', 'utf-8')
cat.write_mo('en') cat.write_mo('en')
assert os.path.exists(cat.mo_path) assert os.path.exists(cat.mo_path)
with open(cat.mo_path, 'rb') as f: with open(cat.mo_path, 'rb') as f:
@ -147,45 +147,45 @@ def test_get_filename_for_language(app):
'/subdir/en/foo.png') '/subdir/en/foo.png')
def test_CatalogRepository(tempdir): def test_CatalogRepository(tmp_path):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs() (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#', encoding='utf8')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#', encoding='utf8')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs() (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').mkdir(parents=True, exist_ok=True)
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test3.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test3.po').write_text('#', encoding='utf8')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#', encoding='utf8')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / '.dotdir').makedirs() (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / '.dotdir').mkdir(parents=True, exist_ok=True)
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / '.dotdir' / 'test5.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'xx' / 'LC_MESSAGES' / '.dotdir' / 'test5.po').write_text('#', encoding='utf8')
(tempdir / 'loc1' / 'yy' / 'LC_MESSAGES').makedirs() (tmp_path / 'loc1' / 'yy' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
(tempdir / 'loc1' / 'yy' / 'LC_MESSAGES' / 'test6.po').write_text('#', encoding='utf8') (tmp_path / 'loc1' / 'yy' / 'LC_MESSAGES' / 'test6.po').write_text('#', encoding='utf8')
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES').makedirs() (tmp_path / 'loc2' / 'xx' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#', encoding='utf8') (tmp_path / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#', encoding='utf8')
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test7.po').write_text('#', encoding='utf8') (tmp_path / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test7.po').write_text('#', encoding='utf8')
# for language xx # for language xx
repo = i18n.CatalogRepository(tempdir, ['loc1', 'loc2'], 'xx', 'utf-8') repo = i18n.CatalogRepository(tmp_path, ['loc1', 'loc2'], 'xx', 'utf-8')
assert list(repo.locale_dirs) == [str(tempdir / 'loc1'), assert list(repo.locale_dirs) == [str(tmp_path / 'loc1'),
str(tempdir / 'loc2')] str(tmp_path / 'loc2')]
assert all(isinstance(c, i18n.CatalogInfo) for c in repo.catalogs) assert all(isinstance(c, i18n.CatalogInfo) for c in repo.catalogs)
assert sorted(c.domain for c in repo.catalogs) == ['sub/test3', 'sub/test4', assert sorted(c.domain for c in repo.catalogs) == ['sub/test3', 'sub/test4',
'test1', 'test1', 'test2', 'test7'] 'test1', 'test1', 'test2', 'test7']
# for language yy # for language yy
repo = i18n.CatalogRepository(tempdir, ['loc1', 'loc2'], 'yy', 'utf-8') repo = i18n.CatalogRepository(tmp_path, ['loc1', 'loc2'], 'yy', 'utf-8')
assert sorted(c.domain for c in repo.catalogs) == ['test6'] assert sorted(c.domain for c in repo.catalogs) == ['test6']
# unknown languages # unknown languages
repo = i18n.CatalogRepository(tempdir, ['loc1', 'loc2'], 'zz', 'utf-8') repo = i18n.CatalogRepository(tmp_path, ['loc1', 'loc2'], 'zz', 'utf-8')
assert sorted(c.domain for c in repo.catalogs) == [] assert sorted(c.domain for c in repo.catalogs) == []
# no languages # no languages
repo = i18n.CatalogRepository(tempdir, ['loc1', 'loc2'], None, 'utf-8') repo = i18n.CatalogRepository(tmp_path, ['loc1', 'loc2'], None, 'utf-8')
assert sorted(c.domain for c in repo.catalogs) == [] assert sorted(c.domain for c in repo.catalogs) == []
# unknown locale_dirs # unknown locale_dirs
repo = i18n.CatalogRepository(tempdir, ['loc3'], None, 'utf-8') repo = i18n.CatalogRepository(tmp_path, ['loc3'], None, 'utf-8')
assert sorted(c.domain for c in repo.catalogs) == [] assert sorted(c.domain for c in repo.catalogs) == []
# no locale_dirs # no locale_dirs
repo = i18n.CatalogRepository(tempdir, [], None, 'utf-8') repo = i18n.CatalogRepository(tmp_path, [], None, 'utf-8')
assert sorted(c.domain for c in repo.catalogs) == [] assert sorted(c.domain for c in repo.catalogs) == []

View File

@ -1,5 +1,5 @@
"""Test inventory util functions.""" """Test inventory util functions."""
import os
import posixpath import posixpath
import zlib import zlib
from io import BytesIO from io import BytesIO
@ -88,12 +88,12 @@ def test_read_inventory_v2_not_having_version():
def _write_appconfig(dir, language, prefix=None): def _write_appconfig(dir, language, prefix=None):
prefix = prefix or language prefix = prefix or language
(dir / prefix).makedirs() os.makedirs(dir / prefix, exist_ok=True)
(dir / prefix / 'conf.py').write_text(f'language = "{language}"', encoding='utf8') (dir / prefix / 'conf.py').write_text(f'language = "{language}"', encoding='utf8')
(dir / prefix / 'index.rst').write_text('index.rst', encoding='utf8') (dir / prefix / 'index.rst').write_text('index.rst', encoding='utf8')
assert sorted((dir / prefix).listdir()) == ['conf.py', 'index.rst'] assert sorted(os.listdir(dir / prefix)) == ['conf.py', 'index.rst']
assert (dir / prefix / 'index.rst').exists() assert (dir / prefix / 'index.rst').exists()
return (dir / prefix) return dir / prefix
def _build_inventory(srcdir): def _build_inventory(srcdir):
@ -103,13 +103,13 @@ def _build_inventory(srcdir):
return (app.outdir / 'objects.inv') return (app.outdir / 'objects.inv')
def test_inventory_localization(tempdir): def test_inventory_localization(tmp_path):
# Build an app using Estonian (EE) locale # Build an app using Estonian (EE) locale
srcdir_et = _write_appconfig(tempdir, "et") srcdir_et = _write_appconfig(tmp_path, "et")
inventory_et = _build_inventory(srcdir_et) inventory_et = _build_inventory(srcdir_et)
# Build the same app using English (US) locale # Build the same app using English (US) locale
srcdir_en = _write_appconfig(tempdir, "en") srcdir_en = _write_appconfig(tmp_path, "en")
inventory_en = _build_inventory(srcdir_en) inventory_en = _build_inventory(srcdir_en)
# Ensure that the inventory contents differ # Ensure that the inventory contents differ

View File

@ -1,6 +1,7 @@
"""Test the versioning implementation.""" """Test the versioning implementation."""
import pickle import pickle
import shutil
import pytest import pytest
@ -15,7 +16,7 @@ def _setup_module(rootdir, sphinx_test_tempdir):
global app, original, original_uids global app, original, original_uids
srcdir = sphinx_test_tempdir / 'test-versioning' srcdir = sphinx_test_tempdir / 'test-versioning'
if not srcdir.exists(): if not srcdir.exists():
(rootdir / 'test-versioning').copytree(srcdir) shutil.copytree(rootdir / 'test-versioning', srcdir)
app = SphinxTestApp(srcdir=srcdir) app = SphinxTestApp(srcdir=srcdir)
app.builder.env.app = app app.builder.env.app = app
app.connect('doctree-resolved', on_doctree_resolved) app.connect('doctree-resolved', on_doctree_resolved)