mirror of https://github.com/zulip/zulip.git
requirements: Upgrade to Django 4.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
59174694af
commit
81892df176
|
@ -1,9 +1,9 @@
|
|||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from html import escape
|
||||
from typing import Any, Collection, Dict, List, Optional, Sequence
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.db.backends.utils import CursorWrapper
|
||||
from django.template import loader
|
||||
|
@ -12,7 +12,12 @@ from markupsafe import Markup as mark_safe
|
|||
|
||||
from zerver.models import UserActivity
|
||||
|
||||
eastern_tz = pytz.timezone("US/Eastern")
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
eastern_tz = zoneinfo.ZoneInfo("America/New_York")
|
||||
|
||||
|
||||
if settings.BILLING_ENABLED:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# and requirements/prod.txt.
|
||||
# See requirements/README.md for more detail.
|
||||
# Django itself
|
||||
Django[argon2]==3.2.*
|
||||
Django[argon2]==4.0.*
|
||||
|
||||
# needed for NotRequired, ParamSpec
|
||||
typing-extensions
|
||||
|
@ -77,7 +77,7 @@ django-bmemcached
|
|||
python-dateutil
|
||||
|
||||
# Needed for time zone work
|
||||
pytz
|
||||
backports.zoneinfo ; python_version < "3.9"
|
||||
|
||||
# Needed for Redis
|
||||
redis
|
||||
|
|
|
@ -93,6 +93,26 @@ backoff-stubs==1.11.1 \
|
|||
--hash=sha256:3fd641261cfe9cd657ebb7fc8a1dc700efa3f1b63e82fe0235d74bb73f8b85da \
|
||||
--hash=sha256:8b56cf2cfaf64abc1623544bd725b21566b5b92cf790a97d33e7437fb131251e
|
||||
# via -r requirements/mypy.in
|
||||
backports.zoneinfo==0.2.1 ; python_version < "3.9" \
|
||||
--hash=sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf \
|
||||
--hash=sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328 \
|
||||
--hash=sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546 \
|
||||
--hash=sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6 \
|
||||
--hash=sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570 \
|
||||
--hash=sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9 \
|
||||
--hash=sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7 \
|
||||
--hash=sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987 \
|
||||
--hash=sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722 \
|
||||
--hash=sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582 \
|
||||
--hash=sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc \
|
||||
--hash=sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b \
|
||||
--hash=sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1 \
|
||||
--hash=sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08 \
|
||||
--hash=sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac \
|
||||
--hash=sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# django
|
||||
beautifulsoup4==4.11.1 \
|
||||
--hash=sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30 \
|
||||
--hash=sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693
|
||||
|
@ -439,9 +459,9 @@ distro==1.7.0 \
|
|||
--hash=sha256:151aeccf60c216402932b52e40ee477a939f8d58898927378a02abbe852c1c39 \
|
||||
--hash=sha256:d596311d707e692c2160c37807f83e3820c5d539d5a83e87cfb6babd8ba3a06b
|
||||
# via zulip
|
||||
django[argon2]==3.2.14 \
|
||||
--hash=sha256:677182ba8b5b285a4e072f3ac17ceee6aff1b5ce77fd173cc5b6a2d3dc022fcf \
|
||||
--hash=sha256:a8681e098fa60f7c33a4b628d6fcd3fe983a0939ff1301ecacac21d0b38bad56
|
||||
django[argon2]==4.0.6 \
|
||||
--hash=sha256:a67a793ff6827fd373555537dca0da293a63a316fe34cb7f367f898ccca3c3ae \
|
||||
--hash=sha256:ca54ebedfcbc60d191391efbf02ba68fb52165b8bf6ccd6fe71f098cac1fe59e
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# django-auth-ldap
|
||||
|
@ -456,9 +476,8 @@ django-auth-ldap==4.1.0 \
|
|||
--hash=sha256:68870e7921e84b1a9867e268a9c8a3e573e8a0d95ea08bcf31be178f5826ff36 \
|
||||
--hash=sha256:77f749d3b17807ce8eb56a9c9c8e5746ff316567f81d5ba613495d9c7495a949
|
||||
# via -r requirements/common.in
|
||||
django-bitfield==2.1.0 \
|
||||
--hash=sha256:158f1056e22cce450d0a49633ea77bfd84b85a2294b1ef03faa775a485f4065d \
|
||||
--hash=sha256:a55859fd16ce4269d5ceed3e20cf8fc3c2df866f0a78b90c60a19a0e76aa5fd8
|
||||
django-bitfield==2.2.0 \
|
||||
--hash=sha256:1b21262acc4ec0af3f82ed04498a056cd9d5452532ac02771e004835a34e0b1b
|
||||
# via -r requirements/common.in
|
||||
django-bmemcached==0.3.0 \
|
||||
--hash=sha256:4e4b7d97216dbae331c1de10e699ca22804b94ec3a90d2762dd5d146e6986a8a
|
||||
|
@ -1606,9 +1625,7 @@ pytz==2022.1 \
|
|||
--hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \
|
||||
--hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# babel
|
||||
# django
|
||||
# moto
|
||||
# twilio
|
||||
pyuca==1.2 \
|
||||
|
@ -2136,10 +2153,6 @@ types-python-dateutil==2.8.18 \
|
|||
--hash=sha256:8695c7d7a5b1aef4002f3ab4e1247e23b1d41cd7cc1286d4594c2d8c5593c991 \
|
||||
--hash=sha256:fd5ed97262b76ae684695ea38ace8dd7c1bc9491aba7eb4edf6654b7ecabc870
|
||||
# via -r requirements/mypy.in
|
||||
types-pytz==2022.1.1 \
|
||||
--hash=sha256:4e7add70886dc2ee6ee7535c8184a26eeb0ac9dbafae9962cb882d74b9f67330 \
|
||||
--hash=sha256:581467742f32f15fff1098698b11fd511057a2a8a7568d33b604083f2b03c24f
|
||||
# via -r requirements/mypy.in
|
||||
types-pyyaml==6.0.9 \
|
||||
--hash=sha256:33ae75c84b8f61fddf0c63e9c7e557db9db1694ad3c2ee8628ec5efebb5a5e9b \
|
||||
--hash=sha256:b738e9ef120da0af8c235ba49d3b72510f56ef9bcc308fc8e7357100ff122284
|
||||
|
|
|
@ -24,7 +24,6 @@ types-Pillow
|
|||
types-psycopg2
|
||||
types-Pygments
|
||||
types-python-dateutil
|
||||
types-pytz
|
||||
types-PyYAML
|
||||
types-redis
|
||||
types-requests
|
||||
|
|
|
@ -245,10 +245,6 @@ types-python-dateutil==2.8.18 \
|
|||
--hash=sha256:8695c7d7a5b1aef4002f3ab4e1247e23b1d41cd7cc1286d4594c2d8c5593c991 \
|
||||
--hash=sha256:fd5ed97262b76ae684695ea38ace8dd7c1bc9491aba7eb4edf6654b7ecabc870
|
||||
# via -r requirements/mypy.in
|
||||
types-pytz==2022.1.1 \
|
||||
--hash=sha256:4e7add70886dc2ee6ee7535c8184a26eeb0ac9dbafae9962cb882d74b9f67330 \
|
||||
--hash=sha256:581467742f32f15fff1098698b11fd511057a2a8a7568d33b604083f2b03c24f
|
||||
# via -r requirements/mypy.in
|
||||
types-pyyaml==6.0.9 \
|
||||
--hash=sha256:33ae75c84b8f61fddf0c63e9c7e557db9db1694ad3c2ee8628ec5efebb5a5e9b \
|
||||
--hash=sha256:b738e9ef120da0af8c235ba49d3b72510f56ef9bcc308fc8e7357100ff122284
|
||||
|
|
|
@ -64,6 +64,26 @@ backoff==2.1.2 \
|
|||
--hash=sha256:407f1bc0f22723648a8880821b935ce5df8475cf04f7b6b5017ae264d30f6069 \
|
||||
--hash=sha256:b135e6d7c7513ba2bfd6895bc32bc8c66c6f3b0279b4c6cd866053cfd7d3126b
|
||||
# via -r requirements/common.in
|
||||
backports.zoneinfo==0.2.1 ; python_version < "3.9" \
|
||||
--hash=sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf \
|
||||
--hash=sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328 \
|
||||
--hash=sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546 \
|
||||
--hash=sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6 \
|
||||
--hash=sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570 \
|
||||
--hash=sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9 \
|
||||
--hash=sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7 \
|
||||
--hash=sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987 \
|
||||
--hash=sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722 \
|
||||
--hash=sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582 \
|
||||
--hash=sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc \
|
||||
--hash=sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b \
|
||||
--hash=sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1 \
|
||||
--hash=sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08 \
|
||||
--hash=sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac \
|
||||
--hash=sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# django
|
||||
beautifulsoup4==4.11.1 \
|
||||
--hash=sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30 \
|
||||
--hash=sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693
|
||||
|
@ -272,9 +292,9 @@ distro==1.7.0 \
|
|||
--hash=sha256:151aeccf60c216402932b52e40ee477a939f8d58898927378a02abbe852c1c39 \
|
||||
--hash=sha256:d596311d707e692c2160c37807f83e3820c5d539d5a83e87cfb6babd8ba3a06b
|
||||
# via zulip
|
||||
django[argon2]==3.2.14 \
|
||||
--hash=sha256:677182ba8b5b285a4e072f3ac17ceee6aff1b5ce77fd173cc5b6a2d3dc022fcf \
|
||||
--hash=sha256:a8681e098fa60f7c33a4b628d6fcd3fe983a0939ff1301ecacac21d0b38bad56
|
||||
django[argon2]==4.0.6 \
|
||||
--hash=sha256:a67a793ff6827fd373555537dca0da293a63a316fe34cb7f367f898ccca3c3ae \
|
||||
--hash=sha256:ca54ebedfcbc60d191391efbf02ba68fb52165b8bf6ccd6fe71f098cac1fe59e
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# django-auth-ldap
|
||||
|
@ -289,9 +309,8 @@ django-auth-ldap==4.1.0 \
|
|||
--hash=sha256:68870e7921e84b1a9867e268a9c8a3e573e8a0d95ea08bcf31be178f5826ff36 \
|
||||
--hash=sha256:77f749d3b17807ce8eb56a9c9c8e5746ff316567f81d5ba613495d9c7495a949
|
||||
# via -r requirements/common.in
|
||||
django-bitfield==2.1.0 \
|
||||
--hash=sha256:158f1056e22cce450d0a49633ea77bfd84b85a2294b1ef03faa775a485f4065d \
|
||||
--hash=sha256:a55859fd16ce4269d5ceed3e20cf8fc3c2df866f0a78b90c60a19a0e76aa5fd8
|
||||
django-bitfield==2.2.0 \
|
||||
--hash=sha256:1b21262acc4ec0af3f82ed04498a056cd9d5452532ac02771e004835a34e0b1b
|
||||
# via -r requirements/common.in
|
||||
django-bmemcached==0.3.0 \
|
||||
--hash=sha256:4e4b7d97216dbae331c1de10e699ca22804b94ec3a90d2762dd5d146e6986a8a
|
||||
|
@ -1085,10 +1104,7 @@ python3-saml==1.14.0 \
|
|||
pytz==2022.1 \
|
||||
--hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \
|
||||
--hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c
|
||||
# via
|
||||
# -r requirements/common.in
|
||||
# django
|
||||
# twilio
|
||||
# via twilio
|
||||
pyuca==1.2 \
|
||||
--hash=sha256:8a382fe74627f08c0d18908c0713ca4a20aad5385f077579e56208beea2893b2 \
|
||||
--hash=sha256:abaa12e1bd2c7c68ca8396ff8383bc0654a739cef3ae68fd7af58bf29af0a91e
|
||||
|
|
|
@ -16,7 +16,7 @@ import subprocess
|
|||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Dict, List, Sequence, Set
|
||||
from typing import IO, Any, Dict, List, Sequence, Set
|
||||
from urllib.parse import SplitResult
|
||||
|
||||
DEPLOYMENTS_DIR = "/home/zulip/deployments"
|
||||
|
@ -445,6 +445,20 @@ def os_families() -> Set[str]:
|
|||
return {distro_info["ID"], *distro_info.get("ID_LIKE", "").split()}
|
||||
|
||||
|
||||
def get_tzdata_zi() -> IO[str]:
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
for path in zoneinfo.TZPATH:
|
||||
filename = os.path.join(path, "tzdata.zi")
|
||||
if os.path.exists(filename):
|
||||
return open(filename)
|
||||
else:
|
||||
raise RuntimeError("Missing time zone data (tzdata.zi)")
|
||||
|
||||
|
||||
def files_and_string_digest(filenames: Sequence[str], extra_strings: Sequence[str]) -> str:
|
||||
# see is_digest_obsolete for more context
|
||||
sha1sum = hashlib.sha1()
|
||||
|
|
|
@ -10,13 +10,13 @@ ZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__f
|
|||
|
||||
sys.path.append(ZULIP_PATH)
|
||||
import pygments
|
||||
from pytz import VERSION as timezones_version
|
||||
|
||||
from scripts.lib import clean_unused_caches
|
||||
from scripts.lib.zulip_tools import (
|
||||
ENDC,
|
||||
OKBLUE,
|
||||
get_dev_uuid_var_path,
|
||||
get_tzdata_zi,
|
||||
is_digest_obsolete,
|
||||
run,
|
||||
run_as_root,
|
||||
|
@ -28,6 +28,11 @@ from version import PROVISION_VERSION
|
|||
VENV_PATH = "/srv/zulip-py3-venv"
|
||||
UUID_VAR_PATH = get_dev_uuid_var_path()
|
||||
|
||||
with get_tzdata_zi() as f:
|
||||
line = f.readline()
|
||||
assert line.startswith("# version ")
|
||||
timezones_version = line[len("# version ") :]
|
||||
|
||||
|
||||
def create_var_directories() -> None:
|
||||
# create var/coverage, var/log, etc.
|
||||
|
|
|
@ -3,7 +3,10 @@ import json
|
|||
import os
|
||||
import sys
|
||||
|
||||
import pytz
|
||||
if sys.version_info < (3, 9):
|
||||
from backports import zoneinfo
|
||||
else:
|
||||
import zoneinfo
|
||||
|
||||
ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
|
||||
sys.path.insert(0, ZULIP_PATH)
|
||||
|
@ -13,4 +16,13 @@ from zerver.lib.timezone import get_canonical_timezone_map
|
|||
OUT_PATH = os.path.join(ZULIP_PATH, "static", "generated", "timezones.json")
|
||||
|
||||
with open(OUT_PATH, "w") as f:
|
||||
json.dump({"timezones": sorted(pytz.all_timezones_set - set(get_canonical_timezone_map()))}, f)
|
||||
json.dump(
|
||||
{
|
||||
"timezones": sorted(
|
||||
zoneinfo.available_timezones()
|
||||
- {"Factory", "localtime"}
|
||||
- set(get_canonical_timezone_map())
|
||||
)
|
||||
},
|
||||
f,
|
||||
)
|
||||
|
|
|
@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 132
|
|||
# historical commits sharing the same major version, in which case a
|
||||
# minor version bump suffices.
|
||||
|
||||
PROVISION_VERSION = "194.0"
|
||||
PROVISION_VERSION = "195.0"
|
||||
|
|
|
@ -3,7 +3,6 @@ import os
|
|||
import re
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
|
@ -16,7 +15,7 @@ from zerver.signals import get_device_browser
|
|||
if settings.PRODUCTION: # nocoverage
|
||||
timestamp = os.path.basename(os.path.abspath(settings.DEPLOY_ROOT))
|
||||
LAST_SERVER_UPGRADE_TIME = datetime.datetime.strptime(timestamp, "%Y-%m-%d-%H-%M-%S").replace(
|
||||
tzinfo=pytz.utc
|
||||
tzinfo=datetime.timezone.utc
|
||||
)
|
||||
else:
|
||||
LAST_SERVER_UPGRADE_TIME = timezone_now()
|
||||
|
@ -31,7 +30,7 @@ def is_outdated_server(user_profile: Optional[UserProfile]) -> bool:
|
|||
git_version_path = os.path.join(settings.DEPLOY_ROOT, "version.py")
|
||||
release_build_time = datetime.datetime.utcfromtimestamp(
|
||||
os.path.getmtime(git_version_path)
|
||||
).replace(tzinfo=pytz.utc)
|
||||
).replace(tzinfo=datetime.timezone.utc)
|
||||
|
||||
version_no_newer_than = min(LAST_SERVER_UPGRADE_TIME, release_build_time)
|
||||
deadline = version_no_newer_than + datetime.timedelta(
|
||||
|
|
|
@ -11,7 +11,6 @@ from email.headerregistry import Address
|
|||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
import lxml.html
|
||||
import pytz
|
||||
from bs4 import BeautifulSoup
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_backends
|
||||
|
@ -46,6 +45,11 @@ from zerver.models import (
|
|||
get_user_profile_by_id,
|
||||
)
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -620,7 +624,7 @@ def followup_day2_email_delay(user: UserProfile) -> timedelta:
|
|||
user_tz = user.timezone
|
||||
if user_tz == "":
|
||||
user_tz = "UTC"
|
||||
signup_day = user.date_joined.astimezone(pytz.timezone(user_tz)).isoweekday()
|
||||
signup_day = user.date_joined.astimezone(zoneinfo.ZoneInfo(user_tz)).isoweekday()
|
||||
if signup_day == 5:
|
||||
# If the day is Friday then delay should be till Monday
|
||||
days_to_delay = 3
|
||||
|
|
|
@ -102,7 +102,7 @@ def process_instrumented_calls(func: Callable[[Dict[str, Any]], None]) -> None:
|
|||
|
||||
|
||||
SerializedSubsuite = Tuple[Type[TestSuite], List[str]]
|
||||
SubsuiteArgs = Tuple[Type["RemoteTestRunner"], int, SerializedSubsuite, bool]
|
||||
SubsuiteArgs = Tuple[Type["RemoteTestRunner"], int, SerializedSubsuite, bool, bool]
|
||||
|
||||
|
||||
def run_subsuite(args: SubsuiteArgs) -> Tuple[int, Any]:
|
||||
|
@ -110,8 +110,8 @@ def run_subsuite(args: SubsuiteArgs) -> Tuple[int, Any]:
|
|||
test_helpers.INSTRUMENTED_CALLS = []
|
||||
# The first argument is the test runner class but we don't need it
|
||||
# because we run our own version of the runner class.
|
||||
_, subsuite_index, subsuite, failfast = args
|
||||
runner = RemoteTestRunner(failfast=failfast)
|
||||
_, subsuite_index, subsuite, failfast, buffer = args
|
||||
runner = RemoteTestRunner(failfast=failfast, buffer=buffer)
|
||||
result = runner.run(deserialize_suite(subsuite))
|
||||
# Now we send instrumentation related events. This data will be
|
||||
# appended to the data structure in the main thread. For Mypy,
|
||||
|
@ -237,8 +237,14 @@ class ParallelTestSuite(django_runner.ParallelTestSuite):
|
|||
run_subsuite = run_subsuite
|
||||
init_worker = init_worker
|
||||
|
||||
def __init__(self, suite: TestSuite, processes: int, failfast: bool) -> None:
|
||||
super().__init__(suite, processes, failfast)
|
||||
def __init__(
|
||||
self,
|
||||
subsuites: List[TestSuite],
|
||||
processes: int,
|
||||
failfast: bool = False,
|
||||
buffer: bool = False,
|
||||
) -> None:
|
||||
super().__init__(subsuites=subsuites, processes=processes, failfast=failfast, buffer=buffer)
|
||||
# We can't specify a consistent type for self.subsuites, since
|
||||
# the whole idea here is to monkey-patch that so we can use
|
||||
# most of django_runner.ParallelTestSuite with our own suite
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
from functools import lru_cache
|
||||
from io import TextIOWrapper
|
||||
from typing import Dict
|
||||
|
||||
import pytz
|
||||
from scripts.lib.zulip_tools import get_tzdata_zi
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def get_canonical_timezone_map() -> Dict[str, str]:
|
||||
canonical = {}
|
||||
with TextIOWrapper(
|
||||
pytz.open_resource("tzdata.zi") # type: ignore[attr-defined] # Unclear if this is part of the public pytz API
|
||||
) as f:
|
||||
with get_tzdata_zi() as f:
|
||||
for line in f:
|
||||
if line.startswith("L "):
|
||||
l, name, alias = line.split()
|
||||
|
|
|
@ -28,6 +28,7 @@ for any particular type of object.
|
|||
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
@ -49,7 +50,6 @@ from typing import (
|
|||
)
|
||||
|
||||
import orjson
|
||||
import pytz
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator, validate_email
|
||||
from django.utils.translation import gettext as _
|
||||
|
@ -58,6 +58,11 @@ from zerver.lib.exceptions import InvalidJSONError, JsonableError
|
|||
from zerver.lib.timezone import canonicalize_timezone
|
||||
from zerver.lib.types import ProfileFieldData, Validator
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
||||
|
||||
|
@ -127,6 +132,17 @@ def check_long_string(var_name: str, val: object) -> str:
|
|||
return check_capped_string(500)(var_name, val)
|
||||
|
||||
|
||||
def check_timezone(var_name: str, val: object) -> str:
|
||||
s = check_string(var_name, val)
|
||||
try:
|
||||
zoneinfo.ZoneInfo(s)
|
||||
except (ValueError, zoneinfo.ZoneInfoNotFoundError):
|
||||
raise ValidationError(
|
||||
_("{var_name} is not a recognized time zone").format(var_name=var_name)
|
||||
)
|
||||
return s
|
||||
|
||||
|
||||
def check_date(var_name: str, val: object) -> str:
|
||||
if not isinstance(val, str):
|
||||
raise ValidationError(_("{var_name} is not a string").format(var_name=var_name))
|
||||
|
@ -564,10 +580,12 @@ def to_decimal(var_name: str, s: str) -> Decimal:
|
|||
|
||||
|
||||
def to_timezone_or_empty(var_name: str, s: str) -> str:
|
||||
if s in pytz.all_timezones_set:
|
||||
return canonicalize_timezone(s)
|
||||
else:
|
||||
try:
|
||||
zoneinfo.ZoneInfo(s)
|
||||
except (ValueError, zoneinfo.ZoneInfoNotFoundError):
|
||||
return ""
|
||||
else:
|
||||
return canonicalize_timezone(s)
|
||||
|
||||
|
||||
def to_converted_or_fallback(
|
||||
|
|
|
@ -1810,7 +1810,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
|
|||
)
|
||||
default_all_public_streams: bool = models.BooleanField(default=False)
|
||||
|
||||
# A time zone name from the `tzdata` database, as found in pytz.all_timezones.
|
||||
# A time zone name from the `tzdata` database, as found in zoneinfo.available_timezones().
|
||||
#
|
||||
# The longest existing name is 32 characters long, so max_length=40 seems
|
||||
# like a safe choice.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
||||
from django.dispatch import receiver
|
||||
|
@ -13,6 +13,11 @@ from zerver.lib.queue import queue_json_publish
|
|||
from zerver.lib.send_email import FromAddress
|
||||
from zerver.models import UserProfile
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
JUST_CREATED_THRESHOLD = 60
|
||||
|
||||
|
||||
|
@ -81,7 +86,7 @@ def email_on_new_login(sender: Any, user: UserProfile, request: Any, **kwargs: A
|
|||
user_tz = user.timezone
|
||||
if user_tz == "":
|
||||
user_tz = timezone_get_current_timezone_name()
|
||||
local_time = timezone_now().astimezone(pytz.timezone(user_tz))
|
||||
local_time = timezone_now().astimezone(zoneinfo.ZoneInfo(user_tz))
|
||||
if user.twenty_four_hour_time:
|
||||
hhmm_string = local_time.strftime("%H:%M")
|
||||
else:
|
||||
|
|
|
@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Any
|
|||
from unittest.mock import patch
|
||||
|
||||
import orjson
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
@ -878,7 +877,7 @@ class HomeTest(ZulipTestCase):
|
|||
# Check when server_upgrade_nag_deadline > last_server_upgrade_time
|
||||
hamlet = self.example_user("hamlet")
|
||||
iago = self.example_user("iago")
|
||||
now = LAST_SERVER_UPGRADE_TIME.replace(tzinfo=pytz.utc)
|
||||
now = LAST_SERVER_UPGRADE_TIME.replace(tzinfo=datetime.timezone.utc)
|
||||
with patch("zerver.lib.compatibility.timezone_now", return_value=now + timedelta(days=10)):
|
||||
self.assertEqual(is_outdated_server(iago), False)
|
||||
self.assertEqual(is_outdated_server(hamlet), False)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import datetime
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Any, List, Mapping, Optional, Set
|
||||
from unittest import mock
|
||||
|
||||
import orjson
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.test import override_settings
|
||||
|
@ -65,6 +65,11 @@ from zerver.models import (
|
|||
)
|
||||
from zerver.views.message_send import InvalidMirrorInput
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
||||
|
||||
|
@ -1432,8 +1437,8 @@ class ScheduledMessageTest(ZulipTestCase):
|
|||
message = self.last_scheduled_message()
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(message.content, "Test message 6")
|
||||
local_tz = pytz.timezone(tz_guess)
|
||||
utz_defer_until = local_tz.normalize(local_tz.localize(defer_until))
|
||||
local_tz = zoneinfo.ZoneInfo(tz_guess)
|
||||
utz_defer_until = defer_until.replace(tzinfo=local_tz)
|
||||
self.assertEqual(message.scheduled_timestamp, convert_to_UTC(utz_defer_until))
|
||||
self.assertEqual(message.delivery_type, ScheduledMessage.SEND_LATER)
|
||||
|
||||
|
@ -1446,8 +1451,8 @@ class ScheduledMessageTest(ZulipTestCase):
|
|||
message = self.last_scheduled_message()
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(message.content, "Test message 7")
|
||||
local_tz = pytz.timezone(user.timezone)
|
||||
utz_defer_until = local_tz.normalize(local_tz.localize(defer_until))
|
||||
local_tz = zoneinfo.ZoneInfo(user.timezone)
|
||||
utz_defer_until = defer_until.replace(tzinfo=local_tz)
|
||||
self.assertEqual(message.scheduled_timestamp, convert_to_UTC(utz_defer_until))
|
||||
self.assertEqual(message.delivery_type, ScheduledMessage.SEND_LATER)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import datetime
|
||||
import sys
|
||||
from typing import Sequence
|
||||
from unittest import mock
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
from django.test import override_settings
|
||||
|
@ -16,6 +16,11 @@ from zerver.lib.test_classes import ZulipTestCase
|
|||
from zerver.models import Message, Realm, Recipient, Stream, UserProfile, get_realm
|
||||
from zerver.signals import JUST_CREATED_THRESHOLD, get_device_browser, get_device_os
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
|
||||
class SendLoginEmailTest(ZulipTestCase):
|
||||
"""
|
||||
|
@ -47,7 +52,7 @@ class SendLoginEmailTest(ZulipTestCase):
|
|||
firefox_windows = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
|
||||
)
|
||||
user_tz = pytz.timezone(user.timezone)
|
||||
user_tz = zoneinfo.ZoneInfo(user.timezone)
|
||||
mock_time = datetime.datetime(year=2018, month=1, day=1, tzinfo=datetime.timezone.utc)
|
||||
reference_time = mock_time.astimezone(user_tz).strftime("%A, %B %d, %Y at %I:%M%p %Z")
|
||||
with mock.patch("zerver.signals.timezone_now", return_value=mock_time):
|
||||
|
|
|
@ -410,6 +410,8 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||
expected_error_msg = f"Invalid {setting_name}"
|
||||
if setting_name == "notification_sound":
|
||||
expected_error_msg = f"Invalid notification sound '{invalid_value}'"
|
||||
elif setting_name == "timezone":
|
||||
expected_error_msg = "timezone is not a recognized time zone"
|
||||
self.assert_json_error(result, expected_error_msg)
|
||||
hamlet = self.example_user("hamlet")
|
||||
self.assertNotEqual(getattr(hamlet, setting_name), invalid_value)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.timezone import canonicalize_timezone, common_timezones
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
|
||||
class TimeZoneTest(ZulipTestCase):
|
||||
def test_canonicalize_timezone(self) -> None:
|
||||
|
@ -32,10 +37,11 @@ class TimeZoneTest(ZulipTestCase):
|
|||
now = timezone_now()
|
||||
dates = [datetime(now.year, 6, 21), datetime(now.year, 12, 21)]
|
||||
extra = {*common_timezones.items(), *ambiguous_abbrevs}
|
||||
for name in pytz.all_timezones:
|
||||
tz = pytz.timezone(name)
|
||||
for name in zoneinfo.available_timezones():
|
||||
tz = zoneinfo.ZoneInfo(name)
|
||||
for date in dates:
|
||||
abbrev = tz.tzname(date)
|
||||
assert abbrev is not None
|
||||
if abbrev.startswith(("-", "+")):
|
||||
continue
|
||||
delta = tz.utcoffset(date)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
from typing import Iterable, Optional, Sequence, Union, cast
|
||||
|
||||
import pytz
|
||||
from dateutil.parser import parse as dateparser
|
||||
from django.core import validators
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -36,6 +36,11 @@ from zerver.models import (
|
|||
get_user_including_cross_realm,
|
||||
)
|
||||
|
||||
if sys.version_info < (3, 9): # nocoverage
|
||||
from backports import zoneinfo
|
||||
else: # nocoverage
|
||||
import zoneinfo
|
||||
|
||||
|
||||
class InvalidMirrorInput(Exception):
|
||||
pass
|
||||
|
@ -159,8 +164,8 @@ def handle_deferred_message(
|
|||
|
||||
deliver_at_usertz = deliver_at
|
||||
if deliver_at_usertz.tzinfo is None:
|
||||
user_tz = pytz.timezone(local_tz)
|
||||
deliver_at_usertz = user_tz.normalize(user_tz.localize(deliver_at))
|
||||
user_tz = zoneinfo.ZoneInfo(local_tz)
|
||||
deliver_at_usertz = deliver_at.replace(tzinfo=user_tz)
|
||||
deliver_at = convert_to_UTC(deliver_at_usertz)
|
||||
|
||||
if deliver_at <= timezone_now():
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Any, Dict, Optional
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, update_session_auth_hash
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -42,7 +41,13 @@ from zerver.lib.response import json_success
|
|||
from zerver.lib.send_email import FromAddress, send_email
|
||||
from zerver.lib.sounds import get_available_notification_sounds
|
||||
from zerver.lib.upload import upload_avatar_image
|
||||
from zerver.lib.validator import check_bool, check_int, check_int_in, check_string_in
|
||||
from zerver.lib.validator import (
|
||||
check_bool,
|
||||
check_int,
|
||||
check_int_in,
|
||||
check_string_in,
|
||||
check_timezone,
|
||||
)
|
||||
from zerver.models import (
|
||||
EmailChangeStatus,
|
||||
UserProfile,
|
||||
|
@ -164,9 +169,7 @@ def json_change_settings(
|
|||
demote_inactive_streams: Optional[int] = REQ(
|
||||
json_validator=check_int_in(UserProfile.DEMOTE_STREAMS_CHOICES), default=None
|
||||
),
|
||||
timezone: Optional[str] = REQ(
|
||||
str_validator=check_string_in(pytz.all_timezones_set), default=None
|
||||
),
|
||||
timezone: Optional[str] = REQ(str_validator=check_timezone, default=None),
|
||||
email_notifications_batching_period_seconds: Optional[int] = REQ(
|
||||
json_validator=check_int, default=None
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue