1
0
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:
Mariusz Felisiak
2023-01-18 09:46:01 +01:00
committed by GitHub
parent d547171183
commit 3bbe22dafc
38 changed files with 51 additions and 327 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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):

View File

@@ -1,4 +1,4 @@
from django.utils.crypto import md5
from hashlib import md5
TEMPLATE_FRAGMENT_KEY_TEMPLATE = "template.cache.%s.%s"

View File

@@ -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")

View File

@@ -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):

View File

@@ -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

View File

@@ -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(

View File

@@ -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")

View File

@@ -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

View File

@@ -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()

View File

@@ -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,

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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