mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add `sphinx.util._files` (#12766)
This commit is contained in:
@@ -19,6 +19,9 @@ Deprecated
|
||||
* #12762: Deprecate ``sphinx.util.import_object``.
|
||||
Use :py:func:`importlib.import_module` instead.
|
||||
Patch by Adam Turner.
|
||||
* #12766: Deprecate ``sphinx.util.FilenameUniqDict``
|
||||
and ``sphinx.util.DownloadFiles``.
|
||||
Patch by Adam Turner.
|
||||
|
||||
Features added
|
||||
--------------
|
||||
@@ -32,13 +35,11 @@ Bugs fixed
|
||||
* #12514: intersphinx: fix the meaning of a negative value for
|
||||
:confval:`intersphinx_cache_limit`.
|
||||
Patch by Shengyu Zhang.
|
||||
|
||||
* #12730: The ``UnreferencedFootnotesDetector`` transform has been improved
|
||||
to more consistently detect unreferenced footnotes.
|
||||
Note, the priority of the transform has been changed from 200 to 622,
|
||||
so that it now runs after the docutils ``Footnotes`` resolution transform.
|
||||
Patch by Chris Sewell.
|
||||
|
||||
* #12587: Do not warn when potential ambiguity detected during Intersphinx
|
||||
resolution occurs due to duplicate targets that differ case-insensitively.
|
||||
Patch by James Addison.
|
||||
|
||||
@@ -22,6 +22,16 @@ The following is a list of deprecated interfaces.
|
||||
- Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.util.FilenameUniqDict``
|
||||
- 8.1
|
||||
- 10.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.util.DownloadFiles``
|
||||
- 8.1
|
||||
- 10.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.util.import_object``
|
||||
- 8.1
|
||||
- 10.0
|
||||
|
||||
@@ -20,7 +20,8 @@ from sphinx.errors import (
|
||||
)
|
||||
from sphinx.locale import __
|
||||
from sphinx.transforms import SphinxTransformer
|
||||
from sphinx.util import DownloadFiles, FilenameUniqDict, logging
|
||||
from sphinx.util import logging
|
||||
from sphinx.util._files import DownloadFiles, FilenameUniqDict
|
||||
from sphinx.util._timestamps import _format_rfc3339_microseconds
|
||||
from sphinx.util.docutils import LoggingReporter
|
||||
from sphinx.util.i18n import CatalogRepository, docname_to_domain
|
||||
|
||||
@@ -6,13 +6,12 @@ import hashlib
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
from os import path
|
||||
from typing import Any
|
||||
from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit
|
||||
|
||||
from sphinx.errors import FiletypeNotFoundError
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import _importer, logging
|
||||
from sphinx.util import _files, _importer, logging
|
||||
from sphinx.util import index_entries as _index_entries
|
||||
from sphinx.util.console import strip_colors # NoQA: F401
|
||||
from sphinx.util.matching import patfilter # NoQA: F401
|
||||
@@ -55,49 +54,6 @@ def get_filetype(source_suffix: dict[str, str], filename: str | os.PathLike) ->
|
||||
raise FiletypeNotFoundError
|
||||
|
||||
|
||||
class FilenameUniqDict(dict):
|
||||
"""
|
||||
A dictionary that automatically generates unique names for its keys,
|
||||
interpreted as filenames, and keeps track of a set of docnames they
|
||||
appear in. Used for images and downloadable files in the environment.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._existing: set[str] = set()
|
||||
|
||||
def add_file(self, docname: str, newfile: str) -> str:
|
||||
if newfile in self:
|
||||
self[newfile][0].add(docname)
|
||||
return self[newfile][1]
|
||||
uniquename = path.basename(newfile)
|
||||
base, ext = path.splitext(uniquename)
|
||||
i = 0
|
||||
while uniquename in self._existing:
|
||||
i += 1
|
||||
uniquename = f'{base}{i}{ext}'
|
||||
self[newfile] = ({docname}, uniquename)
|
||||
self._existing.add(uniquename)
|
||||
return uniquename
|
||||
|
||||
def purge_doc(self, docname: str) -> None:
|
||||
for filename, (docs, unique) in list(self.items()):
|
||||
docs.discard(docname)
|
||||
if not docs:
|
||||
del self[filename]
|
||||
self._existing.discard(unique)
|
||||
|
||||
def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None:
|
||||
for filename, (docs, _unique) in other.items():
|
||||
for doc in docs & set(docnames):
|
||||
self.add_file(doc, filename)
|
||||
|
||||
def __getstate__(self) -> set[str]:
|
||||
return self._existing
|
||||
|
||||
def __setstate__(self, state: set[str]) -> None:
|
||||
self._existing = state
|
||||
|
||||
|
||||
def _md5(data: bytes = b'', **_kw: Any) -> hashlib._Hash:
|
||||
"""Deprecated wrapper around hashlib.md5
|
||||
|
||||
@@ -114,34 +70,6 @@ def _sha1(data: bytes = b'', **_kw: Any) -> hashlib._Hash:
|
||||
return hashlib.sha1(data, usedforsecurity=False)
|
||||
|
||||
|
||||
class DownloadFiles(dict):
|
||||
"""A special dictionary for download files.
|
||||
|
||||
.. important:: This class would be refactored in nearly future.
|
||||
Hence don't hack this directly.
|
||||
"""
|
||||
|
||||
def add_file(self, docname: str, filename: str) -> str:
|
||||
if filename not in self:
|
||||
digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest()
|
||||
dest = f'{digest}/{os.path.basename(filename)}'
|
||||
self[filename] = (set(), dest)
|
||||
|
||||
self[filename][0].add(docname)
|
||||
return self[filename][1]
|
||||
|
||||
def purge_doc(self, docname: str) -> None:
|
||||
for filename, (docs, _dest) in list(self.items()):
|
||||
docs.discard(docname)
|
||||
if not docs:
|
||||
del self[filename]
|
||||
|
||||
def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None:
|
||||
for filename, (docs, _dest) in other.items():
|
||||
for docname in docs & set(docnames):
|
||||
self.add_file(docname, filename)
|
||||
|
||||
|
||||
class UnicodeDecodeErrorHandler:
|
||||
"""Custom error handler for open() that warns and replaces."""
|
||||
|
||||
@@ -217,6 +145,8 @@ _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
|
||||
'md5': (_md5, '', (9, 0)),
|
||||
'sha1': (_sha1, '', (9, 0)),
|
||||
'import_object': (_importer.import_object, '', (10, 0)),
|
||||
'FilenameUniqDict': (_files.FilenameUniqDict, '', (10, 0)),
|
||||
'DownloadFiles': (_files.DownloadFiles, '', (10, 0)),
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
sphinx/util/_files.py
Normal file
76
sphinx/util/_files.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os.path
|
||||
from typing import Any
|
||||
|
||||
|
||||
class FilenameUniqDict(dict[str, tuple[set[str], str]]):
|
||||
"""
|
||||
A dictionary that automatically generates unique names for its keys,
|
||||
interpreted as filenames, and keeps track of a set of docnames they
|
||||
appear in. Used for images and downloadable files in the environment.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._existing: set[str] = set()
|
||||
|
||||
def add_file(self, docname: str, newfile: str) -> str:
|
||||
if newfile in self:
|
||||
self[newfile][0].add(docname)
|
||||
return self[newfile][1]
|
||||
uniquename = os.path.basename(newfile)
|
||||
base, ext = os.path.splitext(uniquename)
|
||||
i = 0
|
||||
while uniquename in self._existing:
|
||||
i += 1
|
||||
uniquename = f'{base}{i}{ext}'
|
||||
self[newfile] = ({docname}, uniquename)
|
||||
self._existing.add(uniquename)
|
||||
return uniquename
|
||||
|
||||
def purge_doc(self, docname: str) -> None:
|
||||
for filename, (docs, unique) in list(self.items()):
|
||||
docs.discard(docname)
|
||||
if not docs:
|
||||
del self[filename]
|
||||
self._existing.discard(unique)
|
||||
|
||||
def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None:
|
||||
for filename, (docs, _unique) in other.items():
|
||||
for doc in docs & set(docnames):
|
||||
self.add_file(doc, filename)
|
||||
|
||||
def __getstate__(self) -> set[str]:
|
||||
return self._existing
|
||||
|
||||
def __setstate__(self, state: set[str]) -> None:
|
||||
self._existing = state
|
||||
|
||||
|
||||
class DownloadFiles(dict[str, tuple[set[str], str]]):
|
||||
"""A special dictionary for download files.
|
||||
|
||||
.. important:: This class would be refactored in nearly future.
|
||||
Hence don't hack this directly.
|
||||
"""
|
||||
|
||||
def add_file(self, docname: str, filename: str) -> str:
|
||||
if filename not in self:
|
||||
digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest()
|
||||
dest = f'{digest}/{os.path.basename(filename)}'
|
||||
self[filename] = (set(), dest)
|
||||
|
||||
self[filename][0].add(docname)
|
||||
return self[filename][1]
|
||||
|
||||
def purge_doc(self, docname: str) -> None:
|
||||
for filename, (docs, _dest) in list(self.items()):
|
||||
docs.discard(docname)
|
||||
if not docs:
|
||||
del self[filename]
|
||||
|
||||
def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None:
|
||||
for filename, (docs, _dest) in other.items():
|
||||
for docname in docs & set(docnames):
|
||||
self.add_file(docname, filename)
|
||||
Reference in New Issue
Block a user