From de24fd0b76f6d86672a721f8c130642b0be4bcf0 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:06:19 +0100
Subject: [PATCH 01/39] Use ``_StrPath`` in ``sphinx.cmd.make_mode``
---
sphinx/cmd/make_mode.py | 48 ++++++++++++++++++++++++-----------------
sphinx/util/osutil.py | 2 +-
2 files changed, 29 insertions(+), 21 deletions(-)
diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py
index ac22ba99822..00c8744868d 100644
--- a/sphinx/cmd/make_mode.py
+++ b/sphinx/cmd/make_mode.py
@@ -13,11 +13,11 @@
import subprocess
import sys
from contextlib import chdir
-from os import path
from typing import TYPE_CHECKING
import sphinx
from sphinx.cmd.build import build_main
+from sphinx.util._pathlib import _StrPath
from sphinx.util.console import blue, bold, color_terminal, nocolor
from sphinx.util.osutil import rmtree
@@ -57,30 +57,36 @@
class Make:
- def __init__(self, *, source_dir: str, build_dir: str, opts: Sequence[str]) -> None:
- self.source_dir = source_dir
- self.build_dir = build_dir
+ def __init__(
+ self,
+ *,
+ source_dir: str | os.PathLike[str],
+ build_dir: str | os.PathLike[str],
+ opts: Sequence[str],
+ ) -> None:
+ self.source_dir = _StrPath(source_dir)
+ self.build_dir = _StrPath(build_dir)
self.opts = [*opts]
- def build_dir_join(self, *comps: str) -> str:
- return path.join(self.build_dir, *comps)
+ def build_dir_join(self, *comps: str | os.PathLike[str]) -> _StrPath:
+ return self.build_dir.joinpath(*comps)
def build_clean(self) -> int:
- source_dir = path.abspath(self.source_dir)
- build_dir = path.abspath(self.build_dir)
- if not path.exists(self.build_dir):
+ source_dir = self.source_dir.resolve()
+ build_dir = self.build_dir.resolve()
+ if not self.build_dir.exists():
return 0
- elif not path.isdir(self.build_dir):
- print('Error: %r is not a directory!' % self.build_dir)
+ elif not self.build_dir.is_dir():
+ print("Error: '%s' is not a directory!" % self.build_dir)
return 1
elif source_dir == build_dir:
- print('Error: %r is same as source directory!' % self.build_dir)
+ print("Error: '%s' is same as source directory!" % self.build_dir)
return 1
- elif path.commonpath([source_dir, build_dir]) == build_dir:
- print('Error: %r directory contains source directory!' % self.build_dir)
+ elif source_dir.is_relative_to(build_dir):
+ print("Error: '%s' directory contains source directory!" % self.build_dir)
return 1
- print('Removing everything under %r...' % self.build_dir)
- for item in os.listdir(self.build_dir):
+ print("Removing everything under '%s'..." % self.build_dir)
+ for item in self.build_dir.iterdir():
rmtree(self.build_dir_join(item))
return 0
@@ -179,7 +185,9 @@ def build_gettext(self) -> int:
return 1
return 0
- def run_generic_build(self, builder: str, doctreedir: str | None = None) -> int:
+ def run_generic_build(
+ self, builder: str, doctreedir: str | os.PathLike[str] | None = None
+ ) -> int:
# compatibility with old Makefile
paper_size = os.getenv('PAPER', '')
if paper_size in {'a4', 'letter'}:
@@ -191,9 +199,9 @@ def run_generic_build(self, builder: str, doctreedir: str | None = None) -> int:
'--builder',
builder,
'--doctree-dir',
- doctreedir,
- self.source_dir,
- self.build_dir_join(builder),
+ str(doctreedir),
+ str(self.source_dir),
+ str(self.build_dir_join(builder)),
]
return build_main(args + self.opts)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 4e092692bf0..7ec14d446b2 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -236,7 +236,7 @@ def __getattr__(self, name: str) -> Any:
return getattr(self._io, name)
-def rmtree(path: str) -> None:
+def rmtree(path: str | os.PathLike[str], /) -> None:
if os.path.isdir(path):
shutil.rmtree(path)
else:
From f0f127f11610797fcef9caa1a6dd8a8a3265ab21 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:11:48 +0100
Subject: [PATCH 02/39] Use ``_StrPath`` in ``sphinx.cmd.build``
---
sphinx/cmd/build.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py
index 3c3d8e4b4de..db76bc3ad85 100644
--- a/sphinx/cmd/build.py
+++ b/sphinx/cmd/build.py
@@ -7,11 +7,10 @@
import contextlib
import locale
import multiprocessing
-import os
import pdb # NoQA: T100
import sys
import traceback
-from os import path
+from pathlib import Path
from typing import TYPE_CHECKING, Any, TextIO
from docutils.utils import SystemMessage
@@ -22,6 +21,7 @@
from sphinx.errors import SphinxError, SphinxParallelError
from sphinx.locale import __
from sphinx.util._io import TeeStripANSI
+from sphinx.util._pathlib import _StrPath
from sphinx.util.console import color_terminal, nocolor, red, terminal_safe
from sphinx.util.docutils import docutils_namespace, patch_docutils
from sphinx.util.exceptions import format_exception_cut_frames, save_traceback
@@ -392,10 +392,10 @@ def _parse_confdir(noconfig: bool, confdir: str, sourcedir: str) -> str | None:
return confdir
-def _parse_doctreedir(doctreedir: str, outputdir: str) -> str:
+def _parse_doctreedir(doctreedir: str, outputdir: str) -> _StrPath:
if doctreedir:
- return doctreedir
- return os.path.join(outputdir, '.doctrees')
+ return _StrPath(doctreedir)
+ return _StrPath(outputdir, '.doctrees')
def _validate_filenames(
@@ -431,12 +431,12 @@ def _parse_logging(
warnfp = None
if warning and warnfile:
try:
- warnfile = path.abspath(warnfile)
- ensuredir(path.dirname(warnfile))
+ warn_file = Path(warnfile).resolve()
+ ensuredir(warn_file.parent)
# the caller is responsible for closing this file descriptor
- warnfp = open(warnfile, 'w', encoding='utf-8') # NoQA: SIM115
+ warnfp = open(warn_file, 'w', encoding='utf-8') # NoQA: SIM115
except Exception as exc:
- parser.error(__('cannot open warning file %r: %s') % (warnfile, exc))
+ parser.error(__("cannot open warning file '%s': %s") % (warn_file, exc))
warning = TeeStripANSI(warning, warnfp) # type: ignore[assignment]
error = warning
From 97ddc477650efb590d8245239279c4158dc24bd2 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:14:24 +0100
Subject: [PATCH 03/39] Use ``_StrPath`` in ``sphinx.directives.patches``
---
sphinx/directives/patches.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py
index 85cff2e6407..ff2989520d8 100644
--- a/sphinx/directives/patches.py
+++ b/sphinx/directives/patches.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import os
-from os import path
+from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, cast
from docutils import nodes
@@ -16,7 +16,7 @@
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
-from sphinx.util.osutil import SEP, os_path, relpath
+from sphinx.util.osutil import SEP, relpath
if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -60,8 +60,8 @@ class CSVTable(tables.CSVTable): # type: ignore[misc]
def run(self) -> list[Node]:
if 'file' in self.options and self.options['file'].startswith((SEP, os.sep)):
env = self.state.document.settings.env
- filename = self.options['file']
- if path.exists(filename):
+ filename = Path(self.options['file'])
+ if filename.exists():
logger.warning(
__(
'":file:" option for csv-table directive now recognizes '
@@ -71,9 +71,9 @@ def run(self) -> list[Node]:
location=(env.docname, self.lineno),
)
else:
- abspath = path.join(env.srcdir, os_path(self.options['file'][1:]))
- docdir = path.dirname(env.doc2path(env.docname))
- self.options['file'] = relpath(abspath, docdir)
+ abspath = env.srcdir / self.options['file'][1:]
+ doc_dir = env.doc2path(env.docname).parent
+ self.options['file'] = relpath(abspath, doc_dir)
return super().run()
From 072df8f779def71d51cf43c8fa934cb0fa5e1ef2 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:21:12 +0100
Subject: [PATCH 04/39] Use ``_StrPath`` in ``sphinx.search``
---
sphinx/search/__init__.py | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index 82081cbe2e9..3f19d3663a0 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -10,13 +10,14 @@
import pickle
import re
from importlib import import_module
-from os import path
+from pathlib import Path
from typing import IO, TYPE_CHECKING, Any
from docutils import nodes
from docutils.nodes import Element, Node
from sphinx import addnodes, package_dir
+from sphinx.util._pathlib import _StrPath
from sphinx.util.index_entries import split_index_msg
if TYPE_CHECKING:
@@ -24,6 +25,9 @@
from sphinx.environment import BuildEnvironment
+_NON_MINIFIED_JS_PATH = Path(package_dir, 'search', 'non-minified-js')
+_MINIFIED_JS_PATH = Path(package_dir, 'search', 'minified-js')
+
class SearchLanguage:
"""
@@ -554,12 +558,12 @@ def context_for_searchtool(self) -> dict[str, Any]:
'search_word_splitter_code': js_splitter_code,
}
- def get_js_stemmer_rawcodes(self) -> list[str]:
+ def get_js_stemmer_rawcodes(self) -> list[_StrPath]:
"""Returns a list of non-minified stemmer JS files to copy."""
if self.lang.js_stemmer_rawcode:
return [
- path.join(package_dir, 'search', 'non-minified-js', fname)
- for fname in ('base-stemmer.js', self.lang.js_stemmer_rawcode)
+ _StrPath(_NON_MINIFIED_JS_PATH / 'base-stemmer.js'),
+ _StrPath(_NON_MINIFIED_JS_PATH / self.lang.js_stemmer_rawcode),
]
else:
return []
@@ -570,15 +574,10 @@ def get_js_stemmer_rawcode(self) -> str | None:
def get_js_stemmer_code(self) -> str:
"""Returns JS code that will be inserted into language_data.js."""
if self.lang.js_stemmer_rawcode:
- js_dir = path.join(package_dir, 'search', 'minified-js')
- with open(
- path.join(js_dir, 'base-stemmer.js'), encoding='utf-8'
- ) as js_file:
- base_js = js_file.read()
- with open(
- path.join(js_dir, self.lang.js_stemmer_rawcode), encoding='utf-8'
- ) as js_file:
- language_js = js_file.read()
+ base_js_path = _NON_MINIFIED_JS_PATH / 'base-stemmer.js'
+ language_js_path = _NON_MINIFIED_JS_PATH / self.lang.js_stemmer_rawcode
+ base_js = base_js_path.read_text(encoding='utf-8')
+ language_js = language_js_path.read_text(encoding='utf-8')
return (
f'{base_js}\n{language_js}\nStemmer = {self.lang.language_name}Stemmer;'
)
From 3015ceb54685f067e1422809392998f43ae396ea Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:46:33 +0100
Subject: [PATCH 05/39] Use ``_StrPath`` in ``sphinx.testing.restructuredtext``
---
sphinx/testing/restructuredtext.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/sphinx/testing/restructuredtext.py b/sphinx/testing/restructuredtext.py
index 1f89336db4b..620e8483492 100644
--- a/sphinx/testing/restructuredtext.py
+++ b/sphinx/testing/restructuredtext.py
@@ -1,5 +1,3 @@
-from os import path
-
from docutils import nodes
from docutils.core import publish_doctree
@@ -20,7 +18,7 @@ def parse(app: Sphinx, text: str, docname: str = 'index') -> nodes.document:
with sphinx_domains(app.env):
return publish_doctree(
text,
- path.join(app.srcdir, docname + '.rst'),
+ str(app.srcdir / f'{docname}.rst'),
reader=reader,
parser=parser,
settings_overrides={
From 5cf3e6bf9a052188bc95d7e26328f9420228b554 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:58:42 +0100
Subject: [PATCH 06/39] Use ``_StrPath`` in ``sphinx.util``
---
sphinx/util/docutils.py | 18 ++++++++----------
sphinx/util/template.py | 25 ++++++++++++++++---------
2 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index a4c1c67098e..4f085bfeee3 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -7,7 +7,7 @@
from collections.abc import Sequence # NoQA: TCH003
from contextlib import contextmanager
from copy import copy
-from os import path
+from pathlib import Path
from typing import IO, TYPE_CHECKING, Any, cast
import docutils
@@ -171,25 +171,23 @@ def patched_get_language(
@contextmanager
-def using_user_docutils_conf(confdir: str | None) -> Iterator[None]:
+def using_user_docutils_conf(confdir: str | os.PathLike[str] | None) -> Iterator[None]:
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
- docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
+ docutils_config = os.environ.get('DOCUTILSCONFIG', None)
if confdir:
- os.environ['DOCUTILSCONFIG'] = path.join(
- path.abspath(confdir), 'docutils.conf'
- )
-
+ docutils_conf_path = Path(confdir, 'docutils.conf').resolve()
+ os.environ['DOCUTILSCONFIG'] = str(docutils_conf_path)
yield
finally:
- if docutilsconfig is None:
+ if docutils_config is None:
os.environ.pop('DOCUTILSCONFIG', None)
else:
- os.environ['DOCUTILSCONFIG'] = docutilsconfig
+ os.environ['DOCUTILSCONFIG'] = docutils_config
@contextmanager
-def patch_docutils(confdir: str | None = None) -> Iterator[None]:
+def patch_docutils(confdir: str | os.PathLike[str] | None = None) -> Iterator[None]:
"""Patch to docutils temporarily."""
with (
patched_get_language(),
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 6e217d6177f..b428d3cf777 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -4,7 +4,7 @@
import os
from functools import partial
-from os import path
+from pathlib import Path
from typing import TYPE_CHECKING, Any
from jinja2 import TemplateNotFound
@@ -21,6 +21,9 @@
from jinja2.environment import Environment
+_TEMPLATES_PATH = Path(package_dir, 'templates')
+_LATEX_TEMPLATES_PATH = _TEMPLATES_PATH / 'latex'
+
class BaseRenderer:
def __init__(self, loader: BaseLoader | None = None) -> None:
@@ -49,11 +52,12 @@ def __init__(self, search_path: Sequence[str | os.PathLike[str]]) -> None:
@classmethod
def render_from_file(
- cls: type[FileRenderer], filename: str, context: dict[str, Any]
+ cls: type[FileRenderer],
+ filename: str | os.PathLike[str],
+ context: dict[str, Any],
) -> str:
- dirname = os.path.dirname(filename)
- basename = os.path.basename(filename)
- return cls(dirname).render(basename, context)
+ filename = Path(filename)
+ return cls((filename.parent,)).render(filename.name, context)
class SphinxRenderer(FileRenderer):
@@ -61,12 +65,14 @@ def __init__(
self, template_path: Sequence[str | os.PathLike[str]] | None = None
) -> None:
if template_path is None:
- template_path = os.path.join(package_dir, 'templates')
+ template_path = (_TEMPLATES_PATH,)
super().__init__(template_path)
@classmethod
def render_from_file(
- cls: type[FileRenderer], filename: str, context: dict[str, Any]
+ cls: type[FileRenderer],
+ filename: str | os.PathLike[str],
+ context: dict[str, Any],
) -> str:
return FileRenderer.render_from_file(filename, context)
@@ -78,7 +84,7 @@ def __init__(
latex_engine: str | None = None,
) -> None:
if template_path is None:
- template_path = [os.path.join(package_dir, 'templates', 'latex')]
+ template_path = (_LATEX_TEMPLATES_PATH,)
super().__init__(template_path)
# use texescape as escape filter
@@ -126,8 +132,9 @@ def __init__(
self.loaders = []
self.sysloaders = []
+ conf_dir = Path(confdir)
for templates_path in templates_paths:
- loader = SphinxFileSystemLoader(path.join(confdir, templates_path))
+ loader = SphinxFileSystemLoader(conf_dir / templates_path)
self.loaders.append(loader)
for templates_path in system_templates_paths:
From e7fa2420b61f4f1908c99d24499fd62bfba259c3 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 26 Oct 2024 00:07:13 +0100
Subject: [PATCH 07/39] Use ``_StrPath`` in ``sphinx.config``
---
sphinx/config.py | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index 8700ed30054..24b0ba2cd9c 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -7,14 +7,14 @@
import types
import warnings
from contextlib import chdir
-from os import getenv, path
+from os import getenv
+from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal, NamedTuple
from sphinx.deprecation import RemovedInSphinx90Warning
from sphinx.errors import ConfigError, ExtensionError
from sphinx.locale import _, __
from sphinx.util import logging
-from sphinx.util.osutil import fs_encoding
if TYPE_CHECKING:
import os
@@ -304,8 +304,8 @@ def overrides(self) -> dict[str, Any]:
def read(cls: type[Config], 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):
+ filename = Path(confdir, CONFIG_FILENAME)
+ if not filename.is_file():
raise ConfigError(__("config directory doesn't contain a conf.py file (%s)") %
confdir)
namespace = eval_config_file(filename, tags)
@@ -510,18 +510,19 @@ def __setstate__(self, state: dict) -> None:
self.__dict__.update(state)
-def eval_config_file(filename: str, tags: Tags | None) -> dict[str, Any]:
+def eval_config_file(filename: str | os.PathLike[str], tags: Tags | None) -> dict[str, Any]:
"""Evaluate a config file."""
+ filename = Path(filename)
+
namespace: dict[str, Any] = {}
- namespace['__file__'] = filename
+ namespace['__file__'] = str(filename)
namespace['tags'] = tags
- with chdir(path.dirname(filename)):
+ with chdir(filename.parent):
# during executing config file, current dir is changed to ``confdir``.
try:
- with open(filename, 'rb') as f:
- code = compile(f.read(), filename.encode(fs_encoding), 'exec')
- exec(code, namespace) # NoQA: S102
+ code = compile(filename.read_bytes(), filename, 'exec')
+ exec(code, namespace) # NoQA: S102
except SyntaxError as err:
msg = __("There is a syntax error in your configuration file: %s\n")
raise ConfigError(msg % err) from err
From eed19a0a7f1dce9088f6fce8250a0573373d4325 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 26 Oct 2024 00:08:33 +0100
Subject: [PATCH 08/39] Use ``_StrPath`` in ``sphinx.versioning``
---
sphinx/versioning.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index 506d7b5753d..778c7ab5da0 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -5,7 +5,6 @@
import pickle
from itertools import product, zip_longest
from operator import itemgetter
-from os import path
from typing import TYPE_CHECKING, Any
from uuid import uuid4
@@ -160,8 +159,8 @@ def apply(self, **kwargs: Any) -> None:
if env.versioning_compare:
# get old doctree
+ filename = env.doctreedir / f'{env.docname}.doctree'
try:
- filename = path.join(env.doctreedir, env.docname + '.doctree')
with open(filename, 'rb') as f:
old_doctree = pickle.load(f)
except OSError:
From 3b5bce906c3a8473ba75c650d03e77ee4fe8e5a0 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 26 Oct 2024 00:12:12 +0100
Subject: [PATCH 09/39] Use ``_StrPath`` in ``sphinx.application``
---
sphinx/application.py | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 2d650dc231f..5edc86afb71 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -11,7 +11,6 @@
import sys
from collections import deque
from io import StringIO
-from os import path
from typing import TYPE_CHECKING, overload
from docutils.parsers.rst import Directive, roles
@@ -182,11 +181,11 @@ def __init__(self, srcdir: str | os.PathLike[str], confdir: str | os.PathLike[st
self.outdir = _StrPath(outdir).resolve()
self.doctreedir = _StrPath(doctreedir).resolve()
- if not path.isdir(self.srcdir):
+ if not self.srcdir.is_dir():
raise ApplicationError(__('Cannot find source directory (%s)') %
self.srcdir)
- if path.exists(self.outdir) and not path.isdir(self.outdir):
+ if self.outdir.exists() and not self.outdir.is_dir():
raise ApplicationError(__('Output directory (%s) is not a directory') %
self.outdir)
@@ -258,9 +257,9 @@ def __init__(self, srcdir: str | os.PathLike[str], confdir: str | os.PathLike[st
# preload builder module (before init config values)
self.preload_builder(buildername)
- if not path.isdir(outdir):
+ if not self.outdir.is_dir():
with progress_message(__('making output directory')):
- ensuredir(outdir)
+ ensuredir(self.outdir)
# the config file itself can be an extension
if self.config.setup:
@@ -327,8 +326,8 @@ def _init_i18n(self) -> None:
logger.info(__('not available for built-in messages'))
def _init_env(self, freshenv: bool) -> BuildEnvironment:
- filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
- if freshenv or not os.path.exists(filename):
+ filename = self.doctreedir / ENV_PICKLE_FILENAME
+ if freshenv or not filename.exists():
return self._create_fresh_env()
else:
return self._load_existing_env(filename)
@@ -339,12 +338,12 @@ def _create_fresh_env(self) -> BuildEnvironment:
return env
@progress_message(__('loading pickled environment'))
- def _load_existing_env(self, filename: str) -> BuildEnvironment:
+ def _load_existing_env(self, filename: Path) -> BuildEnvironment:
try:
with open(filename, 'rb') as f:
env = pickle.load(f)
- env.setup(self)
- self._fresh_env_used = False
+ env.setup(self)
+ self._fresh_env_used = False
except Exception as err:
logger.info(__('failed: %s'), err)
env = self._create_fresh_env()
@@ -383,8 +382,8 @@ def build(self, force_all: bool = False, filenames: list[str] | None = None) ->
self.events.emit('build-finished', None)
except Exception as err:
# delete the saved env to force a fresh build next time
- envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
- if path.isfile(envfile):
+ envfile = self.doctreedir / ENV_PICKLE_FILENAME
+ if envfile.is_file():
os.unlink(envfile)
self.events.emit('build-finished', err)
raise
From 1e968be6ef441cadcf99d8073b3281dbdd60139e Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 26 Oct 2024 15:49:14 +0100
Subject: [PATCH 10/39] Minimise installed apt packages
---
.github/workflows/builddoc.yml | 2 +-
.github/workflows/main.yml | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/builddoc.yml b/.github/workflows/builddoc.yml
index f2055e2798f..e3347a7b535 100644
--- a/.github/workflows/builddoc.yml
+++ b/.github/workflows/builddoc.yml
@@ -27,7 +27,7 @@ jobs:
with:
python-version: "3"
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install uv
run: >
curl --no-progress-meter --location --fail
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 27f67597d46..aa3a27753b2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -54,7 +54,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install uv
run: >
curl --no-progress-meter --location --fail
@@ -92,7 +92,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -125,7 +125,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -158,7 +158,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -218,7 +218,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install uv
run: >
curl --no-progress-meter --location --fail
@@ -250,7 +250,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install uv
run: >
curl --no-progress-meter --location --fail
@@ -310,7 +310,7 @@ jobs:
- name: Check Python version
run: python --version --version
- name: Install graphviz
- run: sudo apt-get install graphviz
+ run: sudo apt-get install --no-install-recommends --yes graphviz
- name: Install uv
run: >
curl --no-progress-meter --location --fail
From c3968e9be96d51179e701439fbe7dfa22c8ed204 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 28 Oct 2024 13:31:59 +0000
Subject: [PATCH 11/39] Use attributes of ``env`` rather than ``env.app``
---
sphinx/directives/__init__.py | 4 ++--
sphinx/directives/other.py | 4 ++--
sphinx/domains/cpp/__init__.py | 2 +-
sphinx/domains/javascript.py | 2 +-
sphinx/domains/python/_object.py | 2 +-
sphinx/domains/rst.py | 2 +-
sphinx/ext/autodoc/__init__.py | 31 +++++++++++++++++++------------
7 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 218bc6d5431..181c6f81a07 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -267,7 +267,7 @@ def run(self) -> list[Node]:
finally:
# Private attributes for ToC generation. Will be modified or removed
# without notice.
- if self.env.app.config.toc_object_entries:
+ if self.env.config.toc_object_entries:
signode['_toc_parts'] = self._object_hierarchy_parts(signode)
signode['_toc_name'] = self._toc_entry_name(signode)
else:
@@ -288,7 +288,7 @@ def run(self) -> list[Node]:
content_node = addnodes.desc_content('', *content_children)
node.append(content_node)
self.transform_content(content_node)
- self.env.app.emit(
+ self.env.events.emit(
'object-description-transform', self.domain, self.objtype, content_node
)
DocFieldTransformer(self).transform_all(content_node)
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index e297ceb4b89..32fd3111a65 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -412,7 +412,7 @@ def _insert_input(include_lines: list[str], source: str) -> None:
# Emit the "include-read" event
arg = [text]
- self.env.app.events.emit('include-read', path, docname, arg)
+ self.env.events.emit('include-read', path, docname, arg)
text = arg[0]
# Split back into lines and reattach the two marker lines
@@ -424,7 +424,7 @@ def _insert_input(include_lines: list[str], source: str) -> None:
return StateMachine.insert_input(self.state_machine, include_lines, source)
# Only enable this patch if there are listeners for 'include-read'.
- if self.env.app.events.listeners.get('include-read'):
+ if self.env.events.listeners.get('include-read'):
# See https://github.com/python/mypy/issues/2427 for details on the mypy issue
self.state_machine.insert_input = _insert_input
diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py
index 45183611106..5e7942dd65b 100644
--- a/sphinx/domains/cpp/__init__.py
+++ b/sphinx/domains/cpp/__init__.py
@@ -403,7 +403,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str:
if not sig_node.get('_toc_parts'):
return ''
- config = self.env.app.config
+ config = self.env.config
objtype = sig_node.parent.get('objtype')
if config.add_function_parentheses and objtype in {'function', 'method'}:
parens = '()'
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index ec81375a6da..7ad37987b38 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -230,7 +230,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str:
if not sig_node.get('_toc_parts'):
return ''
- config = self.env.app.config
+ config = self.env.config
objtype = sig_node.parent.get('objtype')
if config.add_function_parentheses and objtype in {'function', 'method'}:
parens = '()'
diff --git a/sphinx/domains/python/_object.py b/sphinx/domains/python/_object.py
index ceb7f8bf83b..3e9049a1a27 100644
--- a/sphinx/domains/python/_object.py
+++ b/sphinx/domains/python/_object.py
@@ -411,7 +411,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str:
if not sig_node.get('_toc_parts'):
return ''
- config = self.env.app.config
+ config = self.env.config
objtype = sig_node.parent.get('objtype')
if config.add_function_parentheses and objtype in {'function', 'method'}:
parens = '()'
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 9eec281f3e6..f2ada04e1fe 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -75,7 +75,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str:
if not sig_node.get('_toc_parts'):
return ''
- config = self.env.app.config
+ config = self.env.config
objtype = sig_node.parent.get('objtype')
*parents, name = sig_node['_toc_parts']
if objtype == 'directive:option':
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index c5d2b0b248b..60c31e2542e 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -584,9 +584,14 @@ def process_doc(self, docstrings: list[list[str]]) -> Iterator[str]:
for docstringlines in docstrings:
if self.env.app:
# let extensions preprocess docstrings
- self.env.app.emit('autodoc-process-docstring',
- self.objtype, self.fullname, self.object,
- self.options, docstringlines)
+ self.env.events.emit(
+ 'autodoc-process-docstring',
+ self.objtype,
+ self.fullname,
+ self.object,
+ self.options,
+ docstringlines,
+ )
if docstringlines and docstringlines[-1]:
# append a blank line to the end of the docstring
@@ -793,7 +798,7 @@ def is_filtered_inherited_member(name: str, obj: Any) -> bool:
# should be skipped
if self.env.app:
# let extensions preprocess docstrings
- skip_user = self.env.app.emit_firstresult(
+ skip_user = self.env.events.emit_firstresult(
'autodoc-skip-member', self.objtype, membername, member,
not keep, self.options)
if skip_user is not None:
@@ -1325,7 +1330,7 @@ def format_args(self, **kwargs: Any) -> str:
kwargs.setdefault('unqualified_typehints', True)
try:
- self.env.app.emit('autodoc-before-process-signature', self.object, False)
+ self.env.events.emit('autodoc-before-process-signature', self.object, False)
sig = inspect.signature(self.object, type_aliases=self.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
@@ -1564,7 +1569,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
call = None
if call is not None:
- self.env.app.emit('autodoc-before-process-signature', call, True)
+ self.env.events.emit('autodoc-before-process-signature', call, True)
try:
sig = inspect.signature(call, bound_method=True,
type_aliases=self.config.autodoc_type_aliases)
@@ -1580,7 +1585,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
new = None
if new is not None:
- self.env.app.emit('autodoc-before-process-signature', new, True)
+ self.env.events.emit('autodoc-before-process-signature', new, True)
try:
sig = inspect.signature(new, bound_method=True,
type_aliases=self.config.autodoc_type_aliases)
@@ -1591,7 +1596,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
# Finally, we should have at least __init__ implemented
init = get_user_defined_function_or_method(self.object, '__init__')
if init is not None:
- self.env.app.emit('autodoc-before-process-signature', init, True)
+ self.env.events.emit('autodoc-before-process-signature', init, True)
try:
sig = inspect.signature(init, bound_method=True,
type_aliases=self.config.autodoc_type_aliases)
@@ -1603,7 +1608,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
# handle it.
# We don't know the exact method that inspect.signature will read
# the signature from, so just pass the object itself to our hook.
- self.env.app.emit('autodoc-before-process-signature', self.object, False)
+ self.env.events.emit('autodoc-before-process-signature', self.object, False)
try:
sig = inspect.signature(self.object, bound_method=False,
type_aliases=self.config.autodoc_type_aliases)
@@ -2198,11 +2203,13 @@ def format_args(self, **kwargs: Any) -> str:
args = '()'
else:
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
- self.env.app.emit('autodoc-before-process-signature', self.object, False)
+ self.env.events.emit(
+ 'autodoc-before-process-signature', self.object, False
+ )
sig = inspect.signature(self.object, bound_method=False,
type_aliases=self.config.autodoc_type_aliases)
else:
- self.env.app.emit('autodoc-before-process-signature', self.object, True)
+ self.env.events.emit('autodoc-before-process-signature', self.object, True)
sig = inspect.signature(self.object, bound_method=True,
type_aliases=self.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
@@ -2785,7 +2792,7 @@ def format_args(self, **kwargs: Any) -> str:
return ''
# update the annotations of the property getter
- self.env.app.emit('autodoc-before-process-signature', func, False)
+ self.env.events.emit('autodoc-before-process-signature', func, False)
# correctly format the arguments for a property
return super().format_args(**kwargs)
From c6b4114cde98c28c9b53b7caab7a9191c99e7afd Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 2 Nov 2024 21:26:57 +0000
Subject: [PATCH 12/39] Make registry use explicit when creating
_DomainsContainer
---
sphinx/domains/_domains_container.py | 7 +++++--
sphinx/environment/__init__.py | 8 ++++++--
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/sphinx/domains/_domains_container.py b/sphinx/domains/_domains_container.py
index b8e389c79e4..5578a92575b 100644
--- a/sphinx/domains/_domains_container.py
+++ b/sphinx/domains/_domains_container.py
@@ -22,6 +22,7 @@
from sphinx.environment import BuildEnvironment
from sphinx.ext.duration import DurationDomain
from sphinx.ext.todo import TodoDomain
+ from sphinx.registry import SphinxComponentRegistry
class _DomainsContainer:
@@ -71,8 +72,10 @@ class _DomainsContainer:
})
@classmethod
- def _from_environment(cls, env: BuildEnvironment, /) -> Self:
- create_domains = env.app.registry.create_domains
+ def _from_environment(
+ cls, env: BuildEnvironment, /, *, registry: SphinxComponentRegistry
+ ) -> Self:
+ create_domains = registry.create_domains
# Initialise domains
if domains := {domain.name: domain for domain in create_domains(env)}:
return cls(**domains) # type: ignore[arg-type]
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 34ef4cc8066..8ecacf257ce 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -220,7 +220,9 @@ def __init__(self, app: Sphinx) -> None:
self._search_index_objnames: dict[int, tuple[str, str, str]] = {}
# all the registered domains, set by the application
- self.domains: _DomainsContainer = _DomainsContainer._from_environment(self)
+ self.domains: _DomainsContainer = _DomainsContainer._from_environment(
+ self, registry=app.registry
+ )
# set up environment
self.setup(app)
@@ -259,7 +261,9 @@ def setup(self, app: Sphinx) -> None:
# initialise domains
if self.domains is None:
# if we are unpickling an environment, we need to recreate the domains
- self.domains = _DomainsContainer._from_environment(self)
+ self.domains = _DomainsContainer._from_environment(
+ self, registry=app.registry
+ )
# setup domains (must do after all initialization)
self.domains._setup()
From 9eff55711be572d4e6f22aa571b16467b8a75371 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 2 Nov 2024 21:30:07 +0000
Subject: [PATCH 13/39] Use ``env.events`` in ``sphinx.ext.todo``
---
sphinx/ext/todo.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index 836dc8b864c..0625621b6db 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -97,7 +97,7 @@ def process_doc(self, env: BuildEnvironment, docname: str,
document: nodes.document) -> None:
todos = self.todos.setdefault(docname, [])
for todo in document.findall(todo_node):
- env.app.emit('todo-defined', todo)
+ env.events.emit('todo-defined', todo)
todos.append(todo)
if env.config.todo_emit_warnings:
From dedb21e63851cd08e71ce2399a97c828afaa16dd Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 2 Nov 2024 21:39:18 +0000
Subject: [PATCH 14/39] Resolve _StrPath warnings in the tests
---
tests/test_environment/test_environment.py | 5 +++--
tests/test_util/test_util_i18n.py | 12 ++++++------
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/tests/test_environment/test_environment.py b/tests/test_environment/test_environment.py
index 64487b8c3f6..10e98584342 100644
--- a/tests/test_environment/test_environment.py
+++ b/tests/test_environment/test_environment.py
@@ -41,11 +41,12 @@ def test_config_status(make_app, app_params):
# incremental build (config entry changed)
app3 = make_app(*args, confoverrides={'root_doc': 'indexx'}, **kwargs)
fname = app3.srcdir / 'index.rst'
+ other_fname = app3.srcdir / 'indexx.rst'
assert fname.is_file()
- shutil.move(fname, fname[:-4] + 'x.rst')
+ shutil.move(fname, other_fname)
assert app3.env.config_status == CONFIG_CHANGED
app3.build()
- shutil.move(fname[:-4] + 'x.rst', fname)
+ shutil.move(other_fname, fname)
output = strip_colors(app3.status.getvalue())
assert 'The configuration has changed' in output
assert "[config changed ('master_doc')] 1 added," in output
diff --git a/tests/test_util/test_util_i18n.py b/tests/test_util/test_util_i18n.py
index 973b054a1d8..d5ee52fb1f8 100644
--- a/tests/test_util/test_util_i18n.py
+++ b/tests/test_util/test_util_i18n.py
@@ -19,16 +19,16 @@ def test_catalog_info_for_file_and_path():
cat = i18n.CatalogInfo('path', 'domain', 'utf-8')
assert cat.po_file == 'domain.po'
assert cat.mo_file == 'domain.mo'
- assert cat.po_path == str(Path('path', 'domain.po'))
- assert cat.mo_path == str(Path('path', 'domain.mo'))
+ assert cat.po_path == Path('path', 'domain.po')
+ assert cat.mo_path == Path('path', 'domain.mo')
def test_catalog_info_for_sub_domain_file_and_path():
cat = i18n.CatalogInfo('path', 'sub/domain', 'utf-8')
assert cat.po_file == 'sub/domain.po'
assert cat.mo_file == 'sub/domain.mo'
- assert cat.po_path == str(Path('path', 'sub', 'domain.po'))
- assert cat.mo_path == str(Path('path', 'sub', 'domain.mo'))
+ assert cat.po_path == Path('path', 'sub', 'domain.po')
+ assert cat.mo_path == Path('path', 'sub', 'domain.mo')
def test_catalog_outdated(tmp_path):
@@ -178,8 +178,8 @@ def test_CatalogRepository(tmp_path):
# for language xx
repo = i18n.CatalogRepository(tmp_path, ['loc1', 'loc2'], 'xx', 'utf-8')
assert list(repo.locale_dirs) == [
- str(tmp_path / 'loc1'),
- str(tmp_path / 'loc2'),
+ tmp_path / 'loc1',
+ tmp_path / 'loc2',
]
assert all(isinstance(c, i18n.CatalogInfo) for c in repo.catalogs)
assert sorted(c.domain for c in repo.catalogs) == [
From 7a3ba905404747fe9ccab5652ef4da591904581a Mon Sep 17 00:00:00 2001
From: James Addison <55152140+jayaddison@users.noreply.github.com>
Date: Sat, 2 Nov 2024 21:50:33 +0000
Subject: [PATCH 15/39] Add James Addison to AUTHORS.rst (#13080)
---
AUTHORS.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 5c463beed8e..3c5de42f249 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -52,6 +52,7 @@ Contributors
* Hugo van Kemenade -- support FORCE_COLOR and NO_COLOR
* Ian Lee -- quickstart improvements
* Jacob Mason -- websupport library (GSOC project)
+* James Addison -- linkcheck and HTML search improvements
* Jeppe Pihl -- literalinclude improvements
* Joel Wurtz -- cellspanning support in LaTeX
* John Waltman -- Texinfo builder
From 867be99098ccb13ca77b9571f798463244aa0db2 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sat, 2 Nov 2024 22:47:28 +0000
Subject: [PATCH 16/39] Narrow types for resolve_xref and resolve_any_xref
---
sphinx/domains/__init__.py | 4 +--
sphinx/domains/c/__init__.py | 15 ++++----
sphinx/domains/citation.py | 4 +--
sphinx/domains/cpp/__init__.py | 15 ++++----
sphinx/domains/javascript.py | 4 +--
sphinx/domains/math.py | 4 +--
sphinx/domains/python/__init__.py | 8 ++---
sphinx/domains/rst.py | 7 ++--
sphinx/domains/std/__init__.py | 34 ++++++++++---------
sphinx/transforms/post_transforms/__init__.py | 2 +-
10 files changed, 53 insertions(+), 44 deletions(-)
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index 12a80e0d8d4..3f21078c6c5 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -223,7 +223,7 @@ def process_field_xref(self, pnode: pending_xref) -> None:
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
"""Resolve the pending_xref *node* with the given *typ* and *target*.
This method should return a new node, to replace the xref node,
@@ -241,7 +241,7 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
"""Resolve the pending_xref *node* with the given *target*.
The reference comes from an "any" or similar role, which means that we
diff --git a/sphinx/domains/c/__init__.py b/sphinx/domains/c/__init__.py
index e82912c167e..23729f09166 100644
--- a/sphinx/domains/c/__init__.py
+++ b/sphinx/domains/c/__init__.py
@@ -771,9 +771,10 @@ def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> Non
ourObjects[fullname] = (fn, id_, objtype)
# no need to warn on duplicates, the symbol merge already does that
- def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
- typ: str, target: str, node: pending_xref,
- contnode: Element) -> tuple[Element | None, str | None]:
+ def _resolve_xref_inner(
+ self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> tuple[nodes.reference, str] | tuple[None, None]:
parser = DefinitionParser(target, location=node, config=env.config)
try:
name = parser.parse_xref_object()
@@ -810,13 +811,13 @@ def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder:
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref,
- contnode: Element) -> Element | None:
+ contnode: Element) -> nodes.reference | None:
return self._resolve_xref_inner(env, fromdocname, builder, typ,
target, node, contnode)[0]
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
with logging.suppress_logging():
retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder,
'any', target, node, contnode)
@@ -844,7 +845,9 @@ def setup(app: Sphinx) -> ExtensionMetadata:
app.add_config_value("c_id_attributes", [], 'env', types={list, tuple})
app.add_config_value("c_paren_attributes", [], 'env', types={list, tuple})
app.add_config_value("c_extra_keywords", _macroKeywords, 'env', types={set, list})
- app.add_config_value("c_maximum_signature_line_length", None, 'env', types={int, None})
+ app.add_config_value(
+ "c_maximum_signature_line_length", None, 'env', types={int, type(None)}
+ )
app.add_post_transform(AliasTransform)
return {
diff --git a/sphinx/domains/citation.py b/sphinx/domains/citation.py
index 58d55774f48..0bac6040c46 100644
--- a/sphinx/domains/citation.py
+++ b/sphinx/domains/citation.py
@@ -86,7 +86,7 @@ def check_consistency(self) -> None:
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
docname, labelid, lineno = self.citations.get(target, ('', '', 0))
if not docname:
return None
@@ -96,7 +96,7 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'ref', target, node, contnode)
if refnode is None:
return []
diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py
index 5e7942dd65b..b038a2efd2d 100644
--- a/sphinx/domains/cpp/__init__.py
+++ b/sphinx/domains/cpp/__init__.py
@@ -962,9 +962,10 @@ def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> Non
logger.debug("\tresult end")
logger.debug("merge_domaindata end")
- def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
- typ: str, target: str, node: pending_xref,
- contnode: Element) -> tuple[Element | None, str | None]:
+ def _resolve_xref_inner(
+ self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> tuple[nodes.reference, str] | tuple[None, None]:
# add parens again for those that could be functions
if typ in {'any', 'func'}:
target += '()'
@@ -1112,13 +1113,13 @@ def checkType() -> bool:
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
return self._resolve_xref_inner(env, fromdocname, builder, typ,
target, node, contnode)[0]
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
with logging.suppress_logging():
retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder,
'any', target, node, contnode)
@@ -1162,7 +1163,9 @@ def setup(app: Sphinx) -> ExtensionMetadata:
app.add_config_value("cpp_index_common_prefix", [], 'env')
app.add_config_value("cpp_id_attributes", [], 'env', types={list, tuple})
app.add_config_value("cpp_paren_attributes", [], 'env', types={list, tuple})
- app.add_config_value("cpp_maximum_signature_line_length", None, 'env', types={int, None})
+ app.add_config_value(
+ "cpp_maximum_signature_line_length", None, 'env', types={int, type(None)}
+ )
app.add_post_transform(AliasTransform)
# debug stuff
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index 7ad37987b38..ca9b78b53b1 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -466,7 +466,7 @@ def find_obj(
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
mod_name = node.get('js:module')
prefix = node.get('js:object')
searchorder = 1 if node.hasattr('refspecific') else 0
@@ -477,7 +477,7 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
mod_name = node.get('js:module')
prefix = node.get('js:object')
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py
index 19e050739db..e59776d7a07 100644
--- a/sphinx/domains/math.py
+++ b/sphinx/domains/math.py
@@ -95,7 +95,7 @@ def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> Non
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
assert typ in {'eq', 'numref'}
result = self.equations.get(target)
if result:
@@ -126,7 +126,7 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode)
if refnode is None:
return []
diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py
index eaa3b158d38..a1285bd3f21 100644
--- a/sphinx/domains/python/__init__.py
+++ b/sphinx/domains/python/__init__.py
@@ -828,7 +828,7 @@ def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
type: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
modname = node.get('py:module')
clsname = node.get('py:class')
searchmode = 1 if node.hasattr('refspecific') else 0
@@ -875,10 +875,10 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
+ ) -> list[tuple[str, nodes.reference]]:
modname = node.get('py:module')
clsname = node.get('py:class')
- results: list[tuple[str, Element]] = []
+ results: list[tuple[str, nodes.reference]] = []
# always search in "refspecific" mode with the :any: role
matches = self.find_obj(env, modname, clsname, target, None, 1)
@@ -910,7 +910,7 @@ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Bui
return results
def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
- contnode: Node) -> Element:
+ contnode: Node) -> nodes.reference:
# get additional info for modules
module: ModuleEntry = self.modules[name]
title_parts = [name]
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index f2ada04e1fe..7dbb7815883 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -18,6 +18,7 @@
if TYPE_CHECKING:
from collections.abc import Iterator, Set
+ from docutils import nodes
from docutils.nodes import Element
from sphinx.addnodes import desc_signature, pending_xref
@@ -262,7 +263,7 @@ def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> Non
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
objtypes = self.objtypes_for_role(typ)
if not objtypes:
return None
@@ -276,8 +277,8 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
- ) -> list[tuple[str, Element]]:
- results: list[tuple[str, Element]] = []
+ ) -> list[tuple[str, nodes.reference]]:
+ results: list[tuple[str, nodes.reference]] = []
for objtype in self.object_types:
result = self.objects.get((objtype, target))
if result:
diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py
index 470a1c05428..af467c0d8cc 100644
--- a/sphinx/domains/std/__init__.py
+++ b/sphinx/domains/std/__init__.py
@@ -845,10 +845,11 @@ def add_program_option(self, program: str | None, name: str,
self.progoptions[program, name] = (docname, labelid)
def build_reference_node(self, fromdocname: str, builder: Builder, docname: str,
- labelid: str, sectname: str, rolename: str, **options: Any,
- ) -> Element:
- nodeclass = options.pop('nodeclass', nodes.reference)
- newnode = nodeclass('', '', internal=True, **options)
+ labelid: str, sectname: str, rolename: str, *,
+ node_class: type[nodes.reference] = nodes.reference,
+ **options: Any,
+ ) -> nodes.reference:
+ newnode = node_class('', '', internal=True, **options)
innernode = nodes.inline(sectname, sectname)
if innernode.get('classes') is not None:
innernode['classes'].append('std')
@@ -871,11 +872,11 @@ def build_reference_node(self, fromdocname: str, builder: Builder, docname: str,
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
- ) -> Element | None:
+ ) -> nodes.reference | None:
if typ == 'ref':
resolver = self._resolve_ref_xref
elif typ == 'numref':
- resolver = self._resolve_numref_xref
+ resolver = self._resolve_numref_xref # type: ignore[assignment]
elif typ == 'keyword':
resolver = self._resolve_keyword_xref
elif typ == 'doc':
@@ -891,7 +892,7 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
def _resolve_ref_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str, node: pending_xref,
- contnode: Element) -> Element | None:
+ contnode: Element) -> nodes.reference | None:
if node['refexplicit']:
# reference to anonymous label; the reference uses
# the supplied link caption
@@ -909,7 +910,8 @@ def _resolve_ref_xref(self, env: BuildEnvironment, fromdocname: str,
def _resolve_numref_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element
+ ) -> nodes.reference | Element | None:
if target in self.labels:
docname, labelid, figname = self.labels.get(target, ('', '', ''))
else:
@@ -968,12 +970,12 @@ def _resolve_numref_xref(self, env: BuildEnvironment, fromdocname: str,
return self.build_reference_node(fromdocname, builder,
docname, labelid, newtitle, 'numref',
- nodeclass=addnodes.number_reference,
+ node_class=addnodes.number_reference,
title=title)
def _resolve_keyword_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element) -> nodes.reference | None:
# keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('', '', ''))
if not docname:
@@ -983,7 +985,7 @@ def _resolve_keyword_xref(self, env: BuildEnvironment, fromdocname: str,
def _resolve_doc_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element) -> nodes.reference | None:
# directly reference to document by source name; can be absolute or relative
refdoc = node.get('refdoc', fromdocname)
docname = docname_join(refdoc, node['reftarget'])
@@ -1000,7 +1002,7 @@ def _resolve_doc_xref(self, env: BuildEnvironment, fromdocname: str,
def _resolve_option_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element) -> nodes.reference | None:
progname = node.get('std:program')
target = target.strip()
docname, labelid = self.progoptions.get((progname, target), ('', ''))
@@ -1033,7 +1035,7 @@ def _resolve_option_xref(self, env: BuildEnvironment, fromdocname: str,
def _resolve_term_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element) -> nodes.reference | None:
result = self._resolve_obj_xref(env, fromdocname, builder, typ,
target, node, contnode)
if result:
@@ -1048,7 +1050,7 @@ def _resolve_term_xref(self, env: BuildEnvironment, fromdocname: str,
def _resolve_obj_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element | None:
+ node: pending_xref, contnode: Element) -> nodes.reference | None:
objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes:
if (objtype, target) in self.objects:
@@ -1063,8 +1065,8 @@ def _resolve_obj_xref(self, env: BuildEnvironment, fromdocname: str,
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str,
builder: Builder, target: str, node: pending_xref,
- contnode: Element) -> list[tuple[str, Element]]:
- results: list[tuple[str, Element]] = []
+ contnode: Element) -> list[tuple[str, nodes.reference]]:
+ results: list[tuple[str, nodes.reference]] = []
ltarget = target.lower() # :ref: lowercases its target automatically
for role in ('ref', 'option'): # do not try "keyword"
res = self.resolve_xref(env, fromdocname, builder, role,
diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py
index e642a95b134..cb590b77a10 100644
--- a/sphinx/transforms/post_transforms/__init__.py
+++ b/sphinx/transforms/post_transforms/__init__.py
@@ -139,7 +139,7 @@ def resolve_anyref(
"""Resolve reference generated by the "any" role."""
stddomain = self.env.domains.standard_domain
target = node['reftarget']
- results: list[tuple[str, Element]] = []
+ results: list[tuple[str, nodes.reference]] = []
# first, try resolving as :doc:
doc_ref = stddomain.resolve_xref(
self.env, refdoc, self.app.builder, 'doc', target, node, contnode
From db1a190c588b38c638676886fc4e1eadc2549549 Mon Sep 17 00:00:00 2001
From: Rafael Fontenelle
Date: Sat, 2 Nov 2024 20:47:24 -0300
Subject: [PATCH 17/39] Use default locale_dirs value (#13079)
---
doc/conf.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/doc/conf.py b/doc/conf.py
index e7976f59a87..9c8aa877075 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -173,7 +173,6 @@
}
# Sphinx document translation with sphinx gettext feature uses these settings:
-locale_dirs = ['locale/']
gettext_compact = False
nitpick_ignore = {
From 1094556afb5740d6378c1ae2742af2f0bf17873b Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sun, 3 Nov 2024 02:50:31 +0000
Subject: [PATCH 18/39] Bump Ruff to 0.7.2
---
pyproject.toml | 2 +-
sphinx/_cli/__init__.py | 2 +-
sphinx/builders/__init__.py | 2 +-
sphinx/builders/latex/__init__.py | 5 +-
sphinx/builders/manpage.py | 7 +--
sphinx/cmd/build.py | 8 ++-
sphinx/cmd/make_mode.py | 2 +-
sphinx/cmd/quickstart.py | 2 +-
sphinx/domains/c/__init__.py | 2 +-
sphinx/domains/c/_parser.py | 2 +-
sphinx/domains/cpp/__init__.py | 2 +-
sphinx/domains/cpp/_parser.py | 2 +-
sphinx/domains/python/__init__.py | 6 +--
sphinx/domains/rst.py | 8 +--
sphinx/domains/std/__init__.py | 10 ++--
sphinx/environment/adapters/toctree.py | 2 +-
sphinx/ext/autodoc/importer.py | 2 +-
sphinx/ext/intersphinx/_load.py | 2 +-
sphinx/ext/intersphinx/_resolve.py | 4 +-
sphinx/ext/viewcode.py | 4 +-
sphinx/testing/path.py | 2 +-
sphinx/testing/util.py | 12 ++---
sphinx/util/docfields.py | 2 +-
sphinx/util/docutils.py | 4 +-
sphinx/writers/html5.py | 8 +--
sphinx/writers/latex.py | 4 +-
sphinx/writers/manpage.py | 2 +-
sphinx/writers/texinfo.py | 17 +++---
tests/test_builders/test_build_html.py | 7 +--
.../test_builders/test_build_html_5_output.py | 3 +-
tests/test_builders/test_build_latex.py | 36 +++++--------
tests/test_builders/test_build_text.py | 22 +-------
tests/test_config/test_config.py | 9 ++--
tests/test_directives/test_directive_code.py | 7 +--
tests/test_directives/test_directive_only.py | 12 ++---
tests/test_domains/test_domain_c.py | 6 +--
tests/test_domains/test_domain_cpp.py | 11 ++--
tests/test_domains/test_domain_py_pyobject.py | 5 +-
tests/test_extensions/test_ext_apidoc.py | 12 ++---
tests/test_extensions/test_ext_autodoc.py | 12 ++---
tests/test_extensions/test_ext_autosummary.py | 27 +++-------
tests/test_extensions/test_ext_graphviz.py | 7 +--
.../test_ext_napoleon_docstring.py | 6 +--
tests/test_intl/test_intl.py | 54 +++++++++----------
tests/test_markup/test_markup.py | 5 +-
tests/test_pycode/test_pycode_parser.py | 5 +-
tests/test_search.py | 6 +--
.../test_transforms_reorder_nodes.py | 9 +---
48 files changed, 150 insertions(+), 238 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 9c46ba94694..4eb5269ed2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -81,7 +81,7 @@ docs = [
]
lint = [
"flake8>=6.0",
- "ruff==0.7.0",
+ "ruff==0.7.2",
"mypy==1.13.0",
"sphinx-lint>=0.9",
"types-colorama==0.4.15.20240311",
diff --git a/sphinx/_cli/__init__.py b/sphinx/_cli/__init__.py
index 3160f08373c..270d9210e33 100644
--- a/sphinx/_cli/__init__.py
+++ b/sphinx/_cli/__init__.py
@@ -170,7 +170,7 @@ def _format_metavar(
def error(self, message: str) -> NoReturn:
sys.stderr.write(
__(
- '{0}: error: {1}\n' "Run '{0} --help' for information" # NoQA: COM812
+ "{0}: error: {1}\nRun '{0} --help' for information" # NoQA: COM812
).format(self.prog, message)
)
raise SystemExit(2)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 21b73f873a0..bf2f3f86080 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -203,7 +203,7 @@ def post_process_images(self, doctree: Node) -> None:
image_uri = images.get_original_image_uri(node['uri'])
if mimetypes:
logger.warning(
- __('a suitable image for %s builder not found: ' '%s (%s)'),
+ __('a suitable image for %s builder not found: %s (%s)'),
self.name,
mimetypes,
image_uri,
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index db7adc5b0d7..19ea60e1290 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -166,10 +166,7 @@ def init_document_data(self) -> None:
docname = entry[0]
if docname not in self.env.all_docs:
logger.warning(
- __(
- '"latex_documents" config value references unknown '
- 'document %s'
- ),
+ __('"latex_documents" config value references unknown document %s'),
docname,
)
continue
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 2e24486d174..4535776b9cb 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -44,10 +44,7 @@ class ManualPageBuilder(Builder):
def init(self) -> None:
if not self.config.man_pages:
logger.warning(
- __(
- 'no "man_pages" config value found; no manual pages '
- 'will be written'
- )
+ __('no "man_pages" config value found; no manual pages will be written')
)
def get_outdated_docs(self) -> str | list[str]:
@@ -73,7 +70,7 @@ def write_documents(self, _docnames: Set[str]) -> None:
docname, name, description, authors, section = info
if docname not in self.env.all_docs:
logger.warning(
- __('"man_pages" config value references unknown ' 'document %s'),
+ __('"man_pages" config value references unknown document %s'),
docname,
)
continue
diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py
index db76bc3ad85..37f3f26c453 100644
--- a/sphinx/cmd/build.py
+++ b/sphinx/cmd/build.py
@@ -217,14 +217,14 @@ def get_parser() -> argparse.ArgumentParser:
'-a',
action='store_true',
dest='force_all',
- help=__('write all files (default: only write new and ' 'changed files)'),
+ help=__('write all files (default: only write new and changed files)'),
)
group.add_argument(
'--fresh-env',
'-E',
action='store_true',
dest='freshenv',
- help=__("don't use a saved environment, always read " 'all files'),
+ help=__("don't use a saved environment, always read all files"),
)
group = parser.add_argument_group(__('path options'))
@@ -243,9 +243,7 @@ def get_parser() -> argparse.ArgumentParser:
'-c',
metavar='PATH',
dest='confdir',
- help=__(
- 'directory for the configuration file (conf.py) ' '(default: SOURCE_DIR)'
- ),
+ help=__('directory for the configuration file (conf.py) (default: SOURCE_DIR)'),
)
group = parser.add_argument_group('build configuration options')
diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py
index 00c8744868d..5966b628a3e 100644
--- a/sphinx/cmd/make_mode.py
+++ b/sphinx/cmd/make_mode.py
@@ -49,7 +49,7 @@
(
'',
'doctest',
- 'to run all doctests embedded in the documentation ' '(if enabled)',
+ 'to run all doctests embedded in the documentation (if enabled)',
),
('', 'coverage', 'to run coverage check of the documentation (if enabled)'),
('', 'clean', 'to remove everything in the build directory'),
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index 1176dc14b40..18d06c39f1d 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -507,7 +507,7 @@ def write_file(fpath: str, content: str, newline: str | None = None) -> None:
end='',
)
if d['makefile'] or d['batchfile']:
- print(__('Use the Makefile to build the docs, like so:\n' ' make builder'))
+ print(__('Use the Makefile to build the docs, like so:\n make builder'))
else:
print(
__(
diff --git a/sphinx/domains/c/__init__.py b/sphinx/domains/c/__init__.py
index 23729f09166..28d77227eee 100644
--- a/sphinx/domains/c/__init__.py
+++ b/sphinx/domains/c/__init__.py
@@ -837,7 +837,7 @@ def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
objectType = symbol.declaration.objectType
docname = symbol.docname
newestId = symbol.declaration.get_newest_id()
- yield (name, dispname, objectType, docname, newestId, 1)
+ yield name, dispname, objectType, docname, newestId, 1
def setup(app: Sphinx) -> ExtensionMetadata:
diff --git a/sphinx/domains/c/_parser.py b/sphinx/domains/c/_parser.py
index 2eeb4e7ef47..d6afb24ade0 100644
--- a/sphinx/domains/c/_parser.py
+++ b/sphinx/domains/c/_parser.py
@@ -465,7 +465,7 @@ def _parse_expression_fallback(
brackets = {'(': ')', '{': '}', '[': ']'}
symbols: list[str] = []
while not self.eof:
- if (len(symbols) == 0 and self.current_char in end):
+ if len(symbols) == 0 and self.current_char in end:
break
if self.current_char in brackets:
symbols.append(brackets[self.current_char])
diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py
index b038a2efd2d..743aa0d1018 100644
--- a/sphinx/domains/cpp/__init__.py
+++ b/sphinx/domains/cpp/__init__.py
@@ -1142,7 +1142,7 @@ def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
objectType = symbol.declaration.objectType
docname = symbol.docname
newestId = symbol.declaration.get_newest_id()
- yield (name, dispname, objectType, docname, newestId, 1)
+ yield name, dispname, objectType, docname, newestId, 1
def get_full_qualified_name(self, node: Element) -> str | None:
target = node.get('reftarget', None)
diff --git a/sphinx/domains/cpp/_parser.py b/sphinx/domains/cpp/_parser.py
index 5eedd078d9b..09f9e69e0cd 100644
--- a/sphinx/domains/cpp/_parser.py
+++ b/sphinx/domains/cpp/_parser.py
@@ -795,7 +795,7 @@ def _parse_expression_fallback(self, end: list[str],
brackets = {'(': ')', '{': '}', '[': ']', '<': '>'}
symbols: list[str] = []
while not self.eof:
- if (len(symbols) == 0 and self.current_char in end):
+ if len(symbols) == 0 and self.current_char in end:
break
if self.current_char in brackets:
symbols.append(brackets[self.current_char])
diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py
index a1285bd3f21..24dfc2a5756 100644
--- a/sphinx/domains/python/__init__.py
+++ b/sphinx/domains/python/__init__.py
@@ -927,14 +927,14 @@ def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
for modname, mod in self.modules.items():
- yield (modname, modname, 'module', mod.docname, mod.node_id, 0)
+ yield modname, modname, 'module', mod.docname, mod.node_id, 0
for refname, obj in self.objects.items():
if obj.objtype != 'module': # modules are already handled
if obj.aliased:
# aliased names are not full-text searchable.
- yield (refname, refname, obj.objtype, obj.docname, obj.node_id, -1)
+ yield refname, refname, obj.objtype, obj.docname, obj.node_id, -1
else:
- yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
+ yield refname, refname, obj.objtype, obj.docname, obj.node_id, 1
def get_full_qualified_name(self, node: Element) -> str | None:
modname = node.get('py:module')
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 7dbb7815883..4bed18e0c9b 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -99,15 +99,15 @@ def parse_directive(d: str) -> tuple[str, str]:
dir = d.strip()
if not dir.startswith('.'):
# Assume it is a directive without syntax
- return (dir, '')
+ return dir, ''
m = dir_sig_re.match(dir)
if not m:
- return (dir, '')
+ return dir, ''
parsed_dir, parsed_args = m.groups()
if parsed_args.strip():
- return (parsed_dir.strip(), ' ' + parsed_args.strip())
+ return parsed_dir.strip(), ' ' + parsed_args.strip()
else:
- return (parsed_dir.strip(), '')
+ return parsed_dir.strip(), ''
class ReSTDirective(ReSTMarkup):
diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py
index af467c0d8cc..0e214c92a5b 100644
--- a/sphinx/domains/std/__init__.py
+++ b/sphinx/domains/std/__init__.py
@@ -1089,23 +1089,23 @@ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str,
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
# handle the special 'doc' reference here
for doc in self.env.all_docs:
- yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
+ yield doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1
for (prog, option), info in self.progoptions.items():
if prog:
fullname = f'{prog}.{option}'
- yield (fullname, fullname, 'cmdoption', info[0], info[1], 1)
+ yield fullname, fullname, 'cmdoption', info[0], info[1], 1
else:
- yield (option, option, 'cmdoption', info[0], info[1], 1)
+ yield option, option, 'cmdoption', info[0], info[1], 1
for (type, name), info in self.objects.items():
yield (name, name, type, info[0], info[1],
self.object_types[type].attrs['searchprio'])
for name, (docname, labelid, sectionname) in self.labels.items():
- yield (name, sectionname, 'label', docname, labelid, -1)
+ yield name, sectionname, 'label', docname, labelid, -1
# add anonymous-only labels as well
non_anon_labels = set(self.labels)
for name, (docname, labelid) in self.anonlabels.items():
if name not in non_anon_labels:
- yield (name, name, 'label', docname, labelid, -1)
+ yield name, name, 'label', docname, labelid, -1
def get_type_name(self, type: ObjType, primary: bool = False) -> str:
# never prepend "Default"
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index edd873b5f44..3079c7dc543 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -315,7 +315,7 @@ def _toctree_entry(
else:
if ref in parents:
logger.warning(
- __('circular toctree references ' 'detected, ignoring: %s <- %s'),
+ __('circular toctree references detected, ignoring: %s <- %s'),
ref,
' <- '.join(parents),
location=ref,
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 4311d428870..ceefdcd9f5f 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -67,7 +67,7 @@ def should_ignore(name: str, value: Any) -> bool:
def query(name: str, defining_class: type) -> tuple[str, type, Any] | None:
value = attrgetter(enum_class, name, sentinel)
if value is not sentinel:
- return (name, defining_class, value)
+ return name, defining_class, value
return None
# attributes defined on a parent type, possibly shadowed later by
diff --git a/sphinx/ext/intersphinx/_load.py b/sphinx/ext/intersphinx/_load.py
index 27b11673465..dde2e34e1c2 100644
--- a/sphinx/ext/intersphinx/_load.py
+++ b/sphinx/ext/intersphinx/_load.py
@@ -272,7 +272,7 @@ def _fetch_inventory_group(
else:
issues = '\n'.join(f[0] % f[1:] for f in failures)
LOGGER.warning(
- __('failed to reach any of the inventories ' 'with the following issues:')
+ __('failed to reach any of the inventories with the following issues:')
+ '\n'
+ issues
)
diff --git a/sphinx/ext/intersphinx/_resolve.py b/sphinx/ext/intersphinx/_resolve.py
index 9387d1e1096..be279b8c350 100644
--- a/sphinx/ext/intersphinx/_resolve.py
+++ b/sphinx/ext/intersphinx/_resolve.py
@@ -516,9 +516,9 @@ def get_role_name(self, name: str) -> tuple[str, str] | None:
return None
if domain and self.is_existent_role(domain, role):
- return (domain, role)
+ return domain, role
elif self.is_existent_role('std', role):
- return ('std', role)
+ return 'std', role
else:
return None
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 9991cf5d426..91ba1f049ed 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -311,7 +311,7 @@ def collect_pages(app: Sphinx) -> Iterator[tuple[str, dict[str, Any], str]]:
'body': (_('Source code for %s
') % modname +
'\n'.join(lines)),
}
- yield (pagename, context, 'page.html')
+ yield pagename, context, 'page.html'
if not modnames:
return
@@ -339,7 +339,7 @@ def collect_pages(app: Sphinx) -> Iterator[tuple[str, dict[str, Any], str]]:
''.join(html)),
}
- yield (posixpath.join(OUTPUT_DIRNAME, 'index'), context, 'page.html')
+ yield posixpath.join(OUTPUT_DIRNAME, 'index'), context, 'page.html'
def setup(app: Sphinx) -> ExtensionMetadata:
diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py
index 9792dcb7479..b469588ea6d 100644
--- a/sphinx/testing/path.py
+++ b/sphinx/testing/path.py
@@ -13,7 +13,7 @@
from collections.abc import Callable
warnings.warn(
- "'sphinx.testing.path' is deprecated. " "Use 'os.path' or 'pathlib' instead.",
+ "'sphinx.testing.path' is deprecated. Use 'os.path' or 'pathlib' instead.",
RemovedInSphinx90Warning,
stacklevel=2,
)
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index 4d221133ffb..d95ffb46bc3 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -40,9 +40,9 @@ def assert_node(node: Node, cls: Any = None, xpath: str = '', **kwargs: Any) ->
assert (
isinstance(node, nodes.Element)
), f'The node{xpath} does not have any children' # fmt: skip
- assert (
- len(node) == 1
- ), f'The node{xpath} has {len(node)} child nodes, not one'
+ assert len(node) == 1, (
+ f'The node{xpath} has {len(node)} child nodes, not one'
+ )
assert_node(node[0], cls[1:], xpath=xpath + '[0]', **kwargs)
elif isinstance(cls, tuple):
assert (
@@ -71,9 +71,9 @@ def assert_node(node: Node, cls: Any = None, xpath: str = '', **kwargs: Any) ->
if (key := key.replace('_', '-')) not in node:
msg = f'The node{xpath} does not have {key!r} attribute: {node!r}'
raise AssertionError(msg)
- assert (
- node[key] == value
- ), f'The node{xpath}[{key}] is not {value!r}: {node[key]!r}'
+ assert node[key] == value, (
+ f'The node{xpath}[{key}] is not {value!r}: {node[key]!r}'
+ )
# keep this to restrict the API usage and to have a correct return type
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index bb8142570f0..33fda6f5dcd 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -132,7 +132,7 @@ def make_xrefs(
]
def make_entry(self, fieldarg: str, content: list[Node]) -> tuple[str, list[Node]]:
- return (fieldarg, content)
+ return fieldarg, content
def make_field(
self,
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 4f085bfeee3..30c87595f31 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -716,7 +716,7 @@ def dispatch_visit(self, node: Node) -> None:
3. ``self.unknown_visit()``
"""
for node_class in node.__class__.__mro__:
- method = getattr(self, 'visit_%s' % (node_class.__name__), None)
+ method = getattr(self, 'visit_%s' % node_class.__name__, None)
if method:
method(node)
break
@@ -733,7 +733,7 @@ def dispatch_departure(self, node: Node) -> None:
3. ``self.unknown_departure()``
"""
for node_class in node.__class__.__mro__:
- method = getattr(self, 'depart_%s' % (node_class.__name__), None)
+ method = getattr(self, 'depart_%s' % node_class.__name__, None)
if method:
method(node)
break
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index e002788fc97..b6e342ac119 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -327,9 +327,9 @@ def visit_reference(self, node: Element) -> None:
atts['href'] = self.cloak_mailto(atts['href'])
self.in_mailto = True
else:
- assert (
- 'refid' in node
- ), 'References must have "refuri" or "refid" attribute.'
+ assert 'refid' in node, (
+ 'References must have "refuri" or "refid" attribute.'
+ )
atts['href'] = '#' + node['refid']
if not isinstance(node.parent, nodes.TextElement):
assert len(node) == 1 and isinstance(node[0], nodes.image) # NoQA: PT018
@@ -379,7 +379,7 @@ def get_secnumber(self, node: Element) -> tuple[int, ...] | None:
if isinstance(node.parent, nodes.section):
if self.builder.name == 'singlehtml':
docname = self.docnames[-1]
- anchorname = f"{docname}/#{node.parent['ids'][0]}"
+ anchorname = f'{docname}/#{node.parent["ids"][0]}'
if anchorname not in self.builder.secnumbers:
# try first heading which has no anchor
anchorname = f'{docname}/'
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index c98135efa7f..a9fbd0161c0 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -2452,7 +2452,7 @@ def visit_math(self, node: Element) -> None:
def visit_math_block(self, node: Element) -> None:
if node.get('label'):
- label = f"equation:{node['docname']}:{node['label']}"
+ label = f'equation:{node["docname"]}:{node["label"]}'
else:
label = None
@@ -2469,7 +2469,7 @@ def visit_math_block(self, node: Element) -> None:
raise nodes.SkipNode
def visit_math_reference(self, node: Element) -> None:
- label = f"equation:{node['docname']}:{node['target']}"
+ label = f'equation:{node["docname"]}:{node["target"]}'
eqref_format = self.config.math_eqref_format
if eqref_format:
try:
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 5bfc23481c0..7b7db13961f 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -117,7 +117,7 @@ def header(self) -> str:
' "%(date)s" "%(version)s" "%(manual_group)s"\n'
)
if self._docinfo['subtitle']:
- tmpl += '.SH NAME\n' '%(title)s \\- %(subtitle)s\n'
+ tmpl += '.SH NAME\n%(title)s \\- %(subtitle)s\n'
return tmpl % self._docinfo
def visit_start_of_file(self, node: Element) -> None:
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 997561b16fd..426b0288390 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -250,9 +250,10 @@ def init_settings(self) -> None:
'(%s)' % elements['filename'],
self.escape_arg(self.settings.texinfo_dir_description),
)
- elements['direntry'] = (
- '@dircategory %s\n' '@direntry\n' '%s' '@end direntry\n'
- ) % (self.escape_id(self.settings.texinfo_dir_category), entry)
+ elements['direntry'] = '@dircategory %s\n@direntry\n%s@end direntry\n' % (
+ self.escape_id(self.settings.texinfo_dir_category),
+ entry,
+ )
elements['copying'] = COPYING % elements
# allow the user to override them all
elements.update(self.settings.texinfo_elements)
@@ -448,10 +449,10 @@ def _add_detailed_menu(name: str) -> None:
for subentry in entries:
_add_detailed_menu(subentry)
- self.body.append('\n@detailmenu\n' ' --- The Detailed Node Listing ---\n')
+ self.body.append('\n@detailmenu\n --- The Detailed Node Listing ---\n')
for entry in entries:
_add_detailed_menu(entry)
- self.body.append('\n@end detailmenu\n' '@end menu\n')
+ self.body.append('\n@end detailmenu\n@end menu\n')
def tex_image_length(self, width_str: str) -> str:
match = re.match(r'(\d*\.?\d*)\s*(\S*)', width_str)
@@ -1119,7 +1120,7 @@ def _visit_named_admonition(self, node: Element) -> None:
def depart_admonition(self, node: Element) -> None:
self.ensure_eol()
- self.body.append('@end quotation\n' '@end cartouche\n')
+ self.body.append('@end quotation\n@end cartouche\n')
visit_attention = _visit_named_admonition
depart_attention = depart_admonition
@@ -1236,7 +1237,7 @@ def visit_image(self, node: Element) -> None:
width = self.tex_image_length(node.get('width', ''))
height = self.tex_image_length(node.get('height', ''))
alt = self.escape_arg(node.get('alt', ''))
- filename = f"{self.elements['filename'][:-5]}-figures/{name}" # type: ignore[index]
+ filename = f'{self.elements["filename"][:-5]}-figures/{name}' # type: ignore[index]
self.body.append(f'\n@image{{{filename},{width},{height},{alt},{ext[1:]}}}\n')
def depart_image(self, node: Element) -> None:
@@ -1280,7 +1281,7 @@ def visit_substitution_definition(self, node: Element) -> None:
def visit_system_message(self, node: Element) -> None:
self.body.append(
- '\n@verbatim\n' '\n' '@end verbatim\n' % node.astext()
+ '\n@verbatim\n\n@end verbatim\n' % node.astext()
)
raise nodes.SkipNode
diff --git a/tests/test_builders/test_build_html.py b/tests/test_builders/test_build_html.py
index 1e82e70eaa1..097f895d7c8 100644
--- a/tests/test_builders/test_build_html.py
+++ b/tests/test_builders/test_build_html.py
@@ -300,9 +300,7 @@ def test_html_raw_directive(app):
[
(".//link[@href='_static/persistent.css'][@rel='stylesheet']", '', True),
(
- ".//link[@href='_static/default.css']"
- "[@rel='stylesheet']"
- "[@title='Default']",
+ ".//link[@href='_static/default.css'][@rel='stylesheet'][@title='Default']",
'',
True,
),
@@ -338,8 +336,7 @@ def test_html_raw_directive(app):
True,
),
(
- ".//link[@href='_static/more_alternate2.css']"
- "[@rel='alternate stylesheet']",
+ ".//link[@href='_static/more_alternate2.css'][@rel='alternate stylesheet']",
'',
True,
),
diff --git a/tests/test_builders/test_build_html_5_output.py b/tests/test_builders/test_build_html_5_output.py
index a0cc15aeffa..a618fae1e45 100644
--- a/tests/test_builders/test_build_html_5_output.py
+++ b/tests/test_builders/test_build_html_5_output.py
@@ -162,8 +162,7 @@ def checker(nodes: Iterable[Element]) -> Literal[True]:
),
(
'markup.html',
- ".//a[@href='#with']"
- "[@class='reference internal']/code/span[@class='pre']",
+ ".//a[@href='#with'][@class='reference internal']/code/span[@class='pre']",
'^with$',
),
(
diff --git a/tests/test_builders/test_build_latex.py b/tests/test_builders/test_build_latex.py
index b6207bae60c..d95c5e4c286 100644
--- a/tests/test_builders/test_build_latex.py
+++ b/tests/test_builders/test_build_latex.py
@@ -389,8 +389,7 @@ def test_numref(app):
print(app.status.getvalue())
print(app.warning.getvalue())
assert (
- '\\hyperref[\\detokenize{index:fig1}]'
- '{Fig.\\@ \\ref{\\detokenize{index:fig1}}}'
+ '\\hyperref[\\detokenize{index:fig1}]{Fig.\\@ \\ref{\\detokenize{index:fig1}}}'
) in result
assert (
'\\hyperref[\\detokenize{baz:fig22}]{Figure\\ref{\\detokenize{baz:fig22}}}'
@@ -400,8 +399,7 @@ def test_numref(app):
'{Table \\ref{\\detokenize{index:table-1}}}'
) in result
assert (
- '\\hyperref[\\detokenize{baz:table22}]'
- '{Table:\\ref{\\detokenize{baz:table22}}}'
+ '\\hyperref[\\detokenize{baz:table22}]{Table:\\ref{\\detokenize{baz:table22}}}'
) in result
assert (
'\\hyperref[\\detokenize{index:code-1}]'
@@ -462,8 +460,7 @@ def test_numref_with_prefix1(app):
assert '\\ref{\\detokenize{index:code-1}}' in result
assert '\\ref{\\detokenize{baz:code22}}' in result
assert (
- '\\hyperref[\\detokenize{index:fig1}]'
- '{Figure:\\ref{\\detokenize{index:fig1}}}'
+ '\\hyperref[\\detokenize{index:fig1}]{Figure:\\ref{\\detokenize{index:fig1}}}'
) in result
assert (
'\\hyperref[\\detokenize{baz:fig22}]{Figure\\ref{\\detokenize{baz:fig22}}}'
@@ -473,8 +470,7 @@ def test_numref_with_prefix1(app):
'{Tab\\_\\ref{\\detokenize{index:table-1}}}'
) in result
assert (
- '\\hyperref[\\detokenize{baz:table22}]'
- '{Table:\\ref{\\detokenize{baz:table22}}}'
+ '\\hyperref[\\detokenize{baz:table22}]{Table:\\ref{\\detokenize{baz:table22}}}'
) in result
assert (
'\\hyperref[\\detokenize{index:code-1}]'
@@ -540,8 +536,7 @@ def test_numref_with_prefix2(app):
'{Tab\\_\\ref{\\detokenize{index:table-1}}:}'
) in result
assert (
- '\\hyperref[\\detokenize{baz:table22}]'
- '{Table:\\ref{\\detokenize{baz:table22}}}'
+ '\\hyperref[\\detokenize{baz:table22}]{Table:\\ref{\\detokenize{baz:table22}}}'
) in result
assert (
'\\hyperref[\\detokenize{index:code-1}]{Code\\sphinxhyphen{}\\ref{\\detokenize{index:code-1}} '
@@ -552,8 +547,7 @@ def test_numref_with_prefix2(app):
'{Code\\sphinxhyphen{}\\ref{\\detokenize{baz:code22}}}'
) in result
assert (
- '\\hyperref[\\detokenize{foo:foo}]'
- '{SECTION\\_\\ref{\\detokenize{foo:foo}}\\_}'
+ '\\hyperref[\\detokenize{foo:foo}]{SECTION\\_\\ref{\\detokenize{foo:foo}}\\_}'
) in result
assert (
'\\hyperref[\\detokenize{bar:bar-a}]'
@@ -590,8 +584,7 @@ def test_numref_with_language_ja(app):
print(app.status.getvalue())
print(app.warning.getvalue())
assert (
- '\\hyperref[\\detokenize{index:fig1}]'
- '{\u56f3 \\ref{\\detokenize{index:fig1}}}'
+ '\\hyperref[\\detokenize{index:fig1}]{\u56f3 \\ref{\\detokenize{index:fig1}}}'
) in result
assert (
'\\hyperref[\\detokenize{baz:fig22}]{Figure\\ref{\\detokenize{baz:fig22}}}'
@@ -601,8 +594,7 @@ def test_numref_with_language_ja(app):
'{\u8868 \\ref{\\detokenize{index:table-1}}}'
) in result
assert (
- '\\hyperref[\\detokenize{baz:table22}]'
- '{Table:\\ref{\\detokenize{baz:table22}}}'
+ '\\hyperref[\\detokenize{baz:table22}]{Table:\\ref{\\detokenize{baz:table22}}}'
) in result
assert (
'\\hyperref[\\detokenize{index:code-1}]'
@@ -937,8 +929,7 @@ def test_footnote(app):
'numbered\n%\n\\end{footnote}'
) in result
assert (
- '\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n'
- '\\end{footnote}'
+ '\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n\\end{footnote}'
) in result
assert (
'\\begin{footnote}[3]\\sphinxAtStartFootnote\nnamed\n%\n\\end{footnote}'
@@ -1880,8 +1871,7 @@ def test_latex_nested_enumerated_list(app):
result = (app.outdir / 'projectnamenotset.tex').read_text(encoding='utf8')
assert (
- '\\sphinxsetlistlabels{\\arabic}{enumi}{enumii}{}{.}%\n'
- '\\setcounter{enumi}{4}\n'
+ '\\sphinxsetlistlabels{\\arabic}{enumi}{enumii}{}{.}%\n\\setcounter{enumi}{4}\n'
) in result
assert (
'\\sphinxsetlistlabels{\\alph}{enumii}{enumiii}{}{.}%\n'
@@ -2201,9 +2191,9 @@ def test_duplicated_labels_before_module(app):
):
tex_label_name = 'index:' + rst_label_name.replace('_', '-')
tex_label_code = r'\phantomsection\label{\detokenize{%s}}' % tex_label_name
- assert (
- content.count(tex_label_code) == 1
- ), f'duplicated label: {tex_label_name!r}'
+ assert content.count(tex_label_code) == 1, (
+ f'duplicated label: {tex_label_name!r}'
+ )
tested_labels.add(tex_label_code)
# ensure that we did not forget any label to check
diff --git a/tests/test_builders/test_build_text.py b/tests/test_builders/test_build_text.py
index 8007ea23157..2023f6f9a53 100644
--- a/tests/test_builders/test_build_text.py
+++ b/tests/test_builders/test_build_text.py
@@ -44,14 +44,7 @@ def test_lineblock(app):
# regression test for #1109: need empty line after line block
app.build()
result = (app.outdir / 'lineblock.txt').read_text(encoding='utf8')
- expect = (
- '* one\n'
- '\n'
- ' line-block 1\n'
- ' line-block 2\n'
- '\n'
- 'followed paragraph.\n'
- )
+ expect = '* one\n\n line-block 1\n line-block 2\n\nfollowed paragraph.\n'
assert result == expect
@@ -265,16 +258,5 @@ def test_secnums(app):
assert lines[5] == ''
assert lines[6] == ' * Sub Bb'
doc2 = (app.outdir / 'doc2.txt').read_text(encoding='utf8')
- expect = (
- 'Section B\n'
- '*********\n'
- '\n'
- '\n'
- 'Sub Ba\n'
- '======\n'
- '\n'
- '\n'
- 'Sub Bb\n'
- '======\n'
- )
+ expect = 'Section B\n*********\n\n\nSub Ba\n======\n\n\nSub Bb\n======\n'
assert doc2 == expect
diff --git a/tests/test_config/test_config.py b/tests/test_config/test_config.py
index f1d6c12a0fd..78b35b83ab1 100644
--- a/tests/test_config/test_config.py
+++ b/tests/test_config/test_config.py
@@ -463,16 +463,15 @@ def test_config_eol(logger, tmp_path):
)
def test_builtin_conf(app):
warnings = app.warning.getvalue()
- assert (
- 'root_doc'
- ) in warnings, 'override on builtin "root_doc" should raise a type warning'
+ assert 'root_doc' in warnings, (
+ 'override on builtin "root_doc" should raise a type warning'
+ )
assert 'language' not in warnings, (
'explicitly permitted override on builtin "language" should NOT raise '
'a type warning'
)
assert 'primary_domain' not in warnings, (
- 'override to None on builtin "primary_domain" should NOT raise a type '
- 'warning'
+ 'override to None on builtin "primary_domain" should NOT raise a type warning'
)
diff --git a/tests/test_directives/test_directive_code.py b/tests/test_directives/test_directive_code.py
index dacf8a3b334..0b81b65c1d8 100644
--- a/tests/test_directives/test_directive_code.py
+++ b/tests/test_directives/test_directive_code.py
@@ -536,12 +536,7 @@ def test_literalinclude_pydecorators(app):
assert actual == expect
actual = literal_include[2].text
- expect = (
- '@function_decorator\n'
- '@other_decorator()\n'
- 'def the_function():\n'
- ' pass\n'
- )
+ expect = '@function_decorator\n@other_decorator()\ndef the_function():\n pass\n'
assert actual == expect
diff --git a/tests/test_directives/test_directive_only.py b/tests/test_directives/test_directive_only.py
index de9230c04da..297f304dfdb 100644
--- a/tests/test_directives/test_directive_only.py
+++ b/tests/test_directives/test_directive_only.py
@@ -28,9 +28,9 @@ def testsects(prefix, sects, indent=0):
assert prefix == parent_num, f'Section out of place: {title!r}'
for i, subsect in enumerate(sects[1]):
num = subsect[0].split()[0]
- assert re.match(
- '[0-9]+[.0-9]*[.]', num
- ), f'Unnumbered section: {subsect[0]!r}'
+ assert re.match('[0-9]+[.0-9]*[.]', num), (
+ f'Unnumbered section: {subsect[0]!r}'
+ )
testsects(prefix + str(i + 1) + '.', subsect, indent + 4)
app.build(filenames=[app.srcdir / 'only.rst'])
@@ -41,6 +41,6 @@ def testsects(prefix, sects, indent=0):
for i, s in enumerate(parts):
testsects(str(i + 1) + '.', s, 4)
actual_headings = '\n'.join(p[0] for p in parts)
- assert (
- len(parts) == 4
- ), f'Expected 4 document level headings, got:\n{actual_headings}'
+ assert len(parts) == 4, (
+ f'Expected 4 document level headings, got:\n{actual_headings}'
+ )
diff --git a/tests/test_domains/test_domain_c.py b/tests/test_domains/test_domain_c.py
index 81451f29845..d90bcd0b7b5 100644
--- a/tests/test_domains/test_domain_c.py
+++ b/tests/test_domains/test_domain_c.py
@@ -923,11 +923,7 @@ def test_domain_c_parse_cvar(app):
@pytest.mark.sphinx('html', testroot='root')
def test_domain_c_parse_no_index_entry(app):
- text = (
- '.. c:function:: void f()\n'
- '.. c:function:: void g()\n'
- ' :no-index-entry:\n'
- )
+ text = '.. c:function:: void f()\n.. c:function:: void g()\n :no-index-entry:\n'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(
diff --git a/tests/test_domains/test_domain_cpp.py b/tests/test_domains/test_domain_cpp.py
index 6a7a7778d8c..a177991a535 100644
--- a/tests/test_domains/test_domain_cpp.py
+++ b/tests/test_domains/test_domain_cpp.py
@@ -159,7 +159,7 @@ def make_id_v2():
id1 = make_id_v1()
id2 = make_id_v2()
- input = f"void f({type_.replace(' ', ' ')} arg)"
+ input = f'void f({type_.replace(" ", " ")} arg)'
output = f'void f({type_} arg)'
check('function', input, {1: id1, 2: id2}, output=output)
@@ -167,7 +167,7 @@ def make_id_v2():
# try permutations of all components
tcs = type_.split()
for p in itertools.permutations(tcs):
- input = f"void f({' '.join(p)} arg)"
+ input = f'void f({" ".join(p)} arg)'
check('function', input, {1: id1, 2: id2})
@@ -1347,8 +1347,7 @@ def test_domain_cpp_ast_template_args():
# from breathe#218
check(
'function',
- 'template '
- 'void allow(F *f, typename func::type tt)',
+ 'template void allow(F *f, typename func::type tt)',
{
2: 'I0E5allowP1FN4funcI1F1BXG != 1EE4typeE',
3: 'I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE',
@@ -1906,9 +1905,7 @@ def test_domain_cpp_build_intersphinx(tmp_path, app):
@pytest.mark.sphinx('html', testroot='root')
def test_domain_cpp_parse_no_index_entry(app):
text = (
- '.. cpp:function:: void f()\n'
- '.. cpp:function:: void g()\n'
- ' :no-index-entry:\n'
+ '.. cpp:function:: void f()\n.. cpp:function:: void g()\n :no-index-entry:\n'
)
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
diff --git a/tests/test_domains/test_domain_py_pyobject.py b/tests/test_domains/test_domain_py_pyobject.py
index 8691e0e08cc..67d91d731e4 100644
--- a/tests/test_domains/test_domain_py_pyobject.py
+++ b/tests/test_domains/test_domain_py_pyobject.py
@@ -165,10 +165,7 @@ def test_pydata_with_union_type_operator(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pyobject_prefix(app):
text = (
- '.. py:class:: Foo\n'
- '\n'
- ' .. py:method:: Foo.say\n'
- ' .. py:method:: FooBar.say'
+ '.. py:class:: Foo\n\n .. py:method:: Foo.say\n .. py:method:: FooBar.say'
)
doctree = restructuredtext.parse(app, text)
assert_node(
diff --git a/tests/test_extensions/test_ext_apidoc.py b/tests/test_extensions/test_ext_apidoc.py
index 3886617c742..7e4edbdfce8 100644
--- a/tests/test_extensions/test_ext_apidoc.py
+++ b/tests/test_extensions/test_ext_apidoc.py
@@ -372,9 +372,9 @@ def test_toc_all_references_should_exist_pep420_enabled(apidoc):
missing_files.append(filename)
all_missing = ', '.join(missing_files)
- assert (
- len(missing_files) == 0
- ), f'File(s) referenced in TOC not found: {all_missing}\nTOC:\n{toc}'
+ assert len(missing_files) == 0, (
+ f'File(s) referenced in TOC not found: {all_missing}\nTOC:\n{toc}'
+ )
@pytest.mark.apidoc(
@@ -403,9 +403,9 @@ def test_toc_all_references_should_exist_pep420_disabled(apidoc):
missing_files.append(filename)
all_missing = ', '.join(missing_files)
- assert (
- len(missing_files) == 0
- ), f'File(s) referenced in TOC not found: {all_missing}\nTOC:\n{toc}'
+ assert len(missing_files) == 0, (
+ f'File(s) referenced in TOC not found: {all_missing}\nTOC:\n{toc}'
+ )
def extract_toc(path):
diff --git a/tests/test_extensions/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py
index f2fb0c8ec11..dbe0fbe1d71 100644
--- a/tests/test_extensions/test_ext_autodoc.py
+++ b/tests/test_extensions/test_ext_autodoc.py
@@ -1558,9 +1558,9 @@ def entry(
def preamble_lookup(
self, doc: str, *, indent: int = 0, **options: Any
) -> list[str]:
- assert (
- doc
- ), f'enumeration class {self.target!r} should have an explicit docstring'
+ assert doc, (
+ f'enumeration class {self.target!r} should have an explicit docstring'
+ )
args = self._preamble_args(functional_constructor=False)
return self._preamble(doc=doc, args=args, indent=indent, **options)
@@ -1568,9 +1568,9 @@ def preamble_lookup(
def preamble_constructor(
self, doc: str, *, indent: int = 0, **options: Any
) -> list[str]:
- assert (
- doc
- ), f'enumeration class {self.target!r} should have an explicit docstring'
+ assert doc, (
+ f'enumeration class {self.target!r} should have an explicit docstring'
+ )
args = self._preamble_args(functional_constructor=True)
return self._preamble(doc=doc, args=args, indent=indent, **options)
diff --git a/tests/test_extensions/test_ext_autosummary.py b/tests/test_extensions/test_ext_autosummary.py
index 81b13860278..5a96afdd3e3 100644
--- a/tests/test_extensions/test_ext_autosummary.py
+++ b/tests/test_extensions/test_ext_autosummary.py
@@ -192,9 +192,9 @@ def handler(app, what, name, obj, options, lines):
'C.C2': 'This is a nested inner class docstring',
}
for key, expected in expected_values.items():
- assert (
- autosummary_items[key][2] == expected
- ), f'Summary for {key} was {autosummary_items[key]!r} - expected {expected!r}'
+ assert autosummary_items[key][2] == expected, (
+ f'Summary for {key} was {autosummary_items[key]!r} - expected {expected!r}'
+ )
# check an item in detail
assert 'func' in autosummary_items
@@ -566,11 +566,7 @@ def test_autosummary_generate(app):
Foo = path.read_text(encoding='utf8')
assert '.. automethod:: __init__' in Foo
assert (
- ' .. autosummary::\n'
- ' \n'
- ' ~Foo.__init__\n'
- ' ~Foo.bar\n'
- ' \n'
+ ' .. autosummary::\n \n ~Foo.__init__\n ~Foo.bar\n \n'
) in Foo
assert (
' .. autosummary::\n'
@@ -591,9 +587,7 @@ def test_autosummary_generate(app):
path = app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.value.rst'
Foo_value = path.read_text(encoding='utf8')
assert (
- '.. currentmodule:: autosummary_dummy_module\n'
- '\n'
- '.. autoattribute:: Foo.value'
+ '.. currentmodule:: autosummary_dummy_module\n\n.. autoattribute:: Foo.value'
) in Foo_value
path = app.srcdir / 'generated' / 'autosummary_dummy_module.qux.rst'
@@ -820,17 +814,10 @@ def test_autosummary_module_all(app):
).read_text(encoding='utf8')
assert ' .. autosummary::\n \n PublicBar\n \n' in module
assert (
- ' .. autosummary::\n'
- ' \n'
- ' public_foo\n'
- ' public_baz\n'
- ' \n'
+ ' .. autosummary::\n \n public_foo\n public_baz\n \n'
) in module
assert (
- '.. autosummary::\n'
- ' :toctree:\n'
- ' :recursive:\n\n'
- ' extra_dummy_module\n'
+ '.. autosummary::\n :toctree:\n :recursive:\n\n extra_dummy_module\n'
) in module
finally:
sys.modules.pop('autosummary_dummy_package_all', None)
diff --git a/tests/test_extensions/test_ext_graphviz.py b/tests/test_extensions/test_ext_graphviz.py
index 929dcfa63a0..4be01caf023 100644
--- a/tests/test_extensions/test_ext_graphviz.py
+++ b/tests/test_extensions/test_ext_graphviz.py
@@ -168,12 +168,7 @@ def test_graphviz_parse_mapfile():
assert cmap.generate_clickable_map() == ''
# normal graph
- code = (
- 'digraph {\n'
- ' foo [href="https://www.google.com/"];\n'
- ' foo -> bar;\n'
- '}\n'
- )
+ code = 'digraph {\n foo [href="https://www.google.com/"];\n foo -> bar;\n}\n'
content = (
'
\n')
raise nodes.SkipNode
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index ed2a8c0936b..47dec78aa9f 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -33,10 +33,10 @@ class E(B): pass
import builtins
import hashlib
import inspect
+import os.path
import re
from collections.abc import Iterable, Sequence
from importlib import import_module
-from os import path
from typing import TYPE_CHECKING, Any, ClassVar, cast
from docutils import nodes
@@ -422,7 +422,7 @@ def html_visit_inheritance_diagram(self: HTML5Translator, node: inheritance_diag
# Create a mapping from fully-qualified class names to URLs.
graphviz_output_format = self.builder.env.config.graphviz_output_format.upper()
- current_filename = path.basename(self.builder.current_docname + self.builder.out_suffix)
+ current_filename = os.path.basename(self.builder.current_docname + self.builder.out_suffix)
urls = {}
pending_xrefs = cast(Iterable[addnodes.pending_xref], node)
for child in pending_xrefs:
diff --git a/sphinx/ext/intersphinx/_load.py b/sphinx/ext/intersphinx/_load.py
index dde2e34e1c2..eb8b46be807 100644
--- a/sphinx/ext/intersphinx/_load.py
+++ b/sphinx/ext/intersphinx/_load.py
@@ -3,10 +3,10 @@
from __future__ import annotations
import concurrent.futures
+import os.path
import posixpath
import time
from operator import itemgetter
-from os import path
from typing import TYPE_CHECKING
from urllib.parse import urlsplit, urlunsplit
@@ -303,7 +303,7 @@ def _fetch_inventory(
if '://' in inv_location:
f: _ReadableStream[bytes] = _read_from_url(inv_location, config=config)
else:
- f = open(path.join(srcdir, inv_location), 'rb') # NoQA: SIM115
+ f = open(os.path.join(srcdir, inv_location), 'rb') # NoQA: SIM115
except Exception as err:
err.args = (
'intersphinx inventory %r not fetchable due to %s: %s',
@@ -321,10 +321,10 @@ def _fetch_inventory(
if target_uri in {
inv_location,
- path.dirname(inv_location),
- path.dirname(inv_location) + '/',
+ os.path.dirname(inv_location),
+ os.path.dirname(inv_location) + '/',
}:
- target_uri = path.dirname(new_inv_location)
+ target_uri = os.path.dirname(new_inv_location)
with f:
try:
invdata = InventoryFile.load(f, target_uri, posixpath.join)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 91ba1f049ed..ebfe4322f84 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -3,10 +3,10 @@
from __future__ import annotations
import operator
+import os.path
import posixpath
import traceback
from importlib import import_module
-from os import path
from typing import TYPE_CHECKING, Any, cast
from docutils import nodes
@@ -229,7 +229,7 @@ def should_generate_module_page(app: Sphinx, modname: str) -> bool:
builder = cast(StandaloneHTMLBuilder, app.builder)
basename = modname.replace('.', '/') + builder.out_suffix
- page_filename = path.join(app.outdir, '_modules/', basename)
+ page_filename = os.path.join(app.outdir, '_modules/', basename)
try:
if _last_modified_time(module_filename) <= _last_modified_time(page_filename):
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 3621d417b94..909505fe5a2 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import os
-from os import path
+import os.path
from pprint import pformat
from typing import TYPE_CHECKING, Any
@@ -127,12 +127,12 @@ def get_source(
legacy_template = None
for searchpath in self.searchpath:
- filename = path.join(searchpath, template)
+ filename = os.path.join(searchpath, template)
f = open_if_exists(filename)
if f is not None:
break
if legacy_template is not None:
- filename = path.join(searchpath, legacy_template)
+ filename = os.path.join(searchpath, legacy_template)
f = open_if_exists(filename)
if f is not None:
break
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index 4094d7a9b8b..81f0519a5a1 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -3,9 +3,9 @@
from __future__ import annotations
import os
+import os.path
import tokenize
from importlib import import_module
-from os import path
from typing import TYPE_CHECKING, Any, Literal
from sphinx.errors import PycodeError
@@ -60,15 +60,15 @@ def get_module_source(modname: str) -> tuple[str | None, str | None]:
if filename is None:
# all methods for getting filename failed, so raise...
raise PycodeError('no source found for module %r' % modname)
- filename = path.normpath(path.abspath(filename))
+ filename = os.path.normpath(os.path.abspath(filename))
if filename.lower().endswith(('.pyo', '.pyc')):
filename = filename[:-1]
- if not path.isfile(filename) and path.isfile(filename + 'w'):
+ if not os.path.isfile(filename) and os.path.isfile(filename + 'w'):
filename += 'w'
elif not filename.lower().endswith(('.py', '.pyw')):
raise PycodeError('source is not a .py file: %r' % filename)
- if not path.isfile(filename):
+ if not os.path.isfile(filename):
raise PycodeError('source file is not present: %r' % filename)
return filename, None
diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py
index 2dd9fbc9280..4c464d5e260 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import contextlib
-from os import path
+import os.path
from re import DOTALL, match
from textwrap import indent
from typing import TYPE_CHECKING, Any, TypeVar
@@ -395,7 +395,7 @@ def apply(self, **kwargs: Any) -> None:
# fetch translations
dirs = [
- path.join(self.env.srcdir, directory)
+ os.path.join(self.env.srcdir, directory)
for directory in self.config.locale_dirs
]
catalog, has_catalog = init_locale(dirs, self.config.language, textdomain)
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index cd5619a729e..826696c83e3 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -3,9 +3,9 @@
from __future__ import annotations
import os
+import os.path
import re
from datetime import datetime
-from os import path
from typing import TYPE_CHECKING
import babel.dates
@@ -151,7 +151,7 @@ def pofiles(self) -> Iterator[tuple[_StrPath, _StrPath]]:
@property
def catalogs(self) -> Iterator[CatalogInfo]:
for basedir, filename in self.pofiles:
- domain = canon_path(path.splitext(filename)[0])
+ domain = canon_path(os.path.splitext(filename)[0])
yield CatalogInfo(basedir, domain, self.encoding)
@@ -290,15 +290,15 @@ def get_image_filename_for_language(
filename: str | os.PathLike[str],
env: BuildEnvironment,
) -> str:
- root, ext = path.splitext(filename)
- dirname = path.dirname(root)
- docpath = path.dirname(env.docname)
+ root, ext = os.path.splitext(filename)
+ dirname = os.path.dirname(root)
+ docpath = os.path.dirname(env.docname)
try:
return env.config.figure_language_filename.format(
root=root,
ext=ext,
path=dirname and dirname + SEP,
- basename=path.basename(root),
+ basename=os.path.basename(root),
docpath=docpath and docpath + SEP,
language=env.config.language,
)
@@ -310,7 +310,7 @@ def get_image_filename_for_language(
def search_image_for_language(filename: str, env: BuildEnvironment) -> str:
translated = get_image_filename_for_language(filename, env)
_, abspath = env.relfn2path(translated)
- if path.exists(abspath):
+ if os.path.exists(abspath):
return translated
else:
return filename
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 4a6a58698e4..634be6b6db8 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import base64
-from os import path
+import os.path
from typing import TYPE_CHECKING, NamedTuple, overload
import imagesize
@@ -68,10 +68,10 @@ def guess_mimetype(
filename: PathLike[str] | str = '',
default: str | None = None,
) -> str | None:
- ext = path.splitext(filename)[1].lower()
+ ext = os.path.splitext(filename)[1].lower()
if ext in mime_suffixes:
return mime_suffixes[ext]
- if path.exists(filename):
+ if os.path.exists(filename):
try:
imgtype = _image_type_from_file(filename)
except ValueError:
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 7ec14d446b2..009f8cc9b7a 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -5,12 +5,12 @@
import contextlib
import filecmp
import os
+import os.path
import re
import shutil
import sys
import unicodedata
from io import StringIO
-from os import path
from pathlib import Path
from typing import TYPE_CHECKING
@@ -28,12 +28,12 @@
def os_path(canonical_path: str, /) -> str:
- return canonical_path.replace(SEP, path.sep)
+ return canonical_path.replace(SEP, os.path.sep)
def canon_path(native_path: str | os.PathLike[str], /) -> str:
"""Return path in OS-independent form"""
- return os.fspath(native_path).replace(path.sep, SEP)
+ return os.fspath(native_path).replace(os.path.sep, SEP)
def path_stabilize(filepath: str | os.PathLike[str], /) -> str:
@@ -173,7 +173,7 @@ def relpath(
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
-abspath = path.abspath
+abspath = os.path.abspath
class FileAvoidWrite:
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index a9fbd0161c0..ec49bc721c7 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -6,10 +6,10 @@
from __future__ import annotations
+import os.path
import re
from collections import defaultdict
from collections.abc import Iterable
-from os import path
from typing import TYPE_CHECKING, Any, ClassVar, cast
from docutils import nodes, writers
@@ -583,12 +583,12 @@ def generate(
def render(self, template_name: str, variables: dict[str, Any]) -> str:
renderer = LaTeXRenderer(latex_engine=self.config.latex_engine)
for template_dir in self.config.templates_path:
- template = path.join(self.builder.confdir, template_dir, template_name)
- if path.exists(template):
+ template = os.path.join(self.builder.confdir, template_dir, template_name)
+ if os.path.exists(template):
return renderer.render(template, variables)
elif template.endswith('.jinja'):
legacy_template = template.removesuffix('.jinja') + '_t'
- if path.exists(legacy_template):
+ if os.path.exists(legacy_template):
logger.warning(
__('template %s not found; loading from legacy %s instead'),
template_name,
@@ -1648,7 +1648,7 @@ def visit_image(self, node: Element) -> None:
options = ''
if include_graphics_options:
options = '[%s]' % ','.join(include_graphics_options)
- base, ext = path.splitext(uri)
+ base, ext = os.path.splitext(uri)
if self.in_title and base:
# Lowercase tokens forcely because some fncychap themes capitalize
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 426b0288390..396e48bf91a 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -2,10 +2,10 @@
from __future__ import annotations
+import os.path
import re
import textwrap
from collections.abc import Iterable, Iterator
-from os import path
from typing import TYPE_CHECKING, Any, ClassVar, cast
from docutils import nodes, writers
@@ -1232,7 +1232,7 @@ def visit_image(self, node: Element) -> None:
if uri.find('://') != -1:
# ignore remote images
return
- name, ext = path.splitext(uri)
+ name, ext = os.path.splitext(uri)
# width and height ignored in non-tex output
width = self.tex_image_length(node.get('width', ''))
height = self.tex_image_length(node.get('height', ''))
From c1172022ed56ee44b0ad023fd4056ce55cd9d6bf Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sun, 3 Nov 2024 22:49:44 +0000
Subject: [PATCH 22/39] Prefer using ``BuildEnvironment.note_dependency()``
---
sphinx/environment/__init__.py | 10 +++++++---
sphinx/environment/collectors/asset.py | 4 ++--
sphinx/environment/collectors/dependencies.py | 2 +-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 68d0249b151..37a18c35172 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -467,7 +467,7 @@ def find_files(self, config: Config, builder: Builder) -> None:
for docname in self.found_docs:
domain = docname_to_domain(docname, self.config.gettext_compact)
if domain in mo_paths:
- self.dependencies[docname].add(str(mo_paths[domain]))
+ self.note_dependency(mo_paths[domain], docname=docname)
except OSError as exc:
raise DocumentError(
__('Failed to scan documents in %s: %r') % (self.srcdir, exc)
@@ -585,14 +585,18 @@ def new_serialno(self, category: str = '') -> int:
self.temp_data[key] = cur + 1
return cur
- def note_dependency(self, filename: str) -> None:
+ def note_dependency(
+ self, filename: str | os.PathLike[str], *, docname: str | None = None
+ ) -> None:
"""Add *filename* as a dependency of the current document.
This means that the document will be rebuilt if this file changes.
*filename* should be absolute or relative to the source directory.
"""
- self.dependencies[self.docname].add(filename)
+ if docname is None:
+ docname = self.docname
+ self.dependencies[docname].add(os.fspath(filename))
def note_included(self, filename: str) -> None:
"""Add *filename* as a included from other document.
diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py
index 838391aa7da..e7d976682b0 100644
--- a/sphinx/environment/collectors/asset.py
+++ b/sphinx/environment/collectors/asset.py
@@ -89,7 +89,7 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# map image paths to unique image names (so that they can be put
# into a single directory)
for imgpath in candidates.values():
- app.env.dependencies[docname].add(imgpath)
+ app.env.note_dependency(imgpath)
if not os.access(os.path.join(app.srcdir, imgpath), os.R_OK):
logger.warning(
__('image file not readable: %s'),
@@ -154,7 +154,7 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
node['refuri'] = targetname
else:
rel_filename, filename = app.env.relfn2path(targetname, app.env.docname)
- app.env.dependencies[app.env.docname].add(rel_filename)
+ app.env.note_dependency(rel_filename)
if not os.access(filename, os.R_OK):
logger.warning(
__('download file not readable: %s'),
diff --git a/sphinx/environment/collectors/dependencies.py b/sphinx/environment/collectors/dependencies.py
index 1173dc3b24a..30701314be3 100644
--- a/sphinx/environment/collectors/dependencies.py
+++ b/sphinx/environment/collectors/dependencies.py
@@ -49,7 +49,7 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
if isinstance(dep, bytes):
dep = dep.decode(fs_encoding)
relpath = relative_path(frompath, os.path.normpath(os.path.join(cwd, dep)))
- app.env.dependencies[app.env.docname].add(relpath)
+ app.env.note_dependency(relpath)
def setup(app: Sphinx) -> ExtensionMetadata:
From fbb23071bff27c0ea408e4d9c9d3409a930e4b3d Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sun, 3 Nov 2024 22:52:39 +0000
Subject: [PATCH 23/39] Accept PathLike in ``BuildEnvironment.note_included()``
---
sphinx/environment/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 37a18c35172..e63e6c3833d 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -598,7 +598,7 @@ def note_dependency(
docname = self.docname
self.dependencies[docname].add(os.fspath(filename))
- def note_included(self, filename: str) -> None:
+ def note_included(self, filename: str | os.PathLike[str]) -> None:
"""Add *filename* as a included from other document.
This means the document is not orphaned.
From 1266c421be03dc9a5cbcf69a7c292eb71d21740d Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Sun, 3 Nov 2024 22:54:00 +0000
Subject: [PATCH 24/39] Use pathlib in ``sys.path`` manipulation examples
---
doc/development/tutorials/extending_build.rst | 4 ++--
doc/development/tutorials/extending_syntax.rst | 4 ++--
doc/usage/configuration.rst | 7 +++++--
doc/usage/extensions/index.rst | 9 ++++++---
4 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/doc/development/tutorials/extending_build.rst b/doc/development/tutorials/extending_build.rst
index a81c84b0075..4d3606a0a33 100644
--- a/doc/development/tutorials/extending_build.rst
+++ b/doc/development/tutorials/extending_build.rst
@@ -313,10 +313,10 @@ For example:
.. code-block:: python
- import os
import sys
+ from pathlib import Path
- sys.path.append(os.path.abspath("./_ext"))
+ sys.path.append(str(Path('_ext').resolve()))
extensions = ['todo']
diff --git a/doc/development/tutorials/extending_syntax.rst b/doc/development/tutorials/extending_syntax.rst
index bab80371703..a8a5bfe62ec 100644
--- a/doc/development/tutorials/extending_syntax.rst
+++ b/doc/development/tutorials/extending_syntax.rst
@@ -169,10 +169,10 @@ For example:
.. code-block:: python
- import os
import sys
+ from pathlib import Path
- sys.path.append(os.path.abspath("./_ext"))
+ sys.path.append(str(Path('_ext').resolve()))
extensions = ['helloworld']
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 9768d248cce..40fe5931250 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -223,11 +223,14 @@ General configuration
Ensure that absolute paths are used when modifying :data:`sys.path`.
If your custom extensions live in a directory that is relative to the
- :term:`configuration directory`, use :func:`os.path.abspath` like so:
+ :term:`configuration directory`, use :meth:`pathlib.Path.resolve` like so:
.. code-block:: python
- import os, sys; sys.path.append(os.path.abspath('sphinxext'))
+ import sys
+ from pathlib import Path
+
+ sys.path.append(str(Path('sphinxext').resolve()))
extensions = [
...
diff --git a/doc/usage/extensions/index.rst b/doc/usage/extensions/index.rst
index 929f2b604b2..4be426c3fe2 100644
--- a/doc/usage/extensions/index.rst
+++ b/doc/usage/extensions/index.rst
@@ -69,11 +69,14 @@ Where to put your own extensions?
Extensions local to a project should be put within the project's directory
structure. Set Python's module search path, ``sys.path``, accordingly so that
Sphinx can find them. For example, if your extension ``foo.py`` lies in the
-``exts`` subdirectory of the project root, put into :file:`conf.py`::
+``exts`` subdirectory of the project root, put into :file:`conf.py`:
- import sys, os
+.. code-block:: python
- sys.path.append(os.path.abspath('exts'))
+ import sys
+ from pathlib import Path
+
+ sys.path.append(str(Path('exts').resolve()))
extensions = ['foo']
From 2ccbc3209ab8ead125a27dadaac26055b117fb73 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 4 Nov 2024 17:26:05 +0000
Subject: [PATCH 25/39] Use type hints for directories
---
sphinx/builders/__init__.py | 9 +++++----
sphinx/environment/__init__.py | 5 ++---
sphinx/writers/html5.py | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index f1733184243..1420417380d 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -48,6 +48,7 @@
from sphinx.application import Sphinx
from sphinx.config import Config
from sphinx.events import EventManager
+ from sphinx.util._pathlib import _StrPath
from sphinx.util.tags import Tags
@@ -94,10 +95,10 @@ class Builder:
supported_data_uri_images: bool = False
def __init__(self, app: Sphinx, env: BuildEnvironment) -> None:
- self.srcdir = app.srcdir
- self.confdir = app.confdir
- self.outdir = app.outdir
- self.doctreedir = app.doctreedir
+ self.srcdir: _StrPath = app.srcdir
+ self.confdir: _StrPath = app.confdir
+ self.outdir: _StrPath = app.outdir
+ self.doctreedir: _StrPath = app.doctreedir
ensuredir(self.doctreedir)
self.app: Sphinx = app
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index e63e6c3833d..397b3667e10 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -32,7 +32,6 @@
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator
- from pathlib import Path
from typing import Any, Literal
from docutils import nodes
@@ -102,8 +101,8 @@ class BuildEnvironment:
def __init__(self, app: Sphinx) -> None:
self.app: Sphinx = app
- self.doctreedir: Path = app.doctreedir
- self.srcdir: Path = app.srcdir
+ self.doctreedir: _StrPath = app.doctreedir
+ self.srcdir: _StrPath = app.srcdir
self.config: Config = None # type: ignore[assignment]
self.config_status: int = CONFIG_UNSET
self.config_status_extra: str = ''
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index b6e342ac119..0ca97c5f361 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -752,7 +752,7 @@ def visit_image(self, node: Element) -> None:
# but it tries the final file name, which does not necessarily exist
# yet at the time the HTML file is written.
if not ('width' in node and 'height' in node):
- path = os.path.join(self.builder.srcdir, olduri) # type: ignore[has-type]
+ path = os.path.join(self.builder.srcdir, olduri)
size = get_image_size(path)
if size is None:
logger.warning(
From 6f0c8f75ac164d5d2cc02bba2d63560857f09d68 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 4 Nov 2024 17:28:30 +0000
Subject: [PATCH 26/39] Improve lookup in ``Project.path2doc()``
---
sphinx/project.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sphinx/project.py b/sphinx/project.py
index 01642999306..74499086034 100644
--- a/sphinx/project.py
+++ b/sphinx/project.py
@@ -96,10 +96,10 @@ def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
*filename* should be absolute or relative to the source directory.
"""
+ path = Path(filename)
try:
- return self._path_to_docname[filename] # type: ignore[index]
+ return self._path_to_docname[path]
except KeyError:
- path = Path(filename)
if path.is_absolute():
with contextlib.suppress(ValueError):
path = path.relative_to(self.srcdir)
From e83432792514998c9bca316319765f3856d6901d Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 4 Nov 2024 18:10:11 +0000
Subject: [PATCH 27/39] Use ``_StrPath`` in ``sphinx.util.images``
---
sphinx/util/images.py | 10 ++++++----
sphinx/writers/html5.py | 4 +---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 634be6b6db8..f1e7344eb7a 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import base64
-import os.path
+from pathlib import Path
from typing import TYPE_CHECKING, NamedTuple, overload
import imagesize
@@ -37,7 +37,8 @@ class DataURI(NamedTuple):
data: bytes
-def get_image_size(filename: str) -> tuple[int, int] | None:
+def get_image_size(filename: str | PathLike[str]) -> tuple[int, int] | None:
+ filename = Path(filename)
try:
size = imagesize.get(filename)
if size[0] == -1:
@@ -68,10 +69,11 @@ def guess_mimetype(
filename: PathLike[str] | str = '',
default: str | None = None,
) -> str | None:
- ext = os.path.splitext(filename)[1].lower()
+ filename = Path(filename)
+ ext = filename.suffix.lower()
if ext in mime_suffixes:
return mime_suffixes[ext]
- if os.path.exists(filename):
+ if filename.exists():
try:
imgtype = _image_type_from_file(filename)
except ValueError:
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 0ca97c5f361..608a84f34db 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -2,7 +2,6 @@
from __future__ import annotations
-import os
import posixpath
import re
import urllib.parse
@@ -752,8 +751,7 @@ def visit_image(self, node: Element) -> None:
# but it tries the final file name, which does not necessarily exist
# yet at the time the HTML file is written.
if not ('width' in node and 'height' in node):
- path = os.path.join(self.builder.srcdir, olduri)
- size = get_image_size(path)
+ size = get_image_size(self.builder.srcdir / olduri)
if size is None:
logger.warning(
__('Could not obtain image size. :scale: option is ignored.'),
From 230ccf2a44d750f5d6c5542f970f48ffe0cbce1e Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 4 Nov 2024 21:14:30 +0000
Subject: [PATCH 28/39] Use ``_StrPath`` in ``sphinx.environment``
---
sphinx/environment/__init__.py | 35 ++++++++++++++++------------------
sphinx/util/osutil.py | 16 ++++++++++++++++
2 files changed, 32 insertions(+), 19 deletions(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 397b3667e10..b17c1ee453b 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -4,7 +4,6 @@
import functools
import os
-import os.path
import pickle
from collections import defaultdict
from copy import copy
@@ -28,7 +27,7 @@
from sphinx.util.docutils import LoggingReporter
from sphinx.util.i18n import CatalogRepository, docname_to_domain
from sphinx.util.nodes import is_translatable
-from sphinx.util.osutil import _last_modified_time, canon_path, os_path
+from sphinx.util.osutil import _last_modified_time, _relative_path, canon_path
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator
@@ -419,17 +418,15 @@ def relfn2path(self, filename: str, docname: str | None = None) -> tuple[str, st
source dir, while relative filenames are relative to the dir of the
containing document.
"""
- filename = os_path(filename)
- if filename.startswith(('/', os.sep)):
- rel_fn = filename[1:]
+ filename = canon_path(filename)
+ if filename.startswith('/'):
+ abs_fn = (self.srcdir / filename[1:]).resolve()
else:
- docdir = os.path.dirname(self.doc2path(docname or self.docname, base=False))
- rel_fn = os.path.join(docdir, filename)
+ doc_dir = self.doc2path(docname or self.docname, base=False).parent
+ abs_fn = (self.srcdir / doc_dir / filename).resolve()
- return (
- canon_path(os.path.normpath(rel_fn)),
- os.path.normpath(os.path.join(self.srcdir, rel_fn)),
- )
+ rel_fn = _relative_path(abs_fn, self.srcdir)
+ return canon_path(rel_fn), os.fspath(abs_fn)
@property
def found_docs(self) -> set[str]:
@@ -492,8 +489,8 @@ def get_outdated_files(
added.add(docname)
continue
# if the doctree file is not there, rebuild
- filename = os.path.join(self.doctreedir, docname + '.doctree')
- if not os.path.isfile(filename):
+ filename = self.doctreedir / f'{docname}.doctree'
+ if not filename.is_file():
logger.debug('[build target] changed %r', docname)
changed.add(docname)
continue
@@ -518,21 +515,21 @@ def get_outdated_files(
for dep in self.dependencies[docname]:
try:
# this will do the right thing when dep is absolute too
- deppath = os.path.join(self.srcdir, dep)
- if not os.path.isfile(deppath):
+ dep_path = self.srcdir / dep
+ if not dep_path.is_file():
logger.debug(
'[build target] changed %r missing dependency %r',
docname,
- deppath,
+ dep_path,
)
changed.add(docname)
break
- depmtime = _last_modified_time(deppath)
+ depmtime = _last_modified_time(dep_path)
if depmtime > mtime:
logger.debug(
'[build target] outdated %r from dependency %r: %s -> %s',
docname,
- deppath,
+ dep_path,
_format_rfc3339_microseconds(mtime),
_format_rfc3339_microseconds(depmtime),
)
@@ -632,7 +629,7 @@ def get_doctree(self, docname: str) -> nodes.document:
try:
serialised = self._pickled_doctree_cache[docname]
except KeyError:
- filename = os.path.join(self.doctreedir, docname + '.doctree')
+ filename = self.doctreedir / f'{docname}.doctree'
with open(filename, 'rb') as f:
serialised = self._pickled_doctree_cache[docname] = f.read()
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 009f8cc9b7a..779727b394b 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -169,6 +169,22 @@ def relpath(
return str(path)
+def _relative_path(path: Path, root: Path, /) -> Path:
+ """Return a relative filepath to *path* from the given *root* directory.
+
+ This is an alternative of ``Path.relative_to``.
+ It returns the original path if *path* and *root* are on different drives,
+ which may happen on Windows.
+ """
+ if path.anchor != root.anchor or '..' in root.parts:
+ # If the drives are different, no relative path exists.
+ # Path.relative_to() requires fully-resolved paths (no '..').
+ return path
+ if sys.version_info[:2] < (3, 12):
+ return Path(os.path.relpath(path, root))
+ return path.relative_to(root, walk_up=True)
+
+
safe_relpath = relpath # for compatibility
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
From d374a1c30d348c95a89b07810fc6bd4a494783e5 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Mon, 4 Nov 2024 22:30:09 +0000
Subject: [PATCH 29/39] Use pathlib version of ``relative_path``
---
sphinx/builders/html/__init__.py | 4 ++--
sphinx/environment/collectors/asset.py | 7 ++++---
sphinx/environment/collectors/dependencies.py | 12 ++++--------
sphinx/ext/intersphinx/_resolve.py | 6 +++---
sphinx/util/fileutil.py | 11 +++++------
5 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index 0dd25fbb644..9a1001fceaf 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -20,7 +20,6 @@
from docutils.core import Publisher
from docutils.frontend import OptionParser
from docutils.io import DocTreeInput, StringOutput
-from docutils.utils import relative_path
from sphinx import __display_version__, package_dir
from sphinx import version_info as sphinx_version
@@ -56,6 +55,7 @@
from sphinx.util.osutil import (
SEP,
_last_modified_time,
+ _relative_path,
copyfile,
ensuredir,
relative_uri,
@@ -795,7 +795,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(Path(f), self.srcdir).as_posix()
# copy downloadable files
if self.env.dlfiles:
diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py
index e7d976682b0..44f72294520 100644
--- a/sphinx/environment/collectors/asset.py
+++ b/sphinx/environment/collectors/asset.py
@@ -5,10 +5,10 @@
import os
import os.path
from glob import glob
+from pathlib import Path
from typing import TYPE_CHECKING
from docutils import nodes
-from docutils.utils import relative_path
from sphinx import addnodes
from sphinx.environment.collectors import EnvironmentCollector
@@ -16,6 +16,7 @@
from sphinx.util import logging
from sphinx.util.i18n import get_image_filename_for_language, search_image_for_language
from sphinx.util.images import guess_mimetype
+from sphinx.util.osutil import _relative_path
if TYPE_CHECKING:
from docutils.nodes import Node
@@ -110,14 +111,14 @@ def collect_candidates(
) -> None:
globbed: dict[str, list[str]] = {}
for filename in glob(imgpath):
- new_imgpath = relative_path(os.path.join(env.srcdir, 'dummy'), filename)
+ new_imgpath = _relative_path(Path(filename), env.srcdir)
try:
mimetype = guess_mimetype(filename)
if mimetype is None:
basename, suffix = os.path.splitext(filename)
mimetype = 'image/x-' + suffix[1:]
if mimetype not in candidates:
- globbed.setdefault(mimetype, []).append(new_imgpath)
+ globbed.setdefault(mimetype, []).append(new_imgpath.as_posix())
except OSError as err:
logger.warning(
__('image file %s not readable: %s'),
diff --git a/sphinx/environment/collectors/dependencies.py b/sphinx/environment/collectors/dependencies.py
index 30701314be3..d77731218b1 100644
--- a/sphinx/environment/collectors/dependencies.py
+++ b/sphinx/environment/collectors/dependencies.py
@@ -2,14 +2,11 @@
from __future__ import annotations
-import os
-import os.path
+from pathlib import Path
from typing import TYPE_CHECKING
-from docutils.utils import relative_path
-
from sphinx.environment.collectors import EnvironmentCollector
-from sphinx.util.osutil import fs_encoding
+from sphinx.util.osutil import _relative_path, fs_encoding
if TYPE_CHECKING:
from docutils import nodes
@@ -38,8 +35,7 @@ def merge_other(
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process docutils-generated dependency info."""
- cwd = os.getcwd()
- frompath = os.path.join(os.path.normpath(app.srcdir), 'dummy')
+ cwd = Path.cwd()
deps = doctree.settings.record_dependencies
if not deps:
return
@@ -48,7 +44,7 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# one relative to the srcdir
if isinstance(dep, bytes):
dep = dep.decode(fs_encoding)
- relpath = relative_path(frompath, os.path.normpath(os.path.join(cwd, dep)))
+ relpath = _relative_path(cwd / dep, app.srcdir)
app.env.note_dependency(relpath)
diff --git a/sphinx/ext/intersphinx/_resolve.py b/sphinx/ext/intersphinx/_resolve.py
index be279b8c350..0dbab63dc69 100644
--- a/sphinx/ext/intersphinx/_resolve.py
+++ b/sphinx/ext/intersphinx/_resolve.py
@@ -2,12 +2,11 @@
from __future__ import annotations
-import posixpath
import re
+from pathlib import Path
from typing import TYPE_CHECKING, cast
from docutils import nodes
-from docutils.utils import relative_path
from sphinx.addnodes import pending_xref
from sphinx.deprecation import _deprecation_warning
@@ -16,6 +15,7 @@
from sphinx.locale import _, __
from sphinx.transforms.post_transforms import ReferencesResolver
from sphinx.util.docutils import CustomReSTDispatcher, SphinxRole
+from sphinx.util.osutil import _relative_path
if TYPE_CHECKING:
from collections.abc import Iterable
@@ -42,7 +42,7 @@ def _create_element_from_result(
proj, version, uri, dispname = data
if '://' not in uri and node.get('refdoc'):
# get correct path in case of subdirectories
- uri = posixpath.join(relative_path(node['refdoc'], '.'), uri)
+ uri = (_relative_path(Path(), Path(node['refdoc']).parent) / uri).as_posix()
if version:
reftitle = _('(in %s v%s)') % (proj, version)
else:
diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py
index acd52b07674..d5e2e2692d5 100644
--- a/sphinx/util/fileutil.py
+++ b/sphinx/util/fileutil.py
@@ -7,11 +7,9 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any
-from docutils.utils import relative_path
-
from sphinx.locale import __
from sphinx.util import logging
-from sphinx.util.osutil import copyfile, ensuredir
+from sphinx.util.osutil import _relative_path, copyfile, ensuredir
if TYPE_CHECKING:
from collections.abc import Callable
@@ -125,7 +123,8 @@ def copy_asset(
:param onerror: The error handler.
:param bool force: Overwrite the destination file even if it exists.
"""
- if not os.path.exists(source):
+ source = Path(source)
+ if not source.exists():
return
if renderer is None:
@@ -134,14 +133,14 @@ def copy_asset(
renderer = SphinxRenderer()
ensuredir(destination)
- if os.path.isfile(source):
+ if source.is_file():
copy_asset_file(
source, destination, context=context, renderer=renderer, force=force
)
return
for root, dirs, files in os.walk(source, followlinks=True):
- reldir = relative_path(source, root)
+ reldir = _relative_path(Path(root), source).as_posix()
for dir in dirs.copy():
if excluded(posixpath.join(reldir, dir)):
dirs.remove(dir)
From 5b1dc828ca988dea07c0e47c725d95d59acb17ac Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Tue, 5 Nov 2024 14:48:24 +0000
Subject: [PATCH 30/39] Import abspath from os.path
---
sphinx/util/logging.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index 758a48f8b0e..20293c8fabe 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -6,6 +6,7 @@
import logging.handlers
from collections import defaultdict
from contextlib import contextmanager, nullcontext
+from os.path import abspath
from typing import IO, TYPE_CHECKING, Any
from docutils import nodes
@@ -13,7 +14,6 @@
from sphinx.errors import SphinxWarning
from sphinx.util.console import colorize
-from sphinx.util.osutil import abspath
if TYPE_CHECKING:
from collections.abc import Iterator, Sequence, Set
From e8db4379d4613e14e057b12fb5cb50dcbe3518d5 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Tue, 5 Nov 2024 14:49:24 +0000
Subject: [PATCH 31/39] Use pathlib in ``sphinx.transforms.i18n``
---
sphinx/transforms/i18n.py | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py
index 4c464d5e260..31542e81e5f 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -3,7 +3,6 @@
from __future__ import annotations
import contextlib
-import os.path
from re import DOTALL, match
from textwrap import indent
from typing import TYPE_CHECKING, Any, TypeVar
@@ -394,10 +393,8 @@ def apply(self, **kwargs: Any) -> None:
textdomain = docname_to_domain(self.env.docname, self.config.gettext_compact)
# fetch translations
- dirs = [
- os.path.join(self.env.srcdir, directory)
- for directory in self.config.locale_dirs
- ]
+ srcdir = self.env.srcdir
+ dirs = [srcdir / directory for directory in self.config.locale_dirs]
catalog, has_catalog = init_locale(dirs, self.config.language, textdomain)
if not has_catalog:
return
From bb6ecbc251a5aa6d5b5dac9f551dd0bc2f787ea3 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Tue, 5 Nov 2024 15:01:31 +0000
Subject: [PATCH 32/39] Use ``_StrPath`` in ``sphinx.writers.latex``
---
sphinx/writers/latex.py | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index ec49bc721c7..2dffd7e3f15 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -6,10 +6,10 @@
from __future__ import annotations
-import os.path
import re
from collections import defaultdict
from collections.abc import Iterable
+from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, cast
from docutils import nodes, writers
@@ -583,18 +583,19 @@ def generate(
def render(self, template_name: str, variables: dict[str, Any]) -> str:
renderer = LaTeXRenderer(latex_engine=self.config.latex_engine)
for template_dir in self.config.templates_path:
- template = os.path.join(self.builder.confdir, template_dir, template_name)
- if os.path.exists(template):
- return renderer.render(template, variables)
- elif template.endswith('.jinja'):
- legacy_template = template.removesuffix('.jinja') + '_t'
- if os.path.exists(legacy_template):
+ template = self.builder.confdir / template_dir / template_name
+ if template.exists():
+ return renderer.render(str(template), variables)
+ elif template.suffix == '.jinja':
+ legacy_template_name = template.name.removesuffix('.jinja') + '_t'
+ legacy_template = template.with_name(legacy_template_name)
+ if legacy_template.exists():
logger.warning(
__('template %s not found; loading from legacy %s instead'),
template_name,
legacy_template,
)
- return renderer.render(legacy_template, variables)
+ return renderer.render(str(legacy_template), variables)
return renderer.render(template_name, variables)
@@ -1648,7 +1649,9 @@ def visit_image(self, node: Element) -> None:
options = ''
if include_graphics_options:
options = '[%s]' % ','.join(include_graphics_options)
- base, ext = os.path.splitext(uri)
+ img_path = Path(uri)
+ base = img_path.with_suffix('')
+ ext = img_path.suffix
if self.in_title and base:
# Lowercase tokens forcely because some fncychap themes capitalize
@@ -1657,8 +1660,8 @@ def visit_image(self, node: Element) -> None:
else:
cmd = rf'\sphinxincludegraphics{options}{{{{{base}}}{ext}}}'
# escape filepath for includegraphics, https://tex.stackexchange.com/a/202714/41112
- if '#' in base:
- cmd = r'{\catcode`\#=12' + cmd + '}'
+ if '#' in str(base):
+ cmd = rf'{{\catcode`\#=12{cmd}}}'
self.body.append(cmd)
self.body.extend(post)
From c4495d0ce06a1c4b894317ea0277decd38127246 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Thu, 7 Nov 2024 00:31:28 +0000
Subject: [PATCH 33/39] Use ``_StrPath`` in ``sphinx.pycode``
---
sphinx/ext/viewcode.py | 3 ++-
sphinx/pycode/__init__.py | 38 +++++++++++++++++++++-----------------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index ebfe4322f84..b64d67bdad3 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -29,6 +29,7 @@
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
+ from sphinx.util._pathlib import _StrPath
from sphinx.util.typing import ExtensionMetadata
logger = logging.getLogger(__name__)
@@ -207,7 +208,7 @@ def remove_viewcode_anchors(self) -> None:
node.parent.remove(node)
-def get_module_filename(app: Sphinx, modname: str) -> str | None:
+def get_module_filename(app: Sphinx, modname: str) -> _StrPath | None:
"""Get module filename for *modname*."""
source_info = app.emit_firstresult('viewcode-find-source', modname)
if source_info:
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index 81f0519a5a1..0242cf9c8be 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-import os
-import os.path
import tokenize
from importlib import import_module
from typing import TYPE_CHECKING, Any, Literal
@@ -13,6 +11,7 @@
from sphinx.util._pathlib import _StrPath
if TYPE_CHECKING:
+ import os
from inspect import Signature
@@ -28,7 +27,7 @@ class ModuleAnalyzer:
cache: dict[tuple[Literal['file', 'module'], str | _StrPath], Any] = {}
@staticmethod
- def get_module_source(modname: str) -> tuple[str | None, str | None]:
+ def get_module_source(modname: str) -> tuple[_StrPath | None, str | None]:
"""Try to find the source code for a module.
Returns ('filename', 'source'). One of it can be None if
@@ -39,14 +38,15 @@ def get_module_source(modname: str) -> tuple[str | None, str | None]:
except Exception as err:
raise PycodeError('error importing %r' % modname, err) from err
loader = getattr(mod, '__loader__', None)
- filename = getattr(mod, '__file__', None)
+ filename: str | None = getattr(mod, '__file__', None)
if loader and getattr(loader, 'get_source', None):
# prefer Native loader, as it respects #coding directive
try:
source = loader.get_source(modname)
if source:
+ mod_path = None if filename is None else _StrPath(filename)
# no exception and not None - it must be module source
- return filename, source
+ return mod_path, source
except ImportError:
pass # Try other "source-mining" methods
if filename is None and loader and getattr(loader, 'get_filename', None):
@@ -60,24 +60,28 @@ def get_module_source(modname: str) -> tuple[str | None, str | None]:
if filename is None:
# all methods for getting filename failed, so raise...
raise PycodeError('no source found for module %r' % modname)
- filename = os.path.normpath(os.path.abspath(filename))
- if filename.lower().endswith(('.pyo', '.pyc')):
- filename = filename[:-1]
- if not os.path.isfile(filename) and os.path.isfile(filename + 'w'):
- filename += 'w'
- elif not filename.lower().endswith(('.py', '.pyw')):
- raise PycodeError('source is not a .py file: %r' % filename)
-
- if not os.path.isfile(filename):
- raise PycodeError('source file is not present: %r' % filename)
- return filename, None
+ mod_path = _StrPath(filename).resolve()
+ if mod_path.suffix in {'.pyo', '.pyc'}:
+ mod_path_pyw = mod_path.with_suffix('.pyw')
+ if not mod_path.is_file() and mod_path_pyw.is_file():
+ mod_path = mod_path_pyw
+ else:
+ mod_path = mod_path.with_suffix('.py')
+ elif mod_path.suffix not in {'.py', '.pyw'}:
+ msg = f'source is not a .py file: {mod_path!r}'
+ raise PycodeError(msg)
+
+ if not mod_path.is_file():
+ msg = f'source file is not present: {mod_path!r}'
+ raise PycodeError(msg)
+ return mod_path, None
@classmethod
def for_string(
cls: type[ModuleAnalyzer],
string: str,
modname: str,
- srcname: str = '',
+ srcname: str | os.PathLike[str] = '',
) -> ModuleAnalyzer:
return cls(string, modname, srcname)
From c6b9dc16b4be3aaa868aed974543eda8ac98f1bb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 7 Nov 2024 16:13:40 +0000
Subject: [PATCH 34/39] Bump pyright to 1.1.388 (#13111)
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 4eb5269ed2e..ac079b0766b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -91,7 +91,7 @@ lint = [
"types-Pygments==2.18.0.20240506",
"types-requests==2.32.0.20241016", # align with requests
"types-urllib3==1.26.25.14",
- "pyright==1.1.387",
+ "pyright==1.1.388",
"pytest>=6.0",
]
test = [
From 8117bcaae8ccd75754bb6c1fabf060a73b32d000 Mon Sep 17 00:00:00 2001
From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Date: Thu, 7 Nov 2024 17:25:40 +0100
Subject: [PATCH 35/39] Format autosummary options in docs (#13106)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
doc/usage/extensions/autosummary.rst | 60 +++++++++++++++-------------
1 file changed, 32 insertions(+), 28 deletions(-)
diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst
index 0a25d8dbd21..0b2b0c69cf8 100644
--- a/doc/usage/extensions/autosummary.rst
+++ b/doc/usage/extensions/autosummary.rst
@@ -61,10 +61,12 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
:event:`autodoc-process-docstring` and :event:`autodoc-process-signature`
hooks as :mod:`~sphinx.ext.autodoc`.
- **Options**
+ .. rubric:: Options
- * If you want the :rst:dir:`autosummary` table to also serve as a
- :rst:dir:`toctree` entry, use the ``toctree`` option, for example::
+ .. rst:directive:option:: toctree: optional directory name
+
+ If you want the :rst:dir:`autosummary` table to also serve as a
+ :rst:dir:`toctree` entry, use the ``toctree`` option, for example::
.. autosummary::
:toctree: DIRNAME
@@ -72,52 +74,54 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
sphinx.environment.BuildEnvironment
sphinx.util.relative_uri
- The ``toctree`` option also signals to the :program:`sphinx-autogen` script
- that stub pages should be generated for the entries listed in this
- directive. The option accepts a directory name as an argument;
- :program:`sphinx-autogen` will by default place its output in this
- directory. If no argument is given, output is placed in the same directory
- as the file that contains the directive.
+ The ``toctree`` option also signals to the :program:`sphinx-autogen` script
+ that stub pages should be generated for the entries listed in this
+ directive. The option accepts a directory name as an argument;
+ :program:`sphinx-autogen` will by default place its output in this
+ directory. If no argument is given, output is placed in the same directory
+ as the file that contains the directive.
- You can also use ``caption`` option to give a caption to the toctree.
+ .. versionadded:: 0.6
- .. versionadded:: 3.1
+ .. rst:directive:option:: caption: caption of ToC
- caption option added.
+ Add a caption to the toctree.
- * If you don't want the :rst:dir:`autosummary` to show function signatures in
- the listing, include the ``nosignatures`` option::
+ .. versionadded:: 3.1
- .. autosummary::
- :nosignatures:
+ .. rst:directive:option:: nosignatures
- sphinx.environment.BuildEnvironment
- sphinx.util.relative_uri
+ Do not show function signatures in the summary.
- * You can specify a custom template with the ``template`` option.
- For example, ::
+ .. versionadded:: 0.6
+
+ .. rst:directive:option:: template: filename
+
+ Specify a custom template for rendering the summary.
+ For example, ::
.. autosummary::
:template: mytemplate.rst
sphinx.environment.BuildEnvironment
- would use the template :file:`mytemplate.rst` in your
- :confval:`templates_path` to generate the pages for all entries
- listed. See `Customizing templates`_ below.
+ would use the template :file:`mytemplate.rst` in your
+ :confval:`templates_path` to generate the pages for all entries
+ listed. See `Customizing templates`_ below.
+
+ .. versionadded:: 1.0
- .. versionadded:: 1.0
+ .. rst:directive:option:: recursive
- * You can specify the ``recursive`` option to generate documents for
- modules and sub-packages recursively. It defaults to disabled.
- For example, ::
+ Generate documents for modules and sub-packages recursively.
+ For example, ::
.. autosummary::
:recursive:
sphinx.environment.BuildEnvironment
- .. versionadded:: 3.1
+ .. versionadded:: 3.1
:program:`sphinx-autogen` -- generate autodoc stub pages
From c427edb0bddba4366e71cd9dca7e70faad18346a Mon Sep 17 00:00:00 2001
From: Dimitri Papadopoulos Orfanos
<3234522+DimitriPapadopoulos@users.noreply.github.com>
Date: Fri, 8 Nov 2024 13:11:42 +0100
Subject: [PATCH 36/39] Emend spelling errors (#13113)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
---
sphinx/texinputs/sphinxlatexadmonitions.sty | 2 +-
sphinx/util/_pathlib.py | 2 +-
tests/test_extensions/test_ext_intersphinx_cache.py | 2 +-
tests/test_intl/test_intl.py | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/sphinx/texinputs/sphinxlatexadmonitions.sty b/sphinx/texinputs/sphinxlatexadmonitions.sty
index 76fef5a8c4f..0519903591b 100644
--- a/sphinx/texinputs/sphinxlatexadmonitions.sty
+++ b/sphinx/texinputs/sphinxlatexadmonitions.sty
@@ -32,7 +32,7 @@
% sphinxlatexshadowbox.sty, and handles both "with icon" and "without
% icon" situations).
%
-% The sphinxlightbox environment is kept for backward compatiblity, for user
+% The sphinxlightbox environment is kept for backward compatibility, for user
% custom code which used it via custom definitions done in preamble or via
% raw latex directive.
% MEMO: here is for example how sphinxnote was formerly defined:
diff --git a/sphinx/util/_pathlib.py b/sphinx/util/_pathlib.py
index b44fec30285..31b47ce5a67 100644
--- a/sphinx/util/_pathlib.py
+++ b/sphinx/util/_pathlib.py
@@ -3,7 +3,7 @@
Instances of _StrPath should not be constructed except in Sphinx itself.
Consumers of Sphinx APIs should prefer using ``pathlib.Path`` objects
where possible. _StrPath objects can be treated as equivalent to ``Path``,
-save that ``_StrPath.replace`` is overriden with ``str.replace``.
+save that ``_StrPath.replace`` is overridden with ``str.replace``.
To continue treating path-like objects as strings, use ``os.fspath``,
or explicit string coercion.
diff --git a/tests/test_extensions/test_ext_intersphinx_cache.py b/tests/test_extensions/test_ext_intersphinx_cache.py
index 72d942846bc..047589b7fd5 100644
--- a/tests/test_extensions/test_ext_intersphinx_cache.py
+++ b/tests/test_extensions/test_ext_intersphinx_cache.py
@@ -295,7 +295,7 @@ def test_load_mappings_cache_revert_update(tmp_path):
app2.build()
app2.cleanup()
- # switch back to old url (re-use 'old_item')
+ # switch back to old url (reuse 'old_item')
confoverrides3 = BASE_CONFIG | {'intersphinx_mapping': old_project.record}
app3 = SphinxTestApp('dummy', srcdir=tmp_path, confoverrides=confoverrides3)
app3.build()
diff --git a/tests/test_intl/test_intl.py b/tests/test_intl/test_intl.py
index 21e7b301919..19808f6d538 100644
--- a/tests/test_intl/test_intl.py
+++ b/tests/test_intl/test_intl.py
@@ -1569,7 +1569,7 @@ def test_additional_targets_should_be_translated(app):
# [literalblock.txt]
result = (app.outdir / 'literalblock.html').read_text(encoding='utf8')
- # basic literal bloc should be translated
+ # basic literal block should be translated
expected_expr = (
'THIS IS\n'
'LITERAL BLOCK'
From eb337fec0acd8ce978caa0663f60f3a6afd76322 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Fri, 8 Nov 2024 12:44:09 +0000
Subject: [PATCH 37/39] Remove duplicate entries in AUTHORS
---
AUTHORS.rst | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 3c5de42f249..8068ae4ae22 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -8,7 +8,7 @@ Maintainers
* Chris Sewell <@chrisjsewell>
* François Freitag <@francoisfreitag>
* Jakob Lykke Andersen <@jakobandersen>
-* Jean-François Burnol <@jfbu>
+* Jean-François B. <@jfbu>
* Stephen Finucane <@stephenfin>
* Takayuki Shimizukawa <@shimizukawa>
* Takeshi Komiya <@tk0miya>
@@ -35,7 +35,6 @@ Contributors
* Christopher Perkins -- autosummary integration
* Dan MacKinlay -- metadata fixes
* Daniel Bültmann -- todo extension
-* Daniel Neuhäuser -- JavaScript domain, Python 3 support (GSOC)
* Daniel Pizetta -- inheritance diagram improvements
* Dave Kuhlman -- original LaTeX writer
* Doug Hellmann -- graphviz improvements
@@ -73,14 +72,12 @@ Contributors
* Michael Wilson -- Intersphinx HTTP basic auth support
* Nathan Damon -- bugfix in validation of static paths in html builders
* Pauli Virtanen -- autodoc improvements, autosummary extension
-* A. Rafey Khan -- improved intersphinx typing
-* Rob Ruana -- napoleon extension
-* Robert Lehmann -- gettext builder (GSOC project)
+* \A. Rafey Khan -- improved intersphinx typing
* Roland Meister -- epub builder
* Sebastian Wiesner -- image handling, distutils support
* Stefan Seefeld -- toctree improvements
* Stefan van der Walt -- autosummary extension
-* T. Powers -- HTML output improvements
+* \T. Powers -- HTML output improvements
* Taku Shimizu -- epub3 builder
* Thomas Lamb -- linkcheck builder
* Thomas Waldmann -- apidoc module fixes
From 2dfb0b907d54b187b5ddc8d4b6490f106f4d51a9 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Date: Wed, 13 Nov 2024 21:41:26 +0000
Subject: [PATCH 38/39] Account for removal of ``docutils.utils.roman``
(#13131)
---
pyproject.toml | 1 +
sphinx/writers/latex.py | 12 ++++--------
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index ac079b0766b..e3abc9d218d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -70,6 +70,7 @@ dependencies = [
"alabaster>=0.7.14",
"imagesize>=1.3",
"requests>=2.30.0",
+ "roman-numerals-py>=1.0.0",
"packaging>=23.0",
"colorama>=0.4.6; sys_platform == 'win32'",
]
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 2dffd7e3f15..454543b043c 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -13,6 +13,7 @@
from typing import TYPE_CHECKING, Any, ClassVar, cast
from docutils import nodes, writers
+from roman_numerals import RomanNumeral
from sphinx import addnodes, highlighting
from sphinx.errors import SphinxError
@@ -24,12 +25,6 @@
from sphinx.util.template import LaTeXRenderer
from sphinx.util.texescape import tex_replace_map
-try:
- from docutils.utils.roman import toRoman
-except ImportError:
- # In Debian/Ubuntu, roman package is provided as roman, not as docutils.utils.roman
- from roman import toRoman # type: ignore[no-redef, import-not-found]
-
if TYPE_CHECKING:
from docutils.nodes import Element, Node, Text
@@ -1421,8 +1416,9 @@ def get_nested_level(node: Element) -> int:
else:
return get_nested_level(node.parent)
- enum = 'enum%s' % toRoman(get_nested_level(node)).lower()
- enumnext = 'enum%s' % toRoman(get_nested_level(node) + 1).lower()
+ nested_level = get_nested_level(node)
+ enum = f'enum{RomanNumeral(nested_level).to_lowercase()}'
+ enumnext = f'enum{RomanNumeral(nested_level + 1).to_lowercase()}'
style = ENUMERATE_LIST_STYLE.get(get_enumtype(node))
prefix = node.get('prefix', '')
suffix = node.get('suffix', '.')
From d6da26a7f5e2c50acd2e5728125aff9667a1a33b Mon Sep 17 00:00:00 2001
From: Dmitry Shachnev
Date: Thu, 14 Nov 2024 00:53:01 +0300
Subject: [PATCH 39/39] Properly strip time zones which are west of UTC
(#13128)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
tests/test_util/test_util_i18n.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_util/test_util_i18n.py b/tests/test_util/test_util_i18n.py
index d5ee52fb1f8..95d0909f90a 100644
--- a/tests/test_util/test_util_i18n.py
+++ b/tests/test_util/test_util_i18n.py
@@ -108,7 +108,7 @@ def test_format_date_timezone():
assert fd_gmt == '2016-08-07 05:11:17'
assert fd_gmt == iso_gmt
- iso_local = dt.astimezone().isoformat(' ').split('+')[0]
+ iso_local = dt.astimezone().isoformat(' ')[:19] # strip the timezone
fd_local = i18n.format_date(fmt, date=dt, language='en', local_time=True)
assert fd_local == iso_local
assert fd_local != fd_gmt