mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #34233 -- Dropped support for Python 3.8 and 3.9.
This commit is contained in:
@@ -14,7 +14,6 @@ from django.utils.crypto import (
|
||||
RANDOM_STRING_CHARS,
|
||||
constant_time_compare,
|
||||
get_random_string,
|
||||
md5,
|
||||
pbkdf2,
|
||||
)
|
||||
from django.utils.deprecation import RemovedInDjango51Warning
|
||||
@@ -684,7 +683,7 @@ class MD5PasswordHasher(BasePasswordHasher):
|
||||
|
||||
def encode(self, password, salt):
|
||||
self._check_encode_args(password, salt)
|
||||
hash = md5((salt + password).encode()).hexdigest()
|
||||
hash = hashlib.md5((salt + password).encode()).hexdigest()
|
||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
||||
|
||||
def decode(self, encoded):
|
||||
@@ -799,7 +798,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
|
||||
def encode(self, password, salt):
|
||||
if salt != "":
|
||||
raise ValueError("salt must be empty.")
|
||||
return md5(password.encode()).hexdigest()
|
||||
return hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
def decode(self, encoded):
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@ import json
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
from hashlib import md5
|
||||
from urllib.parse import unquote, urldefrag, urlsplit, urlunsplit
|
||||
|
||||
from django.conf import STATICFILES_STORAGE_ALIAS, settings
|
||||
@@ -9,7 +10,6 @@ from django.contrib.staticfiles.utils import check_settings, matches_patterns
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import FileSystemStorage, storages
|
||||
from django.utils.crypto import md5
|
||||
from django.utils.functional import LazyObject
|
||||
|
||||
|
||||
|
||||
2
django/core/cache/backends/filebased.py
vendored
2
django/core/cache/backends/filebased.py
vendored
@@ -6,11 +6,11 @@ import random
|
||||
import tempfile
|
||||
import time
|
||||
import zlib
|
||||
from hashlib import md5
|
||||
|
||||
from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache
|
||||
from django.core.files import locks
|
||||
from django.core.files.move import file_move_safe
|
||||
from django.utils.crypto import md5
|
||||
|
||||
|
||||
class FileBasedCache(BaseCache):
|
||||
|
||||
2
django/core/cache/utils.py
vendored
2
django/core/cache/utils.py
vendored
@@ -1,4 +1,4 @@
|
||||
from django.utils.crypto import md5
|
||||
from hashlib import md5
|
||||
|
||||
TEMPLATE_FRAGMENT_KEY_TEMPLATE = "template.cache.%s.%s"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
from contextlib import aclosing
|
||||
|
||||
from asgiref.sync import ThreadSensitiveContext, sync_to_async
|
||||
|
||||
@@ -19,7 +20,6 @@ from django.http import (
|
||||
parse_cookie,
|
||||
)
|
||||
from django.urls import set_script_prefix
|
||||
from django.utils.asyncio import aclosing
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
logger = logging.getLogger("django.request")
|
||||
|
||||
@@ -275,16 +275,6 @@ def validate_ipv4_address(value):
|
||||
raise ValidationError(
|
||||
_("Enter a valid IPv4 address."), code="invalid", params={"value": value}
|
||||
)
|
||||
else:
|
||||
# Leading zeros are forbidden to avoid ambiguity with the octal
|
||||
# notation. This restriction is included in Python 3.9.5+.
|
||||
# TODO: Remove when dropping support for PY39.
|
||||
if any(octet != "0" and octet[0] == "0" for octet in value.split(".")):
|
||||
raise ValidationError(
|
||||
_("Enter a valid IPv4 address."),
|
||||
code="invalid",
|
||||
params={"value": value},
|
||||
)
|
||||
|
||||
|
||||
def validate_ipv6_address(value):
|
||||
|
||||
@@ -5,22 +5,17 @@ import logging
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
import zoneinfo
|
||||
from collections import deque
|
||||
from contextlib import contextmanager
|
||||
|
||||
from django.db.backends.utils import debug_transaction
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import DEFAULT_DB_ALIAS, DatabaseError, NotSupportedError
|
||||
from django.db.backends import utils
|
||||
from django.db.backends.base.validation import BaseDatabaseValidation
|
||||
from django.db.backends.signals import connection_created
|
||||
from django.db.backends.utils import debug_transaction
|
||||
from django.db.transaction import TransactionManagementError
|
||||
from django.db.utils import DatabaseErrorWrapper
|
||||
from django.utils.asyncio import async_unsafe
|
||||
|
||||
@@ -4,8 +4,9 @@ Implementations of SQL functions for SQLite.
|
||||
import functools
|
||||
import random
|
||||
import statistics
|
||||
import zoneinfo
|
||||
from datetime import timedelta
|
||||
from hashlib import sha1, sha224, sha256, sha384, sha512
|
||||
from hashlib import md5, sha1, sha224, sha256, sha384, sha512
|
||||
from math import (
|
||||
acos,
|
||||
asin,
|
||||
@@ -32,14 +33,8 @@ from django.db.backends.utils import (
|
||||
typecast_timestamp,
|
||||
)
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import md5
|
||||
from django.utils.duration import duration_microseconds
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
|
||||
def register(connection):
|
||||
create_deterministic_function = functools.partial(
|
||||
|
||||
@@ -4,9 +4,9 @@ import functools
|
||||
import logging
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from hashlib import md5
|
||||
|
||||
from django.db import NotSupportedError
|
||||
from django.utils.crypto import md5
|
||||
from django.utils.dateparse import parse_time
|
||||
|
||||
logger = logging.getLogger("django.db.backends")
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import zoneinfo
|
||||
from datetime import datetime
|
||||
from datetime import timezone as datetime_timezone
|
||||
from datetime import tzinfo
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.template import Library, Node, TemplateSyntaxError
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
import ctypes
|
||||
import faulthandler
|
||||
import hashlib
|
||||
import io
|
||||
import itertools
|
||||
import logging
|
||||
@@ -27,7 +28,6 @@ from django.test.utils import setup_databases as _setup_databases
|
||||
from django.test.utils import setup_test_environment
|
||||
from django.test.utils import teardown_databases as _teardown_databases
|
||||
from django.test.utils import teardown_test_environment
|
||||
from django.utils.crypto import new_hash
|
||||
from django.utils.datastructures import OrderedSet
|
||||
|
||||
try:
|
||||
@@ -580,7 +580,7 @@ class Shuffler:
|
||||
|
||||
@classmethod
|
||||
def _hash_text(cls, text):
|
||||
h = new_hash(cls.hash_algorithm, usedforsecurity=False)
|
||||
h = hashlib.new(cls.hash_algorithm, usedforsecurity=False)
|
||||
h.update(text.encode("utf-8"))
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ from django.test.utils import (
|
||||
)
|
||||
from django.utils.deprecation import RemovedInDjango51Warning
|
||||
from django.utils.functional import classproperty
|
||||
from django.utils.version import PY310
|
||||
from django.views.static import serve
|
||||
|
||||
logger = logging.getLogger("django.test")
|
||||
@@ -795,32 +794,6 @@ class SimpleTestCase(unittest.TestCase):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# A similar method is available in Python 3.10+.
|
||||
if not PY310:
|
||||
|
||||
@contextmanager
|
||||
def assertNoLogs(self, logger, level=None):
|
||||
"""
|
||||
Assert no messages are logged on the logger, with at least the
|
||||
given level.
|
||||
"""
|
||||
if isinstance(level, int):
|
||||
level = logging.getLevelName(level)
|
||||
elif level is None:
|
||||
level = "INFO"
|
||||
try:
|
||||
with self.assertLogs(logger, level) as cm:
|
||||
yield
|
||||
except AssertionError as e:
|
||||
msg = e.args[0]
|
||||
expected_msg = (
|
||||
f"no logs of level {level} or higher triggered on {logger}"
|
||||
)
|
||||
if msg != expected_msg:
|
||||
raise e
|
||||
else:
|
||||
self.fail(f"Unexpected logs found: {cm.output!r}")
|
||||
|
||||
def assertFieldOutput(
|
||||
self,
|
||||
fieldclass,
|
||||
|
||||
@@ -37,28 +37,3 @@ def async_unsafe(message):
|
||||
return decorator(func)
|
||||
else:
|
||||
return decorator
|
||||
|
||||
|
||||
try:
|
||||
from contextlib import aclosing
|
||||
except ImportError:
|
||||
# TODO: Remove when dropping support for PY39.
|
||||
from contextlib import AbstractAsyncContextManager
|
||||
|
||||
# Backport of contextlib.aclosing() from Python 3.10. Copyright (C) Python
|
||||
# Software Foundation (see LICENSE.python).
|
||||
class aclosing(AbstractAsyncContextManager):
|
||||
"""
|
||||
Async context manager for safely finalizing an asynchronously
|
||||
cleaned-up resource such as an async generator, calling its
|
||||
``aclose()`` method.
|
||||
"""
|
||||
|
||||
def __init__(self, thing):
|
||||
self.thing = thing
|
||||
|
||||
async def __aenter__(self):
|
||||
return self.thing
|
||||
|
||||
async def __aexit__(self, *exc_info):
|
||||
await self.thing.aclose()
|
||||
|
||||
@@ -16,11 +16,11 @@ An example: i18n middleware would need to distinguish caches by the
|
||||
"""
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from hashlib import md5
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import caches
|
||||
from django.http import HttpResponse, HttpResponseNotModified
|
||||
from django.utils.crypto import md5
|
||||
from django.utils.http import http_date, parse_etags, parse_http_date_safe, quote_etag
|
||||
from django.utils.log import log_response
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
|
||||
@@ -7,7 +7,6 @@ import secrets
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.inspect import func_supports_parameter
|
||||
|
||||
|
||||
class InvalidAlgorithm(ValueError):
|
||||
@@ -75,19 +74,3 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
password = force_bytes(password)
|
||||
salt = force_bytes(salt)
|
||||
return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)
|
||||
|
||||
|
||||
# TODO: Remove when dropping support for PY38. inspect.signature() is used to
|
||||
# detect whether the usedforsecurity argument is available as this fix may also
|
||||
# have been applied by downstream package maintainers to other versions in
|
||||
# their repositories.
|
||||
if func_supports_parameter(hashlib.md5, "usedforsecurity"):
|
||||
md5 = hashlib.md5
|
||||
new_hash = hashlib.new
|
||||
else:
|
||||
|
||||
def md5(data=b"", *, usedforsecurity=True):
|
||||
return hashlib.md5(data)
|
||||
|
||||
def new_hash(hash_algorithm, *, usedforsecurity=True):
|
||||
return hashlib.new(hash_algorithm)
|
||||
|
||||
@@ -4,18 +4,9 @@ import re
|
||||
import unicodedata
|
||||
from binascii import Error as BinasciiError
|
||||
from email.utils import formatdate
|
||||
from urllib.parse import (
|
||||
ParseResult,
|
||||
SplitResult,
|
||||
_coerce_args,
|
||||
_splitnetloc,
|
||||
_splitparams,
|
||||
quote,
|
||||
scheme_chars,
|
||||
unquote,
|
||||
)
|
||||
from urllib.parse import quote, unquote
|
||||
from urllib.parse import urlencode as original_urlencode
|
||||
from urllib.parse import uses_params
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
@@ -47,10 +38,6 @@ ASCTIME_DATE = _lazy_re_compile(r"^\w{3} %s %s %s %s$" % (__M, __D2, __T, __Y))
|
||||
RFC3986_GENDELIMS = ":/?#[]@"
|
||||
RFC3986_SUBDELIMS = "!$&'()*+,;="
|
||||
|
||||
# TODO: Remove when dropping support for PY38.
|
||||
# Unsafe bytes to be removed per WHATWG spec.
|
||||
_UNSAFE_URL_BYTES_TO_REMOVE = ["\t", "\r", "\n"]
|
||||
|
||||
|
||||
def urlencode(query, doseq=False):
|
||||
"""
|
||||
@@ -283,74 +270,13 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
|
||||
)
|
||||
|
||||
|
||||
# TODO: Remove when dropping support for PY38.
|
||||
# Copied from urllib.parse.urlparse() but uses fixed urlsplit() function.
|
||||
def _urlparse(url, scheme="", allow_fragments=True):
|
||||
"""Parse a URL into 6 components:
|
||||
<scheme>://<netloc>/<path>;<params>?<query>#<fragment>
|
||||
Return a 6-tuple: (scheme, netloc, path, params, query, fragment).
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
url, scheme, _coerce_result = _coerce_args(url, scheme)
|
||||
splitresult = _urlsplit(url, scheme, allow_fragments)
|
||||
scheme, netloc, url, query, fragment = splitresult
|
||||
if scheme in uses_params and ";" in url:
|
||||
url, params = _splitparams(url)
|
||||
else:
|
||||
params = ""
|
||||
result = ParseResult(scheme, netloc, url, params, query, fragment)
|
||||
return _coerce_result(result)
|
||||
|
||||
|
||||
# TODO: Remove when dropping support for PY38.
|
||||
def _remove_unsafe_bytes_from_url(url):
|
||||
for b in _UNSAFE_URL_BYTES_TO_REMOVE:
|
||||
url = url.replace(b, "")
|
||||
return url
|
||||
|
||||
|
||||
# TODO: Remove when dropping support for PY38.
|
||||
# Backport of urllib.parse.urlsplit() from Python 3.9.
|
||||
def _urlsplit(url, scheme="", allow_fragments=True):
|
||||
"""Parse a URL into 5 components:
|
||||
<scheme>://<netloc>/<path>?<query>#<fragment>
|
||||
Return a 5-tuple: (scheme, netloc, path, query, fragment).
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
url, scheme, _coerce_result = _coerce_args(url, scheme)
|
||||
url = _remove_unsafe_bytes_from_url(url)
|
||||
scheme = _remove_unsafe_bytes_from_url(scheme)
|
||||
|
||||
netloc = query = fragment = ""
|
||||
i = url.find(":")
|
||||
if i > 0:
|
||||
for c in url[:i]:
|
||||
if c not in scheme_chars:
|
||||
break
|
||||
else:
|
||||
scheme, url = url[:i].lower(), url[i + 1 :]
|
||||
|
||||
if url[:2] == "//":
|
||||
netloc, url = _splitnetloc(url, 2)
|
||||
if ("[" in netloc and "]" not in netloc) or (
|
||||
"]" in netloc and "[" not in netloc
|
||||
):
|
||||
raise ValueError("Invalid IPv6 URL")
|
||||
if allow_fragments and "#" in url:
|
||||
url, fragment = url.split("#", 1)
|
||||
if "?" in url:
|
||||
url, query = url.split("?", 1)
|
||||
v = SplitResult(scheme, netloc, url, query, fragment)
|
||||
return _coerce_result(v)
|
||||
|
||||
|
||||
def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
|
||||
# Chrome considers any URL with more than two slashes to be absolute, but
|
||||
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
|
||||
if url.startswith("///"):
|
||||
return False
|
||||
try:
|
||||
url_info = _urlparse(url)
|
||||
url_info = urlparse(url)
|
||||
except ValueError: # e.g. invalid IPv6 addresses
|
||||
return False
|
||||
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
|
||||
|
||||
@@ -3,12 +3,7 @@ Timezone-related classes and functions.
|
||||
"""
|
||||
|
||||
import functools
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
import zoneinfo
|
||||
from contextlib import ContextDecorator
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
|
||||
|
||||
Reference in New Issue
Block a user