Skip to content

Commit

Permalink
Start using pathlib.Path and deprecate sphinx.testing.path (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Jul 27, 2023
1 parent 23c7fdd commit 49d8304
Show file tree
Hide file tree
Showing 51 changed files with 490 additions and 630 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Expand Up @@ -14,10 +14,15 @@ Deprecated

* #11512: Deprecate ``sphinx.util.md5`` and ``sphinx.util.sha1``.
Use ``hashlib`` instead.
* #11526: Deprecate ``sphinx.testing.path``.
Use ``os.path`` or ``pathlib`` instead.

Features added
--------------

* #11526: Support ``os.PathLike`` types and ``pathlib.Path`` objects
in many more places.

Bugs fixed
----------

Expand Down
5 changes: 5 additions & 0 deletions doc/extdev/deprecated.rst
Expand Up @@ -22,6 +22,11 @@ The following is a list of deprecated interfaces.
- Removed
- Alternatives

* - ``sphinx.testing.path``
- 7.2
- 9.0
- ``os.path`` or ``pathlib``

* - ``sphinx.util.md5``
- 7.2
- 9.0
Expand Down
14 changes: 8 additions & 6 deletions sphinx/application.py
Expand Up @@ -11,6 +11,7 @@
from collections import deque
from io import StringIO
from os import path
from pathlib import Path
from typing import IO, TYPE_CHECKING, Any, Callable

from docutils import nodes
Expand Down Expand Up @@ -41,7 +42,7 @@
from sphinx.util.display import progress_message
from sphinx.util.i18n import CatalogRepository
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.typing import RoleFunction, TitleGetter

Expand Down Expand Up @@ -132,7 +133,8 @@ class Sphinx:
warningiserror: bool
_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,
status: IO | None = sys.stdout, warning: IO | None = sys.stderr,
freshenv: bool = False, warningiserror: bool = False,
Expand All @@ -145,9 +147,9 @@ def __init__(self, srcdir: str, confdir: str | None, outdir: str, doctreedir: st
self.registry = SphinxComponentRegistry()

# validate provided directories
self.srcdir = abspath(srcdir)
self.outdir = abspath(outdir)
self.doctreedir = abspath(doctreedir)
self.srcdir = Path(srcdir).resolve()
self.outdir = Path(outdir).resolve()
self.doctreedir = Path(doctreedir).resolve()

if not path.isdir(self.srcdir):
raise ApplicationError(__('Cannot find source directory (%s)') %
Expand Down Expand Up @@ -203,7 +205,7 @@ def __init__(self, srcdir: str, confdir: str | None, outdir: str, doctreedir: st
self.confdir = self.srcdir
self.config = Config({}, confoverrides or {})
else:
self.confdir = abspath(confdir)
self.confdir = Path(confdir).resolve()
self.config = Config.read(self.confdir, confoverrides or {}, self.tags)

# initialize some limited config variables before initialize i18n and loading
Expand Down
2 changes: 1 addition & 1 deletion sphinx/builders/__init__.py
Expand Up @@ -262,7 +262,7 @@ def build_specific(self, filenames: list[str]) -> None:
filename)
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 '
'source directory, ignoring'), filename)
continue
Expand Down
7 changes: 2 additions & 5 deletions sphinx/builders/_epub_base.py
Expand Up @@ -21,7 +21,7 @@
from sphinx.util import logging
from sphinx.util.display import status_iterator
from sphinx.util.fileutil import copy_asset_file
from sphinx.util.osutil import copyfile, ensuredir
from sphinx.util.osutil import copyfile, ensuredir, relpath

try:
from PIL import Image
Expand Down Expand Up @@ -508,9 +508,6 @@ def build_content(self) -> None:
metadata = self.content_metadata()

# files
if not self.outdir.endswith(os.sep):
self.outdir += os.sep
olen = len(self.outdir)
self.files: list[str] = []
self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf',
'toc.ncx', 'META-INF/container.xml',
Expand All @@ -522,7 +519,7 @@ def build_content(self) -> None:
for root, dirs, files in os.walk(self.outdir):
dirs.sort()
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:
continue
ext = path.splitext(filename)[-1]
Expand Down
2 changes: 1 addition & 1 deletion sphinx/builders/changes.py
Expand Up @@ -34,7 +34,7 @@ def init(self) -> None:
self.templates.init(self, self.theme)

def get_outdated_docs(self) -> str:
return self.outdir
return str(self.outdir)

typemap = {
'versionadded': 'added',
Expand Down
6 changes: 4 additions & 2 deletions sphinx/builders/gettext.py
Expand Up @@ -28,6 +28,7 @@
from sphinx.util.template import SphinxRenderer

if TYPE_CHECKING:
import os
from collections.abc import Generator, Iterable

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,11 +85,12 @@ def __init__(self, source: str, line: int) -> None:

class GettextRenderer(SphinxRenderer):
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:
self.outdir = outdir
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)

def escape(s: str) -> str:
Expand Down
10 changes: 5 additions & 5 deletions sphinx/builders/html/__init__.py
Expand Up @@ -786,7 +786,7 @@ def copy_image_files(self) -> None:

def copy_download_files(self) -> None:
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
if self.env.dlfiles:
Expand Down Expand Up @@ -1254,9 +1254,9 @@ def js_tag(js: JavaScript) -> str:
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
if '://' in filename:
if '://' in str(filename):
return ''
try:
# Ensure universal newline mode is used to avoid checksum differences
Expand Down Expand Up @@ -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)
config.html_extra_path.remove(entry)
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)
config.html_extra_path.remove(entry)

Expand All @@ -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)
config.html_static_path.remove(entry)
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)
config.html_static_path.remove(entry)

Expand Down
4 changes: 2 additions & 2 deletions sphinx/cmd/build.py
Expand Up @@ -24,7 +24,7 @@
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.exceptions import format_exception_cut_frames, save_traceback
from sphinx.util.osutil import abspath, ensuredir
from sphinx.util.osutil import ensuredir


def handle_exception(
Expand Down Expand Up @@ -234,7 +234,7 @@ def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:

if warning and args.warnfile:
try:
warnfile = abspath(args.warnfile)
warnfile = path.abspath(args.warnfile)
ensuredir(path.dirname(warnfile))
warnfp = open(args.warnfile, 'w', encoding="utf-8")
except Exception as exc:
Expand Down
6 changes: 3 additions & 3 deletions sphinx/config.py
Expand Up @@ -21,6 +21,7 @@
from sphinx.util.osutil import _chdir as chdir

if TYPE_CHECKING:
import os
from collections.abc import Generator, Iterator, Sequence

from sphinx.application import Sphinx
Expand Down Expand Up @@ -168,9 +169,8 @@ def __init__(self, config: dict[str, Any] = {}, overrides: dict[str, Any] = {})
self.extensions: list[str] = config.get('extensions', [])

@classmethod
def read(
cls, confdir: str, overrides: dict | None = None, tags: Tags | None = None,
) -> Config:
def read(cls, confdir: str | os.PathLike[str], overrides: dict | None = None,
tags: Tags | None = None) -> Config:
"""Create a Config object from configuration file."""
filename = path.join(confdir, CONFIG_FILENAME)
if not path.isfile(filename):
Expand Down
2 changes: 1 addition & 1 deletion sphinx/directives/code.py
Expand Up @@ -244,7 +244,7 @@ def show_diff(self, location: tuple[str, int] | None = None) -> list[str]:
new_lines = self.read_file(self.filename)
old_filename = self.options['diff']
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)

def pyobject_filter(
Expand Down
7 changes: 4 additions & 3 deletions sphinx/environment/__init__.py
Expand Up @@ -31,6 +31,7 @@

if TYPE_CHECKING:
from collections.abc import Generator, Iterator
from pathlib import Path

from sphinx.application import Sphinx
from sphinx.builders import Builder
Expand Down Expand Up @@ -147,8 +148,8 @@ class BuildEnvironment:

def __init__(self, app: Sphinx):
self.app: Sphinx = app
self.doctreedir: str = app.doctreedir
self.srcdir: str = app.srcdir
self.doctreedir: Path = app.doctreedir
self.srcdir: Path = app.srcdir
self.config: Config = None # type: ignore[assignment]
self.config_status: int = CONFIG_UNSET
self.config_status_extra: str = ''
Expand Down Expand Up @@ -387,7 +388,7 @@ def merge_info_from(self, docnames: list[str], other: BuildEnvironment,
domain.merge_domaindata(docnames, other.domaindata[domainname])
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.
*filename* should be absolute or relative to the source directory.
Expand Down
6 changes: 4 additions & 2 deletions sphinx/ext/autosummary/generate.py
Expand Up @@ -424,8 +424,10 @@ def _get_modules(
return public, items


def generate_autosummary_docs(sources: list[str], output_dir: str | None = None,
suffix: str = '.rst', base_path: str | None = None,
def generate_autosummary_docs(sources: list[str],
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,
overwrite: bool = True, encoding: str = 'utf-8') -> None:
showed_sources = sorted(sources)
Expand Down
7 changes: 5 additions & 2 deletions sphinx/ext/imgmath.py
Expand Up @@ -10,7 +10,7 @@
from hashlib import sha1
from os import path
from subprocess import CalledProcessError
from typing import Any
from typing import TYPE_CHECKING, Any

from docutils import nodes
from docutils.nodes import Element
Expand All @@ -29,6 +29,9 @@
from sphinx.util.template import LaTeXRenderer
from sphinx.writers.html import HTML5Translator

if TYPE_CHECKING:
import os

logger = logging.getLogger(__name__)

templates_path = path.join(package_dir, 'templates', 'imgmath')
Expand Down Expand Up @@ -83,7 +86,7 @@ def write_svg_depth(filename: str, depth: int) -> None:
def generate_latex_macro(image_format: str,
math: str,
config: Config,
confdir: str = '') -> str:
confdir: str | os.PathLike[str] = '') -> str:
"""Generate LaTeX macro."""
variables = {
'fontsize': config.imgmath_font_size,
Expand Down
8 changes: 4 additions & 4 deletions sphinx/project.py
Expand Up @@ -21,7 +21,7 @@
class Project:
"""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.
self.srcdir = srcdir

Expand Down Expand Up @@ -61,15 +61,15 @@ def discover(self, exclude_paths: Iterable[str] = (),

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.
*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)
for suffix in self.source_suffix:
if filename.endswith(suffix):
if str(filename).endswith(suffix):
filename = path_stabilize(filename)
return filename[:-len(suffix)]

Expand Down

0 comments on commit 49d8304

Please sign in to comment.