Remove `_StrPath` (#12650)

This commit is contained in:
Adam Turner
2024-07-22 16:22:41 +01:00
committed by GitHub
parent aa12307cc2
commit f31dbabf20
4 changed files with 11 additions and 130 deletions

View File

@@ -68,6 +68,10 @@ Incompatible changes
* #12096: Do not overwrite user-supplied files when copying assets
unless forced with ``force=True``.
Patch by Adam Turner.
* #12650: Remove support for string methods on :py:class:`~pathlib.Path` objects.
Use :py:func:`os.fspath` to convert :py:class:`~pathlib.Path` objects to strings,
or :py:class:`~pathlib.Path`'s methods to work with path objects.
Patch by Adam Turner.
Deprecated
----------

View File

@@ -13,6 +13,7 @@ from collections import deque
from collections.abc import Callable, Collection, Sequence # NoQA: TCH003
from io import StringIO
from os import path
from pathlib import Path
from typing import IO, TYPE_CHECKING, Any, Literal
from docutils.nodes import TextElement # NoQA: TCH002
@@ -31,7 +32,6 @@ from sphinx.locale import __
from sphinx.project import Project
from sphinx.registry import SphinxComponentRegistry
from sphinx.util import docutils, logging
from sphinx.util._pathlib import _StrPath
from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold
from sphinx.util.display import progress_message
@@ -173,9 +173,9 @@ class Sphinx:
self.registry = SphinxComponentRegistry()
# validate provided directories
self.srcdir = _StrPath(srcdir).resolve()
self.outdir = _StrPath(outdir).resolve()
self.doctreedir = _StrPath(doctreedir).resolve()
self.srcdir = Path(srcdir).resolve()
self.outdir = Path(outdir).resolve()
self.doctreedir = Path(doctreedir).resolve()
if not path.isdir(self.srcdir):
raise ApplicationError(__('Cannot find source directory (%s)') %
@@ -231,7 +231,7 @@ class Sphinx:
self.confdir = self.srcdir
self.config = Config({}, confoverrides or {})
else:
self.confdir = _StrPath(confdir).resolve()
self.confdir = Path(confdir).resolve()
self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
# set up translation infrastructure

View File

@@ -1,120 +0,0 @@
"""What follows is awful and will be gone in Sphinx 8"""
from __future__ import annotations
import sys
import warnings
from pathlib import Path, PosixPath, PurePath, WindowsPath
from typing import Any
from sphinx.deprecation import RemovedInSphinx80Warning
_STR_METHODS = frozenset(str.__dict__)
_PATH_NAME = Path().__class__.__name__
_MSG = (
'Sphinx 8 will drop support for representing paths as strings. '
'Use "pathlib.Path" or "os.fspath" instead.'
)
# https://docs.python.org/3/library/stdtypes.html#typesseq-common
# https://docs.python.org/3/library/stdtypes.html#string-methods
if sys.platform == 'win32':
class _StrPath(WindowsPath):
def replace( # type: ignore[override]
self, old: str, new: str, count: int = -1, /,
) -> str:
# replace exists in both Path and str;
# in Path it makes filesystem changes, so we use the safer str version
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__().replace(old, new, count) # NoQA: PLC2801
def __getattr__(self, item: str) -> Any:
if item in _STR_METHODS:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return getattr(self.__str__(), item)
msg = f'{_PATH_NAME!r} has no attribute {item!r}'
raise AttributeError(msg)
def __add__(self, other: str) -> str:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__() + other
def __bool__(self) -> bool:
if not self.__str__():
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return False
return True
def __contains__(self, item: str) -> bool:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return item in self.__str__()
def __eq__(self, other: object) -> bool:
if isinstance(other, PurePath):
return super().__eq__(other)
if isinstance(other, str):
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__() == other
return NotImplemented
def __hash__(self) -> int:
return super().__hash__()
def __getitem__(self, item: int | slice) -> str:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__()[item]
def __len__(self) -> int:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return len(self.__str__())
else:
class _StrPath(PosixPath):
def replace( # type: ignore[override]
self, old: str, new: str, count: int = -1, /,
) -> str:
# replace exists in both Path and str;
# in Path it makes filesystem changes, so we use the safer str version
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__().replace(old, new, count) # NoQA: PLC2801
def __getattr__(self, item: str) -> Any:
if item in _STR_METHODS:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return getattr(self.__str__(), item)
msg = f'{_PATH_NAME!r} has no attribute {item!r}'
raise AttributeError(msg)
def __add__(self, other: str) -> str:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__() + other
def __bool__(self) -> bool:
if not self.__str__():
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return False
return True
def __contains__(self, item: str) -> bool:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return item in self.__str__()
def __eq__(self, other: object) -> bool:
if isinstance(other, PurePath):
return super().__eq__(other)
if isinstance(other, str):
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__() == other
return NotImplemented
def __hash__(self) -> int:
return super().__hash__()
def __getitem__(self, item: int | slice) -> str:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return self.__str__()[item]
def __len__(self) -> int:
warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
return len(self.__str__())

View File

@@ -7,7 +7,6 @@ import re
import pytest
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.deprecation import RemovedInSphinx80Warning
from sphinx.errors import ConfigError
from sphinx.util.console import strip_colors
from sphinx.util.inventory import InventoryFile
@@ -324,8 +323,7 @@ def test_validate_html_extra_path(app):
app.outdir, # outdir
app.outdir / '_static', # inside outdir
]
with pytest.warns(RemovedInSphinx80Warning, match='Use "pathlib.Path" or "os.fspath" instead'):
validate_html_extra_path(app, app.config)
validate_html_extra_path(app, app.config)
assert app.config.html_extra_path == ['_static']
@@ -338,8 +336,7 @@ def test_validate_html_static_path(app):
app.outdir, # outdir
app.outdir / '_static', # inside outdir
]
with pytest.warns(RemovedInSphinx80Warning, match='Use "pathlib.Path" or "os.fspath" instead'):
validate_html_static_path(app, app.config)
validate_html_static_path(app, app.config)
assert app.config.html_static_path == ['_static']