From 7227b09a2d01bb4671ebc48329c97e59f8207e64 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 18 Jan 2025 18:38:40 +0000 Subject: [PATCH 1/3] Replace ``path`` with ``os.path`` --- sphinx/builders/gettext.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index dbb593f8f03..2976483d55a 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -4,9 +4,11 @@ import codecs import operator +import os +import os.path import time from collections import defaultdict -from os import getenv, path, walk +from os import getenv, walk from pathlib import Path from typing import TYPE_CHECKING from uuid import uuid4 @@ -28,7 +30,6 @@ from sphinx.util.template import SphinxRenderer if TYPE_CHECKING: - import os from collections.abc import Iterable, Iterator, Sequence from typing import Any, Literal @@ -209,7 +210,7 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None: def should_write(filepath: str, new_content: str) -> bool: - if not path.exists(filepath): + if not os.path.exists(filepath): return True try: with codecs.open(filepath, encoding='utf-8') as oldpot: @@ -251,11 +252,11 @@ def init(self) -> None: def _collect_templates(self) -> set[str]: template_files = set() for template_path in self.config.templates_path: - tmpl_abs_path = path.join(self.app.srcdir, template_path) + tmpl_abs_path = os.path.join(self.app.srcdir, template_path) for dirpath, _dirs, files in walk(tmpl_abs_path): for fn in files: if fn.endswith('.html'): - filename = canon_path(path.join(dirpath, fn)) + filename = canon_path(os.path.join(dirpath, fn)) template_files.add(filename) return template_files @@ -311,7 +312,7 @@ def finish(self) -> None: operator.itemgetter(0), ): # noop if config.gettext_compact is set - ensuredir(path.join(self.outdir, path.dirname(textdomain))) + ensuredir(os.path.join(self.outdir, os.path.dirname(textdomain))) context['messages'] = list(catalog) template_path = [ @@ -320,7 +321,7 @@ def finish(self) -> None: renderer = GettextRenderer(template_path, outdir=self.outdir) content = renderer.render('message.pot.jinja', context) - pofn = path.join(self.outdir, textdomain + '.pot') + pofn = os.path.join(self.outdir, textdomain + '.pot') if should_write(pofn, content): with codecs.open(pofn, 'w', encoding='utf-8') as pofile: pofile.write(content) From 4b1d64d1e54aa3705b3246e9cd3cb647e3c4aaf1 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 18 Jan 2025 18:38:59 +0000 Subject: [PATCH 2/3] Use ``app.build(force_all=True)`` --- tests/test_domains/test_domain_cpp.py | 2 +- tests/test_extensions/test_ext_viewcode.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_domains/test_domain_cpp.py b/tests/test_domains/test_domain_cpp.py index 98af279ebdf..19e5e07523d 100644 --- a/tests/test_domains/test_domain_cpp.py +++ b/tests/test_domains/test_domain_cpp.py @@ -1826,7 +1826,7 @@ def test_domain_cpp_build_field_role(app): @pytest.mark.sphinx('html', testroot='domain-cpp', confoverrides={'nitpicky': True}) def test_domain_cpp_build_operator_lookup(app): - app.builder.build_all() + app.build(force_all=True) ws = filter_warnings(app.warning, 'operator-lookup') assert len(ws) == 5 # TODO: the first one should not happen diff --git a/tests/test_extensions/test_ext_viewcode.py b/tests/test_extensions/test_ext_viewcode.py index cb6b29859dc..01d68cd3ccf 100644 --- a/tests/test_extensions/test_ext_viewcode.py +++ b/tests/test_extensions/test_ext_viewcode.py @@ -173,7 +173,7 @@ def find_source(app, modname): @pytest.mark.sphinx('html', testroot='ext-viewcode-find-package', freshenv=True) def test_find_local_package_import_path(app, status, warning): - app.builder.build_all() + app.build(force_all=True) result = (app.outdir / 'index.html').read_text(encoding='utf8') count_func1 = result.count( From fc62472a03679928a7f0ca9755dee68a7e88328c Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:42:47 +0000 Subject: [PATCH 3/3] Replace ``os.path.join`` with pathlib (#13252) --- sphinx/builders/__init__.py | 6 +-- sphinx/builders/_epub_base.py | 36 ++++++------- sphinx/builders/changes.py | 15 +++--- sphinx/builders/epub3.py | 5 +- sphinx/builders/gettext.py | 14 +++--- sphinx/builders/latex/__init__.py | 23 ++++----- sphinx/builders/latex/theming.py | 15 +++--- sphinx/builders/linkcheck.py | 8 ++- sphinx/builders/manpage.py | 5 +- sphinx/builders/texinfo.py | 9 ++-- sphinx/builders/text.py | 10 ++-- sphinx/builders/xml.py | 10 ++-- sphinx/ext/apidoc/_generate.py | 12 +++-- sphinx/ext/autosummary/generate.py | 4 +- sphinx/ext/coverage.py | 10 ++-- sphinx/ext/graphviz.py | 29 ++++++----- sphinx/ext/imgconverter.py | 10 ++-- sphinx/ext/imgmath.py | 56 ++++++++++----------- sphinx/ext/viewcode.py | 3 +- sphinx/jinja2glue.py | 12 ++--- sphinx/transforms/post_transforms/images.py | 26 +++++----- sphinx/util/_files.py | 3 +- sphinx/util/png.py | 8 ++- 23 files changed, 163 insertions(+), 166 deletions(-) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 89d518f8539..0b162305e7e 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -414,7 +414,7 @@ def build( with ( progress_message(__('pickling environment')), - open(os.path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f, + open(self.doctreedir / ENV_PICKLE_FILENAME, 'wb') as f, ): pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL) @@ -622,7 +622,7 @@ def read_doc(self, docname: str, *, _cache: bool = True) -> None: env.prepare_settings(docname) # Add confdir/docutils.conf to dependencies list if exists - docutilsconf = os.path.join(self.confdir, 'docutils.conf') + docutilsconf = self.confdir / 'docutils.conf' if os.path.isfile(docutilsconf): env.note_dependency(docutilsconf) @@ -674,7 +674,7 @@ def write_doctree( doctree.settings.env = None doctree.settings.record_dependencies = None - doctree_filename = os.path.join(self.doctreedir, docname + '.doctree') + doctree_filename = self.doctreedir / f'{docname}.doctree' ensuredir(os.path.dirname(doctree_filename)) with open(doctree_filename, 'wb') as f: pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index 441bb71481d..66e1fa7a22d 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -7,6 +7,7 @@ import os.path import re import time +from pathlib import Path from typing import TYPE_CHECKING, NamedTuple from urllib.parse import quote from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile @@ -19,12 +20,12 @@ from sphinx.builders.html._build_info import BuildInfo from sphinx.locale import __ from sphinx.util import logging +from sphinx.util._pathlib import _StrPath from sphinx.util.display import status_iterator from sphinx.util.fileutil import copy_asset_file from sphinx.util.osutil import copyfile, ensuredir, relpath if TYPE_CHECKING: - from pathlib import Path from typing import Any from docutils.nodes import Element, Node @@ -158,7 +159,7 @@ class EpubBuilder(StandaloneHTMLBuilder): guide_titles = GUIDE_TITLES media_types = MEDIA_TYPES refuri_re = REFURI_RE - template_dir = '' + template_dir: _StrPath = _StrPath() doctype = '' def init(self) -> None: @@ -417,7 +418,7 @@ def copy_image_files_pil(self) -> None: The method tries to read and write the files with Pillow, converting the format and resizing the image if necessary/possible. """ - ensuredir(os.path.join(self.outdir, self.imagedir)) + ensuredir(self.outdir / self.imagedir) for src in status_iterator( self.images, __('copying images... '), @@ -427,12 +428,12 @@ def copy_image_files_pil(self) -> None: ): dest = self.images[src] try: - img = Image.open(os.path.join(self.srcdir, src)) + img = Image.open(self.srcdir / src) except OSError: if not self.is_vector_graphics(src): logger.warning( __('cannot read image file %r: copying it instead'), - os.path.join(self.srcdir, src), + self.srcdir / src, ) try: copyfile( @@ -443,7 +444,7 @@ def copy_image_files_pil(self) -> None: except OSError as err: logger.warning( __('cannot copy image file %r: %s'), - os.path.join(self.srcdir, src), + self.srcdir / src, err, ) continue @@ -459,11 +460,11 @@ def copy_image_files_pil(self) -> None: nh = round((height * nw) / width) img = img.resize((nw, nh), Image.BICUBIC) try: - img.save(os.path.join(self.outdir, self.imagedir, dest)) + img.save(self.outdir / self.imagedir / dest) except OSError as err: logger.warning( __('cannot write image file %r: %s'), - os.path.join(self.srcdir, src), + self.srcdir / src, err, ) @@ -511,7 +512,7 @@ def build_mimetype(self) -> None: """Write the metainfo file mimetype.""" logger.info(__('writing mimetype file...')) copyfile( - os.path.join(self.template_dir, 'mimetype'), + self.template_dir / 'mimetype', self.outdir / 'mimetype', force=True, ) @@ -522,7 +523,7 @@ def build_container(self, outname: str = 'META-INF/container.xml') -> None: outdir = self.outdir / 'META-INF' ensuredir(outdir) copyfile( - os.path.join(self.template_dir, 'container.xml'), + self.template_dir / 'container.xml', outdir / 'container.xml', force=True, ) @@ -577,9 +578,10 @@ def build_content(self) -> None: if not self.use_index: self.ignored_files.append('genindex' + self.out_suffix) for root, dirs, files in os.walk(self.outdir): + root_path = Path(root) dirs.sort() for fn in sorted(files): - filename = relpath(os.path.join(root, fn), self.outdir) + filename = relpath(root_path / fn, self.outdir) if filename in self.ignored_files: continue ext = os.path.splitext(filename)[-1] @@ -684,7 +686,7 @@ def build_content(self) -> None: # write the project file copy_asset_file( - os.path.join(self.template_dir, 'content.opf.jinja'), + self.template_dir / 'content.opf.jinja', self.outdir, context=metadata, force=True, @@ -778,7 +780,7 @@ def build_toc(self) -> None: level = max(item['level'] for item in self.refnodes) level = min(level, self.config.epub_tocdepth) copy_asset_file( - os.path.join(self.template_dir, 'toc.ncx.jinja'), + self.template_dir / 'toc.ncx.jinja', self.outdir, context=self.toc_metadata(level, navpoints), force=True, @@ -792,10 +794,10 @@ def build_epub(self) -> None: """ outname = self.config.epub_basename + '.epub' logger.info(__('writing %s file...'), outname) - epub_filename = os.path.join(self.outdir, outname) + epub_filename = self.outdir / outname with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub: - epub.write(os.path.join(self.outdir, 'mimetype'), 'mimetype', ZIP_STORED) + epub.write(self.outdir / 'mimetype', 'mimetype', ZIP_STORED) for filename in ('META-INF/container.xml', 'content.opf', 'toc.ncx'): - epub.write(os.path.join(self.outdir, filename), filename, ZIP_DEFLATED) + epub.write(self.outdir / filename, filename, ZIP_DEFLATED) for filename in self.files: - epub.write(os.path.join(self.outdir, filename), filename, ZIP_DEFLATED) + epub.write(self.outdir / filename, filename, ZIP_DEFLATED) diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index 585bdf3f748..7edf9c39c03 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -4,6 +4,7 @@ import html import os.path +from pathlib import Path from typing import TYPE_CHECKING from sphinx import package_dir @@ -13,7 +14,7 @@ from sphinx.theming import HTMLThemeFactory from sphinx.util import logging from sphinx.util.fileutil import copy_asset_file -from sphinx.util.osutil import ensuredir, os_path +from sphinx.util.osutil import ensuredir if TYPE_CHECKING: from collections.abc import Set @@ -103,9 +104,9 @@ def write_documents(self, _docnames: Set[str]) -> None: 'show_copyright': self.config.html_show_copyright, 'show_sphinx': self.config.html_show_sphinx, } - with open(os.path.join(self.outdir, 'index.html'), 'w', encoding='utf8') as f: + with open(self.outdir / 'index.html', 'w', encoding='utf8') as f: f.write(self.templates.render('changes/frameset.html', ctx)) - with open(os.path.join(self.outdir, 'changes.html'), 'w', encoding='utf8') as f: + with open(self.outdir / 'changes.html', 'w', encoding='utf8') as f: f.write(self.templates.render('changes/versionchanges.html', ctx)) hltext = [ @@ -141,7 +142,7 @@ def hl(no: int, line: str) -> str: 'text': text, } rendered = self.templates.render('changes/rstsource.html', ctx) - targetfn = os.path.join(self.outdir, 'rst', os_path(docname)) + '.html' + targetfn = self.outdir / 'rst' / f'{docname}.html' ensuredir(os.path.dirname(targetfn)) with open(targetfn, 'w', encoding='utf-8') as f: f.write(rendered) @@ -149,16 +150,14 @@ def hl(no: int, line: str) -> str: 'theme_' + key: val for (key, val) in self.theme.get_options({}).items() } copy_asset_file( - os.path.join( - package_dir, 'themes', 'default', 'static', 'default.css.jinja' - ), + Path(package_dir, 'themes', 'default', 'static', 'default.css.jinja'), self.outdir, context=themectx, renderer=self.templates, force=True, ) copy_asset_file( - os.path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'), + Path(package_dir, 'themes', 'basic', 'static', 'basic.css'), self.outdir / 'basic.css', force=True, ) diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index 23528adcf18..ac9ac85a754 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -17,6 +17,7 @@ from sphinx.config import ENUM from sphinx.locale import __ from sphinx.util import logging +from sphinx.util._pathlib import _StrPath from sphinx.util.fileutil import copy_asset_file from sphinx.util.osutil import make_filename @@ -84,7 +85,7 @@ class Epub3Builder(_epub_base.EpubBuilder): epilog = __('The ePub file is in %(outdir)s.') supported_remote_images = False - template_dir = os.path.join(package_dir, 'templates', 'epub3') + template_dir = _StrPath(package_dir, 'templates', 'epub3') doctype = DOCTYPE html_tag = HTML_TAG use_meta_charset = True @@ -199,7 +200,7 @@ def build_navigation_doc(self) -> None: refnodes = self.refnodes navlist = self.build_navlist(refnodes) copy_asset_file( - os.path.join(self.template_dir, 'nav.xhtml.jinja'), + self.template_dir / 'nav.xhtml.jinja', self.outdir, context=self.navigation_doc_metadata(navlist), force=True, diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 2976483d55a..1d581afb655 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -209,11 +209,11 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None: ctime = time.strftime('%Y-%m-%d %H:%M%z', timestamp) -def should_write(filepath: str, new_content: str) -> bool: +def should_write(filepath: str | os.PathLike[str], new_content: str) -> bool: if not os.path.exists(filepath): return True try: - with codecs.open(filepath, encoding='utf-8') as oldpot: + with codecs.open(str(filepath), encoding='utf-8') as oldpot: old_content = oldpot.read() old_header_index = old_content.index('"POT-Creation-Date:') new_header_index = new_content.index('"POT-Creation-Date:') @@ -252,11 +252,11 @@ def init(self) -> None: def _collect_templates(self) -> set[str]: template_files = set() for template_path in self.config.templates_path: - tmpl_abs_path = os.path.join(self.app.srcdir, template_path) + tmpl_abs_path = self.app.srcdir / template_path for dirpath, _dirs, files in walk(tmpl_abs_path): for fn in files: if fn.endswith('.html'): - filename = canon_path(os.path.join(dirpath, fn)) + filename = Path(dirpath, fn).as_posix() template_files.add(filename) return template_files @@ -312,7 +312,7 @@ def finish(self) -> None: operator.itemgetter(0), ): # noop if config.gettext_compact is set - ensuredir(os.path.join(self.outdir, os.path.dirname(textdomain))) + ensuredir(self.outdir / os.path.dirname(textdomain)) context['messages'] = list(catalog) template_path = [ @@ -321,9 +321,9 @@ def finish(self) -> None: renderer = GettextRenderer(template_path, outdir=self.outdir) content = renderer.render('message.pot.jinja', context) - pofn = os.path.join(self.outdir, textdomain + '.pot') + pofn = self.outdir / f'{textdomain}.pot' if should_write(pofn, content): - with codecs.open(pofn, 'w', encoding='utf-8') as pofile: + with codecs.open(str(pofn), 'w', encoding='utf-8') as pofile: pofile.write(content) diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 2bec3c010dc..27a4767a5f6 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -277,7 +277,7 @@ def init_multilingual(self) -> None: def write_stylesheet(self) -> None: highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style) - stylesheet = os.path.join(self.outdir, 'sphinxhighlight.sty') + stylesheet = self.outdir / 'sphinxhighlight.sty' with open(stylesheet, 'w', encoding='utf-8') as f: f.write('\\NeedsTeXFormat{LaTeX2e}[1995/12/01]\n') f.write( @@ -318,7 +318,7 @@ def write_documents(self, _docnames: Set[str]) -> None: if len(entry) > 5: toctree_only = entry[5] destination = SphinxFileOutput( - destination_path=os.path.join(self.outdir, targetname), + destination_path=self.outdir / targetname, encoding='utf-8', overwrite_if_changed=True, ) @@ -444,11 +444,11 @@ def copy_support_files(self) -> None: 'xindy_lang_option': xindy_lang_option, 'xindy_cyrillic': xindy_cyrillic, } - staticdirname = os.path.join(package_dir, 'texinputs') - for filename in Path(staticdirname).iterdir(): + static_dir_name = Path(package_dir, 'texinputs') + for filename in Path(static_dir_name).iterdir(): if not filename.name.startswith('.'): copy_asset_file( - os.path.join(staticdirname, filename), + static_dir_name / filename, self.outdir, context=context, force=True, @@ -456,9 +456,9 @@ def copy_support_files(self) -> None: # use pre-1.6.x Makefile for make latexpdf on Windows if os.name == 'nt': - staticdirname = os.path.join(package_dir, 'texinputs_win') + static_dir_name = Path(package_dir, 'texinputs_win') copy_asset_file( - os.path.join(staticdirname, 'Makefile.jinja'), + static_dir_name / 'Makefile.jinja', self.outdir, context=context, force=True, @@ -496,11 +496,11 @@ def copy_image_files(self) -> None: except Exception as err: logger.warning( __('cannot copy image file %r: %s'), - os.path.join(self.srcdir, src), + self.srcdir / src, err, ) if self.config.latex_logo: - if not os.path.isfile(os.path.join(self.confdir, self.config.latex_logo)): + if not os.path.isfile(self.confdir / self.config.latex_logo): raise SphinxError( __('logo file %r does not exist') % self.config.latex_logo ) @@ -523,11 +523,8 @@ def write_message_catalog(self) -> None: if self.context['babel'] or self.context['polyglossia']: context['addtocaptions'] = r'\addto\captions%s' % self.babel.get_language() - filename = os.path.join( - package_dir, 'templates', 'latex', 'sphinxmessages.sty.jinja' - ) copy_asset_file( - filename, + Path(package_dir, 'templates', 'latex', 'sphinxmessages.sty.jinja'), self.outdir, context=context, renderer=LaTeXRenderer(), diff --git a/sphinx/builders/latex/theming.py b/sphinx/builders/latex/theming.py index de0540127df..f55c077c9ca 100644 --- a/sphinx/builders/latex/theming.py +++ b/sphinx/builders/latex/theming.py @@ -3,7 +3,6 @@ from __future__ import annotations import configparser -import os.path from typing import TYPE_CHECKING from sphinx.errors import ThemeError @@ -11,6 +10,8 @@ from sphinx.util import logging if TYPE_CHECKING: + from pathlib import Path + from sphinx.application import Sphinx from sphinx.config import Config @@ -74,10 +75,10 @@ class UserTheme(Theme): REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass'] OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning'] - def __init__(self, name: str, filename: str) -> None: + def __init__(self, name: str, filename: Path) -> None: super().__init__(name) self.config = configparser.RawConfigParser() - self.config.read(os.path.join(filename), encoding='utf-8') + self.config.read(filename, encoding='utf-8') for key in self.REQUIRED_CONFIG_KEYS: try: @@ -103,9 +104,7 @@ class ThemeFactory: def __init__(self, app: Sphinx) -> None: self.themes: dict[str, Theme] = {} - self.theme_paths = [ - os.path.join(app.srcdir, p) for p in app.config.latex_theme_path - ] + self.theme_paths = [app.srcdir / p for p in app.config.latex_theme_path] self.config = app.config self.load_builtin_themes(app.config) @@ -127,8 +126,8 @@ def get(self, name: str) -> Theme: def find_user_theme(self, name: str) -> Theme | None: """Find a theme named as *name* from latex_theme_path.""" for theme_path in self.theme_paths: - config_path = os.path.join(theme_path, name, 'theme.conf') - if os.path.isfile(config_path): + config_path = theme_path / name / 'theme.conf' + if config_path.is_file(): try: return UserTheme(name, config_path) except ThemeError as exc: diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 4fa8e0933b2..31a130f3947 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -4,7 +4,6 @@ import contextlib import json -import os.path import re import socket import time @@ -88,8 +87,8 @@ def finish(self) -> None: checker = HyperlinkAvailabilityChecker(self.config) logger.info('') - output_text = os.path.join(self.outdir, 'output.txt') - output_json = os.path.join(self.outdir, 'output.json') + output_text = self.outdir / 'output.txt' + output_json = self.outdir / 'output.json' with ( open(output_text, 'w', encoding='utf-8') as self.txt_outfile, open(output_json, 'w', encoding='utf-8') as self.json_outfile, @@ -460,8 +459,7 @@ def _check(self, docname: str, uri: str, hyperlink: Hyperlink) -> _URIProperties # Non-supported URI schemes (ex. ftp) return _Status.UNCHECKED, '', 0 - src_dir = os.path.dirname(hyperlink.docpath) - if os.path.exists(os.path.join(src_dir, uri)): + if (hyperlink.docpath.parent / uri).exists(): return _Status.WORKING, '', 0 return _Status.BROKEN, '', 0 diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py index a60a8bb9c28..5136124ca44 100644 --- a/sphinx/builders/manpage.py +++ b/sphinx/builders/manpage.py @@ -2,7 +2,6 @@ from __future__ import annotations -import os.path import warnings from typing import TYPE_CHECKING @@ -86,14 +85,14 @@ def write_documents(self, _docnames: Set[str]) -> None: if self.config.man_make_section_directory: dirname = 'man%s' % section - ensuredir(os.path.join(self.outdir, dirname)) + ensuredir(self.outdir / dirname) targetname = f'{dirname}/{name}.{section}' else: targetname = f'{name}.{section}' logger.info('%s { ', darkgreen(targetname)) destination = FileOutput( - destination_path=os.path.join(self.outdir, targetname), + destination_path=self.outdir / targetname, encoding='utf-8', ) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index 761831e0d1d..f2d8c060745 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -5,6 +5,7 @@ import os import os.path import warnings +from pathlib import Path from typing import TYPE_CHECKING from docutils import nodes @@ -35,7 +36,7 @@ from sphinx.util.typing import ExtensionMetadata logger = logging.getLogger(__name__) -template_dir = os.path.join(package_dir, 'templates', 'texinfo') +template_dir = Path(package_dir, 'templates', 'texinfo') class TexinfoBuilder(Builder): @@ -108,7 +109,7 @@ def write_documents(self, _docnames: Set[str]) -> None: if len(entry) > 7: toctree_only = entry[7] destination = FileOutput( - destination_path=os.path.join(self.outdir, targetname), + destination_path=self.outdir / targetname, encoding='utf-8', ) with progress_message(__('processing %s') % targetname, nonl=False): @@ -216,7 +217,7 @@ def copy_image_files(self, targetname: str) -> None: except Exception as err: logger.warning( __('cannot copy image file %r: %s'), - os.path.join(self.srcdir, src), + self.srcdir / src, err, ) @@ -225,7 +226,7 @@ def copy_support_files(self) -> None: with progress_message(__('copying Texinfo support files')): logger.info('Makefile ', nonl=True) copyfile( - os.path.join(template_dir, 'Makefile'), + template_dir / 'Makefile', self.outdir / 'Makefile', force=True, ) diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py index 03e22d2c73e..75be377cabd 100644 --- a/sphinx/builders/text.py +++ b/sphinx/builders/text.py @@ -10,11 +10,7 @@ from sphinx.builders import Builder from sphinx.locale import __ from sphinx.util import logging -from sphinx.util.osutil import ( - _last_modified_time, - ensuredir, - os_path, -) +from sphinx.util.osutil import _last_modified_time, ensuredir from sphinx.writers.text import TextTranslator, TextWriter if TYPE_CHECKING: @@ -48,7 +44,7 @@ def get_outdated_docs(self) -> Iterator[str]: if docname not in self.env.all_docs: yield docname continue - targetname = os.path.join(self.outdir, docname + self.out_suffix) + targetname = self.outdir / (docname + self.out_suffix) try: targetmtime = _last_modified_time(targetname) except Exception: @@ -72,7 +68,7 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None: self.secnumbers = self.env.toc_secnumbers.get(docname, {}) destination = StringOutput(encoding='utf-8') self.writer.write(doctree, destination) - outfilename = os.path.join(self.outdir, os_path(docname) + self.out_suffix) + outfilename = self.outdir / (docname + self.out_suffix) ensuredir(os.path.dirname(outfilename)) try: with open(outfilename, 'w', encoding='utf-8') as f: diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py index e50d6f2b02d..b1204b8495f 100644 --- a/sphinx/builders/xml.py +++ b/sphinx/builders/xml.py @@ -12,11 +12,7 @@ from sphinx.builders import Builder from sphinx.locale import __ from sphinx.util import logging -from sphinx.util.osutil import ( - _last_modified_time, - ensuredir, - os_path, -) +from sphinx.util.osutil import _last_modified_time, ensuredir from sphinx.writers.xml import PseudoXMLWriter, XMLWriter if TYPE_CHECKING: @@ -50,7 +46,7 @@ def get_outdated_docs(self) -> Iterator[str]: if docname not in self.env.all_docs: yield docname continue - targetname = os.path.join(self.outdir, docname + self.out_suffix) + targetname = self.outdir / (docname + self.out_suffix) try: targetmtime = _last_modified_time(targetname) except Exception: @@ -86,7 +82,7 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None: value[i] = list(val) destination = StringOutput(encoding='utf-8') self.writer.write(doctree, destination) - outfilename = os.path.join(self.outdir, os_path(docname) + self.out_suffix) + outfilename = self.outdir / (docname + self.out_suffix) ensuredir(os.path.dirname(outfilename)) try: with open(outfilename, 'w', encoding='utf-8') as f: diff --git a/sphinx/ext/apidoc/_generate.py b/sphinx/ext/apidoc/_generate.py index 8c6da962af3..8099eb8ada4 100644 --- a/sphinx/ext/apidoc/_generate.py +++ b/sphinx/ext/apidoc/_generate.py @@ -33,7 +33,7 @@ PY_SUFFIXES = ('.py', '.pyx', *EXTENSION_SUFFIXES) -template_dir = os.path.join(package_dir, 'templates', 'apidoc') +template_dir = Path(package_dir, 'templates', 'apidoc') def is_initpy(filename: str | Path) -> bool: @@ -84,7 +84,7 @@ def create_module_file( package: str | None, basename: str, opts: ApidocOptions, - user_template_dir: str | None = None, + user_template_dir: str | os.PathLike[str] | None = None, ) -> Path: """Build the text of the file and write the file.""" options = set(OPTIONS if not opts.automodule_options else opts.automodule_options) @@ -98,6 +98,7 @@ def create_module_file( 'qualname': qualname, 'automodule_options': sorted(options), } + template_path: Sequence[str | os.PathLike[str]] if user_template_dir is not None: template_path = [user_template_dir, template_dir] else: @@ -115,7 +116,7 @@ def create_package_file( subs: list[str], is_namespace: bool, excludes: Sequence[re.Pattern[str]] = (), - user_template_dir: str | None = None, + user_template_dir: str | os.PathLike[str] | None = None, ) -> list[Path]: """Build the text of the file and write the file. @@ -178,7 +179,7 @@ def create_modules_toc_file( modules: list[str], opts: ApidocOptions, name: str = 'modules', - user_template_dir: str | None = None, + user_template_dir: str | os.PathLike[str] | None = None, ) -> Path: """Create the module's index.""" modules.sort() @@ -195,6 +196,7 @@ def create_modules_toc_file( 'maxdepth': opts.maxdepth, 'docnames': modules, } + template_path: Sequence[str | os.PathLike[str]] if user_template_dir is not None: template_path = [user_template_dir, template_dir] else: @@ -274,7 +276,7 @@ def recurse_tree( rootpath: str | os.PathLike[str], excludes: Sequence[re.Pattern[str]], opts: ApidocOptions, - user_template_dir: str | None = None, + user_template_dir: str | os.PathLike[str] | None = None, ) -> tuple[list[Path], list[str]]: """Look for every file in the directory tree and create the corresponding ReST files. diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 617fc495be8..24f014d8cf6 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -133,9 +133,7 @@ def __init__(self, app: Sphinx) -> None: msg = 'Expected a Sphinx application object!' raise TypeError(msg) - system_templates_path = [ - os.path.join(package_dir, 'ext', 'autosummary', 'templates') - ] + system_templates_path = [Path(package_dir, 'ext', 'autosummary', 'templates')] loader = SphinxTemplateLoader( app.srcdir, app.config.templates_path, system_templates_path ) diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index 4911458e319..ebb1c1f5bb0 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -176,8 +176,8 @@ class CoverageBuilder(Builder): def init(self) -> None: self.c_sourcefiles: list[str] = [] for pattern in self.config.coverage_c_path: - pattern = os.path.join(self.srcdir, pattern) - self.c_sourcefiles.extend(glob.glob(pattern)) # NoQA: PTH207 + pattern = self.srcdir / pattern + self.c_sourcefiles.extend(glob.glob(str(pattern))) # NoQA: PTH207 self.c_regexes: list[tuple[str, re.Pattern[str]]] = [] for name, exp in self.config.coverage_c_regexes.items(): @@ -244,7 +244,7 @@ def build_c_coverage(self) -> None: self.c_undoc[filename] = undoc def write_c_coverage(self) -> None: - output_file = os.path.join(self.outdir, 'c.txt') + output_file = self.outdir / 'c.txt' with open(output_file, 'w', encoding='utf-8') as op: if self.config.coverage_write_headline: write_header(op, 'Undocumented C API elements', '=') @@ -419,7 +419,7 @@ def _write_py_statistics(self, op: TextIO) -> None: op.write(f'{line}\n') def write_py_coverage(self) -> None: - output_file = os.path.join(self.outdir, 'python.txt') + output_file = self.outdir / 'python.txt' failed = [] with open(output_file, 'w', encoding='utf-8') as op: if self.config.coverage_write_headline: @@ -513,7 +513,7 @@ def write_py_coverage(self) -> None: def finish(self) -> None: # dump the coverage data to a pickle file too - picklepath = os.path.join(self.outdir, 'undoc.pickle') + picklepath = self.outdir / 'undoc.pickle' with open(picklepath, 'wb') as dumpfile: pickle.dump( (self.py_undoc, self.c_undoc, self.py_undocumented, self.py_documented), diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 107872e476d..9bbd8ed0a62 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -3,12 +3,12 @@ from __future__ import annotations import os.path -import posixpath import re import subprocess import xml.etree.ElementTree as ET from hashlib import sha1 from itertools import chain +from pathlib import Path from subprocess import CalledProcessError from typing import TYPE_CHECKING from urllib.parse import urlsplit, urlunsplit @@ -20,6 +20,7 @@ from sphinx.errors import SphinxError from sphinx.locale import _, __ from sphinx.util import logging +from sphinx.util._pathlib import _StrPath from sphinx.util.docutils import SphinxDirective from sphinx.util.i18n import search_image_for_language from sphinx.util.nodes import set_source_info @@ -235,7 +236,8 @@ def run(self) -> list[Node]: def fix_svg_relative_paths( - self: HTML5Translator | LaTeXTranslator | TexinfoTranslator, filepath: str + self: HTML5Translator | LaTeXTranslator | TexinfoTranslator, + filepath: str | os.PathLike[str], ) -> None: """Change relative links in generated svg files to be relative to imgpath.""" env = self.builder.env @@ -279,7 +281,7 @@ def render_dot( format: str, prefix: str = 'graphviz', filename: str | None = None, -) -> tuple[str | None, str | None]: +) -> tuple[_StrPath | None, _StrPath | None]: """Render graphviz code into a PNG or PDF output file.""" graphviz_dot = options.get('graphviz_dot', self.builder.config.graphviz_dot) if not graphviz_dot: @@ -294,8 +296,8 @@ def render_dot( )).encode() fname = f'{prefix}-{sha1(hashkey, usedforsecurity=False).hexdigest()}.{format}' - relfn = posixpath.join(self.builder.imgpath, fname) - outfn = os.path.join(self.builder.outdir, self.builder.imagedir, fname) + relfn = _StrPath(self.builder.imgpath, fname) + outfn = self.builder.outdir / self.builder.imagedir / fname if os.path.isfile(outfn): return relfn, outfn @@ -307,13 +309,13 @@ def render_dot( dot_args = [graphviz_dot] dot_args.extend(self.builder.config.graphviz_dot_args) - dot_args.extend(['-T' + format, '-o' + outfn]) + dot_args.extend([f'-T{format}', f'-o{outfn}']) docname = options.get('docname', 'index') if filename: - cwd = os.path.dirname(os.path.join(self.builder.srcdir, filename)) + cwd = os.path.dirname(self.builder.srcdir / filename) else: - cwd = os.path.dirname(os.path.join(self.builder.srcdir, docname)) + cwd = os.path.dirname(self.builder.srcdir / docname) if format == 'png': dot_args.extend(['-Tcmapx', f'-o{outfn}.map']) @@ -379,6 +381,7 @@ def render_dot_html( if fname is None: self.body.append(self.encode(code)) else: + src = fname.as_posix() if alt is None: alt = node.get('alt', self.encode(code).strip()) if 'align' in node: @@ -387,7 +390,7 @@ def render_dot_html( if format == 'svg': self.body.append('