Deprecate `md5 and sha1 wrappers in sphinx.util` (#11512)

This commit is contained in:
Adam Turner 2023-07-25 02:29:28 +01:00 committed by GitHub
parent f804da4b45
commit 5cf3dce36e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 42 additions and 25 deletions

View File

@ -12,6 +12,9 @@ Incompatible changes
Deprecated Deprecated
---------- ----------
* #11512: Deprecate ``sphinx.util.md5`` and ``sphinx.util.sha1``.
Use ``hashlib`` instead.
Features added Features added
-------------- --------------

View File

@ -22,6 +22,16 @@ The following is a list of deprecated interfaces.
- Removed - Removed
- Alternatives - Alternatives
* - ``sphinx.util.md5``
- 7.2
- 9.0
- ``hashlib.md5``
* - ``sphinx.util.sha1``
- 7.2
- 9.0
- ``hashlib.sha1``
* - ``sphinx.util.osutil.cd`` * - ``sphinx.util.osutil.cd``
- 6.2 - 6.2
- 8.0 - 8.0

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import hashlib
import html import html
import os import os
import posixpath import posixpath
@ -37,7 +38,7 @@ from sphinx.highlighting import PygmentsBridge
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.search import js_index from sphinx.search import js_index
from sphinx.theming import HTMLThemeFactory from sphinx.theming import HTMLThemeFactory
from sphinx.util import isurl, logging, md5 from sphinx.util import isurl, logging
from sphinx.util.display import progress_message, status_iterator from sphinx.util.display import progress_message, status_iterator
from sphinx.util.docutils import new_document from sphinx.util.docutils import new_document
from sphinx.util.fileutil import copy_asset from sphinx.util.fileutil import copy_asset
@ -80,7 +81,7 @@ def get_stable_hash(obj: Any) -> str:
return get_stable_hash(list(obj.items())) return get_stable_hash(list(obj.items()))
elif isinstance(obj, (list, tuple)): elif isinstance(obj, (list, tuple)):
obj = sorted(get_stable_hash(o) for o in obj) obj = sorted(get_stable_hash(o) for o in obj)
return md5(str(obj).encode()).hexdigest() return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest()
def convert_locale_to_language_tag(locale: str | None) -> str | None: def convert_locale_to_language_tag(locale: str | None) -> str | None:

View File

@ -6,6 +6,7 @@ from __future__ import annotations
import posixpath import posixpath
import re import re
import subprocess import subprocess
from hashlib import sha1
from os import path from os import path
from subprocess import CalledProcessError from subprocess import CalledProcessError
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
@ -18,7 +19,7 @@ import sphinx
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging, sha1 from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, SphinxTranslator from sphinx.util.docutils import SphinxDirective, SphinxTranslator
from sphinx.util.i18n import search_image_for_language from sphinx.util.i18n import search_image_for_language
from sphinx.util.nodes import set_source_info from sphinx.util.nodes import set_source_info
@ -62,7 +63,7 @@ class ClickableMapDefinition:
if self.id == '%3': if self.id == '%3':
# graphviz generates wrong ID if graph name not specified # graphviz generates wrong ID if graph name not specified
# https://gitlab.com/graphviz/graphviz/issues/1327 # https://gitlab.com/graphviz/graphviz/issues/1327
hashed = sha1(dot.encode()).hexdigest() hashed = sha1(dot.encode(), usedforsecurity=False).hexdigest()
self.id = 'grapviz%s' % hashed[-10:] self.id = 'grapviz%s' % hashed[-10:]
self.content[0] = self.content[0].replace('%3', self.id) self.content[0] = self.content[0].replace('%3', self.id)
@ -221,7 +222,7 @@ def render_dot(self: SphinxTranslator, code: str, options: dict, format: str,
hashkey = (code + str(options) + str(graphviz_dot) + hashkey = (code + str(options) + str(graphviz_dot) +
str(self.builder.config.graphviz_dot_args)).encode() str(self.builder.config.graphviz_dot_args)).encode()
fname = f'{prefix}-{sha1(hashkey).hexdigest()}.{format}' fname = f'{prefix}-{sha1(hashkey, usedforsecurity=False).hexdigest()}.{format}'
relfn = posixpath.join(self.builder.imgpath, fname) relfn = posixpath.join(self.builder.imgpath, fname)
outfn = path.join(self.builder.outdir, self.builder.imagedir, fname) outfn = path.join(self.builder.outdir, self.builder.imagedir, fname)

View File

@ -7,6 +7,7 @@ import re
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
from hashlib import sha1
from os import path from os import path
from subprocess import CalledProcessError from subprocess import CalledProcessError
from typing import Any from typing import Any
@ -21,7 +22,7 @@ from sphinx.builders import Builder
from sphinx.config import Config from sphinx.config import Config
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging, sha1 from sphinx.util import logging
from sphinx.util.math import get_node_equation_number, wrap_displaymath from sphinx.util.math import get_node_equation_number, wrap_displaymath
from sphinx.util.osutil import ensuredir from sphinx.util.osutil import ensuredir
from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.png import read_png_depth, write_png_depth
@ -239,7 +240,7 @@ def render_math(
self.builder.config, self.builder.config,
self.builder.confdir) self.builder.confdir)
filename = f"{sha1(latex.encode()).hexdigest()}.{image_format}" filename = f"{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}"
generated_path = path.join(self.builder.outdir, self.builder.imagedir, 'math', filename) generated_path = path.join(self.builder.outdir, self.builder.imagedir, 'math', filename)
ensuredir(path.dirname(generated_path)) ensuredir(path.dirname(generated_path))
if path.isfile(generated_path): if path.isfile(generated_path):

View File

@ -31,6 +31,7 @@ LaTeX.
from __future__ import annotations from __future__ import annotations
import builtins import builtins
import hashlib
import inspect import inspect
import re import re
from collections.abc import Iterable from collections.abc import Iterable
@ -52,7 +53,6 @@ from sphinx.ext.graphviz import (
render_dot_latex, render_dot_latex,
render_dot_texinfo, render_dot_texinfo,
) )
from sphinx.util import md5
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec from sphinx.util.typing import OptionSpec
from sphinx.writers.html import HTML5Translator from sphinx.writers.html import HTML5Translator
@ -392,7 +392,7 @@ class InheritanceDiagram(SphinxDirective):
def get_graph_hash(node: inheritance_diagram) -> str: def get_graph_hash(node: inheritance_diagram) -> str:
encoded = (node['content'] + str(node['parts'])).encode() encoded = (node['content'] + str(node['parts'])).encode()
return md5(encoded).hexdigest()[-10:] return hashlib.md5(encoded, usedforsecurity=False).hexdigest()[-10:]
def html_visit_inheritance_diagram(self: HTML5Translator, node: inheritance_diagram) -> None: def html_visit_inheritance_diagram(self: HTML5Translator, node: inheritance_diagram) -> None:

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import os import os
import re import re
from hashlib import sha1
from math import ceil from math import ceil
from typing import Any from typing import Any
@ -12,7 +13,7 @@ from docutils import nodes
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.locale import __ from sphinx.locale import __
from sphinx.transforms import SphinxTransform from sphinx.transforms import SphinxTransform
from sphinx.util import logging, requests, sha1 from sphinx.util import logging, requests
from sphinx.util.http_date import epoch_to_rfc1123, rfc1123_to_epoch from sphinx.util.http_date import epoch_to_rfc1123, rfc1123_to_epoch
from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_uri from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_uri
from sphinx.util.osutil import ensuredir from sphinx.util.osutil import ensuredir
@ -57,13 +58,13 @@ class ImageDownloader(BaseImageConverter):
basename = basename.split('?')[0] basename = basename.split('?')[0]
if basename == '' or len(basename) > MAX_FILENAME_LEN: if basename == '' or len(basename) > MAX_FILENAME_LEN:
filename, ext = os.path.splitext(node['uri']) filename, ext = os.path.splitext(node['uri'])
basename = sha1(filename.encode()).hexdigest() + ext basename = sha1(filename.encode(), usedforsecurity=False).hexdigest() + ext
basename = re.sub(CRITICAL_PATH_CHAR_RE, "_", basename) basename = re.sub(CRITICAL_PATH_CHAR_RE, "_", basename)
dirname = node['uri'].replace('://', '/').translate({ord("?"): "/", dirname = node['uri'].replace('://', '/').translate({ord("?"): "/",
ord("&"): "/"}) ord("&"): "/"})
if len(dirname) > MAX_FILENAME_LEN: if len(dirname) > MAX_FILENAME_LEN:
dirname = sha1(dirname.encode()).hexdigest() dirname = sha1(dirname.encode(), usedforsecurity=False).hexdigest()
ensuredir(os.path.join(self.imagedir, dirname)) ensuredir(os.path.join(self.imagedir, dirname))
path = os.path.join(self.imagedir, dirname, basename) path = os.path.join(self.imagedir, dirname, basename)
@ -125,7 +126,7 @@ class DataURIExtractor(BaseImageConverter):
return return
ensuredir(os.path.join(self.imagedir, 'embeded')) ensuredir(os.path.join(self.imagedir, 'embeded'))
digest = sha1(image.data).hexdigest() digest = sha1(image.data, usedforsecurity=False).hexdigest()
path = os.path.join(self.imagedir, 'embeded', digest + ext) path = os.path.join(self.imagedir, 'embeded', digest + ext)
self.app.env.original_image_uri[path] = node['uri'] self.app.env.original_image_uri[path] = node['uri']

View File

@ -104,21 +104,19 @@ class FilenameUniqDict(dict):
self._existing = state self._existing = state
def md5(data=b'', **kwargs): def _md5(data=b'', **_kw):
"""Wrapper around hashlib.md5 """Deprecated wrapper around hashlib.md5
Call with 'usedforsecurity=False'. To be removed in Sphinx 9.0
""" """
return hashlib.md5(data, usedforsecurity=False) return hashlib.md5(data, usedforsecurity=False)
def sha1(data=b'', **kwargs): def _sha1(data=b'', **_kw):
"""Wrapper around hashlib.sha1 """Deprecated wrapper around hashlib.sha1
Call with 'usedforsecurity=False'. To be removed in Sphinx 9.0
""" """
return hashlib.sha1(data, usedforsecurity=False) return hashlib.sha1(data, usedforsecurity=False)
@ -131,7 +129,7 @@ class DownloadFiles(dict):
def add_file(self, docname: str, filename: str) -> str: def add_file(self, docname: str, filename: str) -> str:
if filename not in self: if filename not in self:
digest = md5(filename.encode()).hexdigest() digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest()
dest = f'{digest}/{os.path.basename(filename)}' dest = f'{digest}/{os.path.basename(filename)}'
self[filename] = (set(), dest) self[filename] = (set(), dest)
@ -349,6 +347,8 @@ _DEPRECATED_OBJECTS = {
'format_exception_cut_frames': (_exceptions.format_exception_cut_frames, 'format_exception_cut_frames': (_exceptions.format_exception_cut_frames,
'sphinx.exceptions.format_exception_cut_frames'), 'sphinx.exceptions.format_exception_cut_frames'),
'xmlname_checker': (_xml_name_checker, 'sphinx.builders.epub3._XML_NAME_PATTERN'), 'xmlname_checker': (_xml_name_checker, 'sphinx.builders.epub3._XML_NAME_PATTERN'),
'md5': (_md5, ''),
'sha1': (_sha1, ''),
} }

View File

@ -1,5 +1,6 @@
"""Test the HTML builder and check output against XPath.""" """Test the HTML builder and check output against XPath."""
import hashlib
import os import os
import re import re
from itertools import chain, cycle from itertools import chain, cycle
@ -12,7 +13,6 @@ from html5lib import HTMLParser
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError from sphinx.errors import ConfigError
from sphinx.testing.util import strip_escseq from sphinx.testing.util import strip_escseq
from sphinx.util import md5
from sphinx.util.inventory import InventoryFile from sphinx.util.inventory import InventoryFile
FIGURE_CAPTION = ".//figure/figcaption/p" FIGURE_CAPTION = ".//figure/figcaption/p"
@ -474,9 +474,9 @@ def test_html_download(app):
@pytest.mark.sphinx('html', testroot='roles-download') @pytest.mark.sphinx('html', testroot='roles-download')
def test_html_download_role(app, status, warning): def test_html_download_role(app, status, warning):
app.build() app.build()
digest = md5(b'dummy.dat').hexdigest() digest = hashlib.md5(b'dummy.dat', usedforsecurity=False).hexdigest()
assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists() assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists()
digest_another = md5(b'another/dummy.dat').hexdigest() digest_another = hashlib.md5(b'another/dummy.dat', usedforsecurity=False).hexdigest()
assert (app.outdir / '_downloads' / digest_another / 'dummy.dat').exists() assert (app.outdir / '_downloads' / digest_another / 'dummy.dat').exists()
content = (app.outdir / 'index.html').read_text(encoding='utf8') content = (app.outdir / 'index.html').read_text(encoding='utf8')