mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Remove `_StrPath` (#12650)
This commit is contained in:
@@ -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
|
||||
----------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__())
|
||||
@@ -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']
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user