From 8a7916f21aacb54b77f2dcbcd8cb930d4beb286a Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Sun, 19 Nov 2023 10:45:19 -0800 Subject: [PATCH] =?UTF-8?q?python:=20Consistently=20use=20from=E2=80=A6imp?= =?UTF-8?q?ort=20for=20datetime.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Kaseorg --- analytics/models.py | 4 +- confirmation/models.py | 8 ++-- corporate/tests/test_remote_billing.py | 10 ++-- corporate/tests/test_remote_counts.py | 14 +++--- .../check_user_zephyr_mirror_liveness | 10 ++-- scripts/lib/zulip_tools.py | 12 ++--- zerver/actions/create_realm.py | 8 ++-- zerver/actions/create_user.py | 4 +- zerver/actions/invites.py | 10 ++-- zerver/actions/message_edit.py | 13 +++-- zerver/actions/message_send.py | 10 ++-- zerver/actions/muted_users.py | 4 +- zerver/actions/presence.py | 17 ++++--- zerver/actions/scheduled_messages.py | 8 ++-- zerver/actions/user_activity.py | 10 ++-- zerver/actions/user_groups.py | 4 +- zerver/actions/user_settings.py | 10 ++-- zerver/actions/user_topics.py | 6 +-- zerver/data_import/slack.py | 6 +-- zerver/decorator.py | 4 +- zerver/lib/cache_helpers.py | 4 +- zerver/lib/compatibility.py | 16 +++---- zerver/lib/digest.py | 18 +++---- zerver/lib/export.py | 4 +- zerver/lib/import_realm.py | 6 +-- zerver/lib/markdown/__init__.py | 10 ++-- zerver/lib/message.py | 10 ++-- zerver/lib/muted_users.py | 6 +-- zerver/lib/presence.py | 18 +++---- zerver/lib/send_email.py | 4 +- zerver/lib/stream_traffic.py | 6 +-- zerver/lib/timestamp.py | 34 ++++++------- zerver/lib/types.py | 6 +-- zerver/lib/user_topics.py | 6 +-- .../delete_old_unclaimed_attachments.py | 4 +- .../commands/enqueue_digest_emails.py | 4 +- .../migrations/0262_mutedtopic_date_muted.py | 6 +-- zerver/models.py | 13 ++--- zerver/tests/test_auth_backends.py | 16 +++---- zerver/tests/test_digest.py | 44 ++++++++--------- zerver/tests/test_email_change.py | 8 ++-- zerver/tests/test_events.py | 14 +++--- zerver/tests/test_example.py | 4 +- zerver/tests/test_home.py | 11 ++--- zerver/tests/test_import_export.py | 8 ++-- zerver/tests/test_invite.py | 30 +++++------- zerver/tests/test_message_edit.py | 48 +++++++++---------- zerver/tests/test_message_fetch.py | 4 +- zerver/tests/test_message_send.py | 22 ++++----- zerver/tests/test_messages.py | 4 +- zerver/tests/test_new_users.py | 12 ++--- zerver/tests/test_presence.py | 31 ++++++------ zerver/tests/test_push_notifications.py | 18 +++---- zerver/tests/test_queue_worker.py | 20 ++++---- zerver/tests/test_realm.py | 13 ++--- zerver/tests/test_scheduled_messages.py | 46 ++++++++---------- zerver/tests/test_signup.py | 4 +- zerver/tests/test_users.py | 12 ++--- zerver/views/message_edit.py | 4 +- zerver/views/presence.py | 4 +- zerver/views/user_topics.py | 4 +- zerver/worker/queue_processors.py | 6 +-- zilencer/lib/remote_counts.py | 4 +- .../commands/add_server_to_legacy_plan.py | 14 +++--- .../commands/populate_billing_realms.py | 12 ++--- zilencer/views.py | 10 ++-- 66 files changed, 362 insertions(+), 412 deletions(-) diff --git a/analytics/models.py b/analytics/models.py index 4ec03edf64..2ebe57338a 100644 --- a/analytics/models.py +++ b/analytics/models.py @@ -1,7 +1,7 @@ # https://github.com/typeddjango/django-stubs/issues/1698 # mypy: disable-error-code="explicit-override" -import datetime +from datetime import datetime from django.db import models from django.db.models import Q, UniqueConstraint @@ -27,7 +27,7 @@ class FillState(models.Model): # The earliest/starting end_time in FillState # We assume there is at least one realm -def installation_epoch() -> datetime.datetime: +def installation_epoch() -> datetime: earliest_realm_creation = Realm.objects.aggregate(models.Min("date_created"))[ "date_created__min" ] diff --git a/confirmation/models.py b/confirmation/models.py index 33da8f69fa..c63607ba21 100644 --- a/confirmation/models.py +++ b/confirmation/models.py @@ -1,9 +1,9 @@ # Copyright: (c) 2008, Jarek Zgoda __revision__ = "$Id: models.py 28 2009-10-22 15:03:02Z jarek.zgoda $" -import datetime import secrets from base64 import b32encode +from datetime import timedelta from typing import List, Mapping, Optional, Union from urllib.parse import urljoin @@ -136,11 +136,9 @@ def create_confirmation_link( expiry_date = None else: assert validity_in_minutes is not None - expiry_date = current_time + datetime.timedelta(minutes=validity_in_minutes) + expiry_date = current_time + timedelta(minutes=validity_in_minutes) else: - expiry_date = current_time + datetime.timedelta( - days=_properties[confirmation_type].validity_in_days - ) + expiry_date = current_time + timedelta(days=_properties[confirmation_type].validity_in_days) Confirmation.objects.create( content_object=obj, diff --git a/corporate/tests/test_remote_billing.py b/corporate/tests/test_remote_billing.py index 8886c3057c..8bb37ddef8 100644 --- a/corporate/tests/test_remote_billing.py +++ b/corporate/tests/test_remote_billing.py @@ -1,4 +1,4 @@ -import datetime +from datetime import timedelta from typing import TYPE_CHECKING, Optional from unittest import mock @@ -223,7 +223,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase): # Go to the URL we're redirected to after authentication and make sure # we're granted access. with time_machine.travel( - now + datetime.timedelta(seconds=1), + now + timedelta(seconds=1), tick=False, ): result = self.client_get(final_url, subdomain="selfhosting") @@ -233,7 +233,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase): # Now go there again, simulating doing this after the session has expired. # We should be denied access and redirected to re-auth. with time_machine.travel( - now + datetime.timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 1), + now + timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 1), tick=False, ): result = self.client_get(final_url, subdomain="selfhosting") @@ -297,7 +297,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase): # Try the case where the identity dict is simultaneously expired. with time_machine.travel( - now + datetime.timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 30), + now + timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 30), tick=False, ): with self.assertLogs("django.request", "ERROR") as m, self.assertRaises(AssertionError): @@ -512,7 +512,7 @@ class LegacyServerLoginTest(BouncerTestCase): # Now we can simulate an expired identity dict in the session. with time_machine.travel( - now + datetime.timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 30), + now + timedelta(seconds=REMOTE_BILLING_SESSION_VALIDITY_SECONDS + 30), tick=False, ): result = self.client_get(f"/server/{self.uuid}/upgrade/", subdomain="selfhosting") diff --git a/corporate/tests/test_remote_counts.py b/corporate/tests/test_remote_counts.py index 346a0025a4..97156d58a4 100644 --- a/corporate/tests/test_remote_counts.py +++ b/corporate/tests/test_remote_counts.py @@ -1,4 +1,4 @@ -import datetime +from datetime import timedelta import time_machine from django.utils.timezone import now as timezone_now @@ -29,7 +29,7 @@ class RemoteCountTest(ZulipTestCase): # inconsistent behavior on the boundaries. E.g. does an entry with # end_time=now - 30 days belong to the "last 30 days" interval or the 30 days before that? # Using now_offset avoids this ambiguity. - now_offset = now + datetime.timedelta(hours=1) + now_offset = now + timedelta(hours=1) # First try with absolutely no analytics data. with self.assertRaises(MissingDataError): @@ -44,7 +44,7 @@ class RemoteCountTest(ZulipTestCase): remote_id=1, property="active_users_audit:is_bot:day", value=5, - end_time=now_offset - datetime.timedelta(days=4), + end_time=now_offset - timedelta(days=4), ) # If we're missing any message data (which is the same as message data with 0, because @@ -61,7 +61,7 @@ class RemoteCountTest(ZulipTestCase): remote_id=1 + t, property="messages_sent:message_type:day", value=100, - end_time=now_offset - datetime.timedelta(days=90 + (31 - t)), + end_time=now_offset - timedelta(days=90 + (31 - t)), ) for t in range(1, 31) ) @@ -74,7 +74,7 @@ class RemoteCountTest(ZulipTestCase): remote_id=31 + t, property="messages_sent:message_type:day", value=20, - end_time=now_offset - datetime.timedelta(days=60 + (31 - t)), + end_time=now_offset - timedelta(days=60 + (31 - t)), ) for t in range(1, 31) ) @@ -86,7 +86,7 @@ class RemoteCountTest(ZulipTestCase): remote_id=61 + t, property="messages_sent:message_type:day", value=30, - end_time=now_offset - datetime.timedelta(days=30 + (31 - t)), + end_time=now_offset - timedelta(days=30 + (31 - t)), ) for t in range(1, 31) ) @@ -98,7 +98,7 @@ class RemoteCountTest(ZulipTestCase): remote_id=91 + t, property="messages_sent:message_type:day", value=10, - end_time=now_offset - datetime.timedelta(days=(31 - t)), + end_time=now_offset - timedelta(days=(31 - t)), ) for t in range(1, 31) ) diff --git a/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness b/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness index 93e0384f41..3c3a94ecd9 100755 --- a/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness +++ b/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness @@ -6,9 +6,9 @@ Nagios plugin to check that our MIT users' Zephyr mirrors are running. It must be run on a machine that is using the live database for the Django ORM. """ -import datetime import os import sys +from datetime import timedelta from typing import NoReturn, Optional sys.path.append("/home/zulip/deployments/current") @@ -47,10 +47,10 @@ def report( too_old_data = "" if all_users is not None: recently_inactive_users = ( - all_users.filter(last_visit__lt=now - datetime.timedelta(minutes=10)) + all_users.filter(last_visit__lt=now - timedelta(minutes=10)) .distinct("user_profile_id") .difference( - all_users.filter(last_visit__lt=now - datetime.timedelta(minutes=60)).distinct( + all_users.filter(last_visit__lt=now - timedelta(minutes=60)).distinct( "user_profile_id" ) ) @@ -83,14 +83,14 @@ all_users = UserActivity.objects.filter( client_id=zephyr_client.id, ) new_inactive_users = ( - all_users.filter(last_visit__lt=now - datetime.timedelta(minutes=10)) + all_users.filter(last_visit__lt=now - timedelta(minutes=10)) .values("user_profile_id") .distinct("user_profile_id") .count() ) old_inactive_users = ( - all_users.filter(last_visit__lt=now - datetime.timedelta(minutes=60)) + all_users.filter(last_visit__lt=now - timedelta(minutes=60)) .values("user_profile_id") .distinct("user_profile_id") .count() diff --git a/scripts/lib/zulip_tools.py b/scripts/lib/zulip_tools.py index 0565a3db19..f79cd78d02 100755 --- a/scripts/lib/zulip_tools.py +++ b/scripts/lib/zulip_tools.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import argparse import configparser -import datetime import functools import hashlib import json @@ -16,6 +15,7 @@ import subprocess import sys import time import uuid +from datetime import datetime, timedelta from typing import IO, Any, Dict, List, Sequence, Set from urllib.parse import SplitResult @@ -186,7 +186,7 @@ def su_to_zulip(save_suid: bool = False) -> None: def make_deploy_path() -> str: - timestamp = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) # noqa: DTZ005 + timestamp = datetime.now().strftime(TIMESTAMP_FORMAT) # noqa: DTZ005 return os.path.join(DEPLOYMENTS_DIR, timestamp) @@ -302,9 +302,7 @@ def get_recent_deployments(threshold_days: int) -> Set[str]: # Returns a list of deployments not older than threshold days # including `/root/zulip` directory if it exists. recent = set() - threshold_date = datetime.datetime.now() - datetime.timedelta( # noqa: DTZ005 - days=threshold_days - ) + threshold_date = datetime.now() - timedelta(days=threshold_days) # noqa: DTZ005 for dir_name in os.listdir(DEPLOYMENTS_DIR): target_dir = os.path.join(DEPLOYMENTS_DIR, dir_name) if not os.path.isdir(target_dir): @@ -314,7 +312,7 @@ def get_recent_deployments(threshold_days: int) -> Set[str]: # Skip things like "lock" that aren't actually a deployment directory continue try: - date = datetime.datetime.strptime(dir_name, TIMESTAMP_FORMAT) # noqa: DTZ007 + date = datetime.strptime(dir_name, TIMESTAMP_FORMAT) # noqa: DTZ007 if date >= threshold_date: recent.add(target_dir) except ValueError: @@ -331,7 +329,7 @@ def get_recent_deployments(threshold_days: int) -> Set[str]: def get_threshold_timestamp(threshold_days: int) -> int: # Given number of days, this function returns timestamp corresponding # to the time prior to given number of days. - threshold = datetime.datetime.now() - datetime.timedelta(days=threshold_days) # noqa: DTZ005 + threshold = datetime.now() - timedelta(days=threshold_days) # noqa: DTZ005 threshold_timestamp = int(time.mktime(threshold.utctimetuple())) return threshold_timestamp diff --git a/zerver/actions/create_realm.py b/zerver/actions/create_realm.py index 02c90bc2d8..377223974d 100644 --- a/zerver/actions/create_realm.py +++ b/zerver/actions/create_realm.py @@ -1,5 +1,5 @@ -import datetime import logging +from datetime import datetime, timedelta from typing import Any, Dict, Optional from django.conf import settings @@ -162,7 +162,7 @@ def do_create_realm( plan_type: Optional[int] = None, org_type: Optional[int] = None, default_language: Optional[str] = None, - date_created: Optional[datetime.datetime] = None, + date_created: Optional[datetime] = None, is_demo_organization: bool = False, enable_read_receipts: Optional[bool] = None, enable_spectator_access: Optional[bool] = None, @@ -223,8 +223,8 @@ def do_create_realm( with transaction.atomic(): realm = Realm(string_id=string_id, name=name, **kwargs) if is_demo_organization: - realm.demo_organization_scheduled_deletion_date = ( - realm.date_created + datetime.timedelta(days=settings.DEMO_ORG_DEADLINE_DAYS) + realm.demo_organization_scheduled_deletion_date = realm.date_created + timedelta( + days=settings.DEMO_ORG_DEADLINE_DAYS ) set_realm_permissions_based_on_org_type(realm) diff --git a/zerver/actions/create_user.py b/zerver/actions/create_user.py index 51c4f54898..9b0adb991c 100644 --- a/zerver/actions/create_user.py +++ b/zerver/actions/create_user.py @@ -1,5 +1,5 @@ -import datetime from collections import defaultdict +from datetime import timedelta from typing import Any, Dict, Iterable, List, Optional, Sequence, Set from django.conf import settings @@ -65,7 +65,7 @@ MAX_NUM_ONBOARDING_UNREAD_MESSAGES = 20 # feel like Zulip is buggy, but in low-traffic or bursty-traffic # organizations, it's reasonable for the most recent 20 messages to be # several weeks old and still be a good place to start. -ONBOARDING_RECENT_TIMEDELTA = datetime.timedelta(weeks=12) +ONBOARDING_RECENT_TIMEDELTA = timedelta(weeks=12) DEFAULT_HISTORICAL_FLAGS = UserMessage.flags.historical | UserMessage.flags.read diff --git a/zerver/actions/invites.py b/zerver/actions/invites.py index 82fcc480b5..48807c8521 100644 --- a/zerver/actions/invites.py +++ b/zerver/actions/invites.py @@ -1,5 +1,5 @@ -import datetime import logging +from datetime import timedelta from typing import Any, Collection, Dict, List, Optional, Sequence, Set, Tuple, Union from django.conf import settings @@ -82,7 +82,7 @@ def estimate_recent_invites(realms: Collection[Realm], *, days: int) -> int: recent_invites = RealmCount.objects.filter( realm__in=realms, property="invites_sent::day", - end_time__gte=timezone_now() - datetime.timedelta(days=days), + end_time__gte=timezone_now() - timedelta(days=days), ).aggregate(Sum("value"))["value__sum"] if recent_invites is None: return 0 @@ -120,7 +120,7 @@ def too_many_recent_realm_invites(realm: Realm, num_invitees: int) -> bool: if realm.icon_source == Realm.ICON_FROM_GRAVATAR: warning_flags.append("no-realm-icon") - if realm.date_created >= timezone_now() - datetime.timedelta(hours=1): + if realm.date_created >= timezone_now() - timedelta(hours=1): warning_flags.append("realm-created-in-last-hour") current_user_count = len(UserProfile.objects.filter(realm=realm, is_bot=False, is_active=True)) @@ -181,7 +181,7 @@ def check_invite_limit(realm: Realm, num_invitees: int) -> None: ) default_max = settings.INVITES_DEFAULT_REALM_DAILY_MAX - newrealm_age = datetime.timedelta(days=settings.INVITES_NEW_REALM_DAYS) + newrealm_age = timedelta(days=settings.INVITES_NEW_REALM_DAYS) if realm.date_created <= timezone_now() - newrealm_age: # If this isn't a "newly-created" realm, we're done. The # remaining code applies an aggregate limit across all @@ -235,7 +235,7 @@ def do_invite_users( realm = user_profile.realm if not realm.invite_required: # Inhibit joining an open realm to send spam invitations. - min_age = datetime.timedelta(days=settings.INVITES_MIN_USER_AGE_DAYS) + min_age = timedelta(days=settings.INVITES_MIN_USER_AGE_DAYS) if user_profile.date_joined > timezone_now() - min_age and not user_profile.is_realm_admin: raise InvitationError( _( diff --git a/zerver/actions/message_edit.py b/zerver/actions/message_edit.py index b83e876907..0f7a4920cd 100644 --- a/zerver/actions/message_edit.py +++ b/zerver/actions/message_edit.py @@ -1,6 +1,6 @@ -import datetime import itertools from collections import defaultdict +from datetime import timedelta from typing import AbstractSet, Any, Dict, Iterable, List, Optional, Set, Tuple from django.conf import settings @@ -1151,8 +1151,7 @@ def check_time_limit_for_change_all_propagate_mode( Message.objects.filter( # Uses index: zerver_message_pkey id__in=accessible_messages_in_topic, - date_sent__gt=timezone_now() - - datetime.timedelta(seconds=message_move_deadline_seconds), + date_sent__gt=timezone_now() - timedelta(seconds=message_move_deadline_seconds), ) .order_by("date_sent") .values_list("id", flat=True) @@ -1164,7 +1163,7 @@ def check_time_limit_for_change_all_propagate_mode( .order_by("id") .values_list("id", "date_sent") ) - oldest_allowed_message_date = timezone_now() - datetime.timedelta( + oldest_allowed_message_date = timezone_now() - timedelta( seconds=message_move_deadline_seconds ) messages_allowed_to_move = [ @@ -1235,7 +1234,7 @@ def check_update_message( edit_limit_buffer = 20 if content is not None and user_profile.realm.message_content_edit_limit_seconds is not None: deadline_seconds = user_profile.realm.message_content_edit_limit_seconds + edit_limit_buffer - if (timezone_now() - message.date_sent) > datetime.timedelta(seconds=deadline_seconds): + if (timezone_now() - message.date_sent) > timedelta(seconds=deadline_seconds): raise JsonableError(_("The time limit for editing this message has passed")) # If there is a change to the topic, check that the user is allowed to @@ -1250,7 +1249,7 @@ def check_update_message( deadline_seconds = ( user_profile.realm.move_messages_within_stream_limit_seconds + edit_limit_buffer ) - if (timezone_now() - message.date_sent) > datetime.timedelta(seconds=deadline_seconds): + if (timezone_now() - message.date_sent) > timedelta(seconds=deadline_seconds): raise JsonableError(_("The time limit for editing this message's topic has passed.")) rendering_result = None @@ -1318,7 +1317,7 @@ def check_update_message( deadline_seconds = ( user_profile.realm.move_messages_between_streams_limit_seconds + edit_limit_buffer ) - if (timezone_now() - message.date_sent) > datetime.timedelta(seconds=deadline_seconds): + if (timezone_now() - message.date_sent) > timedelta(seconds=deadline_seconds): raise JsonableError( _("The time limit for editing this message's stream has passed") ) diff --git a/zerver/actions/message_send.py b/zerver/actions/message_send.py index d335083cb6..5ba43d0480 100644 --- a/zerver/actions/message_send.py +++ b/zerver/actions/message_send.py @@ -1,7 +1,7 @@ -import datetime import logging from collections import defaultdict from dataclasses import dataclass +from datetime import timedelta from email.headerregistry import Address from typing import ( AbstractSet, @@ -818,7 +818,7 @@ def filter_presence_idle_user_ids(user_ids: Set[int]) -> List[int]: if not user_ids: return [] - recent = timezone_now() - datetime.timedelta(seconds=settings.OFFLINE_THRESHOLD_SECS) + recent = timezone_now() - timedelta(seconds=settings.OFFLINE_THRESHOLD_SECS) rows = UserPresence.objects.filter( user_profile_id__in=user_ids, last_active_time__gte=recent, @@ -1174,9 +1174,9 @@ def already_sent_mirrored_message_id(message: Message) -> Optional[int]: # For huddle messages, we use a 10-second window because the # timestamps aren't guaranteed to actually match between two # copies of the same message. - time_window = datetime.timedelta(seconds=10) + time_window = timedelta(seconds=10) else: - time_window = datetime.timedelta(seconds=0) + time_window = timedelta(seconds=0) messages = Message.objects.filter( # Uses index: zerver_message_realm_recipient_subject @@ -1370,7 +1370,7 @@ def send_rate_limited_pm_notification_to_bot_owner( # direct messages on a misconfigured integration, re-using the # UserProfile.last_reminder field, which is not used for bots. last_reminder = sender.last_reminder - waitperiod = datetime.timedelta(minutes=UserProfile.BOT_OWNER_STREAM_ALERT_WAITPERIOD) + waitperiod = timedelta(minutes=UserProfile.BOT_OWNER_STREAM_ALERT_WAITPERIOD) if last_reminder and timezone_now() - last_reminder <= waitperiod: return diff --git a/zerver/actions/muted_users.py b/zerver/actions/muted_users.py index 3ce10123bc..b8a0a5f076 100644 --- a/zerver/actions/muted_users.py +++ b/zerver/actions/muted_users.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime from typing import Optional from django.utils.timezone import now as timezone_now @@ -12,7 +12,7 @@ from zerver.tornado.django_api import send_event def do_mute_user( user_profile: UserProfile, muted_user: UserProfile, - date_muted: Optional[datetime.datetime] = None, + date_muted: Optional[datetime] = None, ) -> None: if date_muted is None: date_muted = timezone_now() diff --git a/zerver/actions/presence.py b/zerver/actions/presence.py index a7204d5719..25031bec6c 100644 --- a/zerver/actions/presence.py +++ b/zerver/actions/presence.py @@ -1,5 +1,5 @@ -import datetime import time +from datetime import datetime, timedelta from django.conf import settings from django.db import transaction @@ -87,7 +87,7 @@ def consolidate_client(client: Client) -> Client: def do_update_user_presence( user_profile: UserProfile, client: Client, - log_time: datetime.datetime, + log_time: datetime, status: int, *, force_send_update: bool = False, @@ -116,15 +116,15 @@ def do_update_user_presence( else: # The user was never active, so let's consider this large to go over any thresholds # we may have. - time_since_last_active = datetime.timedelta(days=1) + time_since_last_active = timedelta(days=1) if presence.last_connected_time is not None: time_since_last_connected = log_time - presence.last_connected_time else: # Same approach as above. - time_since_last_connected = datetime.timedelta(days=1) + time_since_last_connected = timedelta(days=1) assert (3 * settings.PRESENCE_PING_INTERVAL_SECS + 20) <= settings.OFFLINE_THRESHOLD_SECS - now_online = time_since_last_active > datetime.timedelta( + now_online = time_since_last_active > timedelta( # Here, we decide whether the user is newly online, and we need to consider # sending an immediate presence update via the events system that this user is now online, # rather than waiting for other clients to poll the presence update. @@ -146,7 +146,7 @@ def do_update_user_presence( # times per minute with multiple connected browser windows. # We also need to be careful not to wrongly "update" the timestamp if we actually already # have newer presence than the reported log_time. - if not created and time_since_last_connected > datetime.timedelta( + if not created and time_since_last_connected > timedelta( seconds=settings.PRESENCE_UPDATE_MIN_FREQ_SECONDS ): presence.last_connected_time = log_time @@ -154,8 +154,7 @@ def do_update_user_presence( if ( not created and status == UserPresence.LEGACY_STATUS_ACTIVE_INT - and time_since_last_active - > datetime.timedelta(seconds=settings.PRESENCE_UPDATE_MIN_FREQ_SECONDS) + and time_since_last_active > timedelta(seconds=settings.PRESENCE_UPDATE_MIN_FREQ_SECONDS) ): presence.last_active_time = log_time update_fields.append("last_active_time") @@ -184,7 +183,7 @@ def do_update_user_presence( def update_user_presence( user_profile: UserProfile, client: Client, - log_time: datetime.datetime, + log_time: datetime, status: int, new_user_input: bool, ) -> None: diff --git a/zerver/actions/scheduled_messages.py b/zerver/actions/scheduled_messages.py index 4a8416b4d9..cc5a9adbe4 100644 --- a/zerver/actions/scheduled_messages.py +++ b/zerver/actions/scheduled_messages.py @@ -1,5 +1,5 @@ -import datetime import logging +from datetime import datetime, timedelta from typing import List, Optional, Sequence, Tuple from django.conf import settings @@ -41,7 +41,7 @@ def check_schedule_message( message_to: List[int], topic_name: Optional[str], message_content: str, - deliver_at: datetime.datetime, + deliver_at: datetime, realm: Optional[Realm] = None, forwarder_user_profile: Optional[UserProfile] = None, ) -> int: @@ -125,7 +125,7 @@ def edit_scheduled_message( message_to: Optional[str], topic_name: Optional[str], message_content: Optional[str], - deliver_at: Optional[datetime.datetime], + deliver_at: Optional[datetime], realm: Realm, ) -> None: with transaction.atomic(): @@ -269,7 +269,7 @@ def send_scheduled_message(scheduled_message: ScheduledMessage) -> None: raise UserDeactivatedError # Limit how late we're willing to send a scheduled message. - latest_send_time = scheduled_message.scheduled_timestamp + datetime.timedelta( + latest_send_time = scheduled_message.scheduled_timestamp + timedelta( minutes=SCHEDULED_MESSAGE_LATE_CUTOFF_MINUTES ) if timezone_now() > latest_send_time: diff --git a/zerver/actions/user_activity.py b/zerver/actions/user_activity.py index f13fabb672..eb27444fa1 100644 --- a/zerver/actions/user_activity.py +++ b/zerver/actions/user_activity.py @@ -1,13 +1,11 @@ -import datetime +from datetime import datetime from zerver.lib.queue import queue_json_publish from zerver.lib.timestamp import datetime_to_timestamp from zerver.models import UserActivity, UserActivityInterval, UserProfile -def do_update_user_activity_interval( - user_profile: UserProfile, log_time: datetime.datetime -) -> None: +def do_update_user_activity_interval(user_profile: UserProfile, log_time: datetime) -> None: effective_end = log_time + UserActivityInterval.MIN_INTERVAL_LENGTH # This code isn't perfect, because with various races we might end # up creating two overlapping intervals, but that shouldn't happen @@ -32,7 +30,7 @@ def do_update_user_activity_interval( def do_update_user_activity( - user_profile_id: int, client_id: int, query: str, count: int, log_time: datetime.datetime + user_profile_id: int, client_id: int, query: str, count: int, log_time: datetime ) -> None: (activity, created) = UserActivity.objects.get_or_create( user_profile_id=user_profile_id, @@ -47,6 +45,6 @@ def do_update_user_activity( activity.save(update_fields=["last_visit", "count"]) -def update_user_activity_interval(user_profile: UserProfile, log_time: datetime.datetime) -> None: +def update_user_activity_interval(user_profile: UserProfile, log_time: datetime) -> None: event = {"user_profile_id": user_profile.id, "time": datetime_to_timestamp(log_time)} queue_json_publish("user_activity_interval", event) diff --git a/zerver/actions/user_groups.py b/zerver/actions/user_groups.py index fceb2600bb..d90f4b5983 100644 --- a/zerver/actions/user_groups.py +++ b/zerver/actions/user_groups.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime from typing import Dict, List, Mapping, Optional, Sequence, TypedDict, Union import django.db.utils @@ -27,7 +27,7 @@ from zerver.tornado.django_api import send_event, send_event_on_commit class MemberGroupUserDict(TypedDict): id: int role: int - date_joined: datetime.datetime + date_joined: datetime @transaction.atomic diff --git a/zerver/actions/user_settings.py b/zerver/actions/user_settings.py index b17efd148a..e73f014fa7 100644 --- a/zerver/actions/user_settings.py +++ b/zerver/actions/user_settings.py @@ -1,4 +1,4 @@ -import datetime +from datetime import timedelta from typing import Iterable, Optional, Union from django.conf import settings @@ -404,9 +404,9 @@ def update_scheduled_email_notifications_time( user_profile=user_profile ) - scheduled_timestamp_change = datetime.timedelta( - seconds=new_batching_period - ) - datetime.timedelta(seconds=old_batching_period) + scheduled_timestamp_change = timedelta(seconds=new_batching_period) - timedelta( + seconds=old_batching_period + ) existing_scheduled_emails.update( scheduled_timestamp=F("scheduled_timestamp") + scheduled_timestamp_change @@ -566,7 +566,7 @@ def do_change_user_setting( # We add a small additional offset as a fudge factor in # case of clock skew. status = UserPresence.LEGACY_STATUS_IDLE_INT - presence_time = timezone_now() - datetime.timedelta( + presence_time = timezone_now() - timedelta( seconds=settings.OFFLINE_THRESHOLD_SECS + 120 ) diff --git a/zerver/actions/user_topics.py b/zerver/actions/user_topics.py index e454e52194..505a185905 100644 --- a/zerver/actions/user_topics.py +++ b/zerver/actions/user_topics.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime from typing import Any, Dict, List, Optional from django.utils.timezone import now as timezone_now @@ -18,7 +18,7 @@ def bulk_do_set_user_topic_visibility_policy( topic: str, *, visibility_policy: int, - last_updated: Optional[datetime.datetime] = None, + last_updated: Optional[datetime] = None, skip_muted_topics_event: bool = False, ) -> None: if last_updated is None: @@ -66,7 +66,7 @@ def do_set_user_topic_visibility_policy( topic: str, *, visibility_policy: int, - last_updated: Optional[datetime.datetime] = None, + last_updated: Optional[datetime] = None, skip_muted_topics_event: bool = False, ) -> None: # For conciseness, this function should be used when a single diff --git a/zerver/data_import/slack.py b/zerver/data_import/slack.py index 4f3fd32934..5c069cb973 100644 --- a/zerver/data_import/slack.py +++ b/zerver/data_import/slack.py @@ -1,4 +1,3 @@ -import datetime import logging import os import posixpath @@ -7,6 +6,7 @@ import secrets import shutil import zipfile from collections import defaultdict +from datetime import datetime, timezone from email.headerregistry import Address from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type, TypeVar from urllib.parse import urlsplit @@ -972,9 +972,7 @@ def channel_message_to_zerver_message( # a counter among topics on that day. topic_name = "imported from Slack" if convert_slack_threads and "thread_ts" in message: - thread_ts = datetime.datetime.fromtimestamp( - float(message["thread_ts"]), tz=datetime.timezone.utc - ) + thread_ts = datetime.fromtimestamp(float(message["thread_ts"]), tz=timezone.utc) thread_ts_str = thread_ts.strftime(r"%Y/%m/%d %H:%M:%S") # The topic name is "2015-08-18 Slack thread 2", where the counter at the end is to disambiguate # threads with the same date. diff --git a/zerver/decorator.py b/zerver/decorator.py index 3074d19429..c8ab7410de 100644 --- a/zerver/decorator.py +++ b/zerver/decorator.py @@ -1,7 +1,7 @@ import base64 -import datetime import logging import urllib +from datetime import datetime from functools import wraps from io import BytesIO from typing import ( @@ -948,7 +948,7 @@ def internal_notify_view( return _wrapped_view_func -def to_utc_datetime(var_name: str, timestamp: str) -> datetime.datetime: +def to_utc_datetime(var_name: str, timestamp: str) -> datetime: return timestamp_to_datetime(float(timestamp)) diff --git a/zerver/lib/cache_helpers.py b/zerver/lib/cache_helpers.py index d7ce7c9eea..d83c2a40bb 100644 --- a/zerver/lib/cache_helpers.py +++ b/zerver/lib/cache_helpers.py @@ -1,6 +1,6 @@ # See https://zulip.readthedocs.io/en/latest/subsystems/caching.html for docs -import datetime import logging +from datetime import timedelta from typing import Any, Callable, Dict, Iterable, Tuple from django.conf import settings @@ -64,7 +64,7 @@ def get_active_realm_ids() -> ValuesQuerySet[RealmCount, int]: worth of cache work (where N is the number of default streams for a new organization). """ - date = timezone_now() - datetime.timedelta(days=2) + date = timezone_now() - timedelta(days=2) return ( RealmCount.objects.filter(end_time__gte=date, property="1day_actives::day", value__gt=0) .distinct("realm_id") diff --git a/zerver/lib/compatibility.py b/zerver/lib/compatibility.py index 3fc4c69adc..912345454a 100644 --- a/zerver/lib/compatibility.py +++ b/zerver/lib/compatibility.py @@ -1,6 +1,6 @@ -import datetime import os import re +from datetime import datetime, timedelta, timezone from typing import List, Optional, Tuple from django.conf import settings @@ -14,8 +14,8 @@ from zerver.signals import get_device_browser # LAST_SERVER_UPGRADE_TIME is the last time the server had a version deployed. 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=datetime.timezone.utc + LAST_SERVER_UPGRADE_TIME = datetime.strptime(timestamp, "%Y-%m-%d-%H-%M-%S").replace( + tzinfo=timezone.utc ) else: LAST_SERVER_UPGRADE_TIME = timezone_now() @@ -28,18 +28,14 @@ def is_outdated_server(user_profile: Optional[UserProfile]) -> bool: # someone has upgraded in the last year but to a release more than # a year old. git_version_path = os.path.join(settings.DEPLOY_ROOT, "version.py") - release_build_time = datetime.datetime.fromtimestamp( - os.path.getmtime(git_version_path), datetime.timezone.utc - ) + release_build_time = datetime.fromtimestamp(os.path.getmtime(git_version_path), timezone.utc) version_no_newer_than = min(LAST_SERVER_UPGRADE_TIME, release_build_time) - deadline = version_no_newer_than + datetime.timedelta( - days=settings.SERVER_UPGRADE_NAG_DEADLINE_DAYS - ) + deadline = version_no_newer_than + timedelta(days=settings.SERVER_UPGRADE_NAG_DEADLINE_DAYS) if user_profile is None or not user_profile.is_realm_admin: # Administrators get warned at the deadline; all users 30 days later. - deadline = deadline + datetime.timedelta(days=30) + deadline = deadline + timedelta(days=30) if timezone_now() > deadline: return True diff --git a/zerver/lib/digest.py b/zerver/lib/digest.py index 4197999fc6..d0e91cdf78 100644 --- a/zerver/lib/digest.py +++ b/zerver/lib/digest.py @@ -1,8 +1,8 @@ -import datetime import functools import heapq import logging from collections import defaultdict +from datetime import datetime, timedelta, timezone from typing import Any, Collection, Dict, Iterator, List, Optional, Set, Tuple from django.conf import settings @@ -88,13 +88,13 @@ class DigestTopic: # Changes to this should also be reflected in # zerver/worker/queue_processors.py:DigestWorker.consume() -def queue_digest_user_ids(user_ids: List[int], cutoff: datetime.datetime) -> None: +def queue_digest_user_ids(user_ids: List[int], cutoff: datetime) -> None: # Convert cutoff to epoch seconds for transit. event = {"user_ids": user_ids, "cutoff": cutoff.strftime("%s")} queue_json_publish("digest_emails", event) -def enqueue_emails(cutoff: datetime.datetime) -> None: +def enqueue_emails(cutoff: datetime) -> None: if not settings.SEND_DIGEST_EMAILS: return @@ -105,10 +105,10 @@ def enqueue_emails(cutoff: datetime.datetime) -> None: _enqueue_emails_for_realm(realm, cutoff) -def _enqueue_emails_for_realm(realm: Realm, cutoff: datetime.datetime) -> None: +def _enqueue_emails_for_realm(realm: Realm, cutoff: datetime) -> None: # This should only be called directly by tests. Use enqueue_emails # to process all realms that are set up for processing on any given day. - twelve_hours_ago = timezone_now() - datetime.timedelta(hours=12) + twelve_hours_ago = timezone_now() - timedelta(hours=12) target_users = ( UserProfile.objects.filter( @@ -174,7 +174,7 @@ def maybe_clear_recent_topics_cache(realm_id: int, cutoff: float) -> None: def get_recent_topics( realm_id: int, stream_id: int, - cutoff_date: datetime.datetime, + cutoff_date: datetime, ) -> List[DigestTopic]: # Gather information about topic conversations, then # classify by: @@ -237,7 +237,7 @@ def get_hot_topics( return hot_topics -def get_recently_created_streams(realm: Realm, threshold: datetime.datetime) -> List[Stream]: +def get_recently_created_streams(realm: Realm, threshold: datetime) -> List[Stream]: fields = ["id", "name", "is_web_public", "invite_only"] return list(get_active_streams(realm).filter(date_created__gt=threshold).only(*fields)) @@ -270,7 +270,7 @@ def enough_traffic(hot_conversations: str, new_streams: int) -> bool: return bool(hot_conversations or new_streams) -def get_user_stream_map(user_ids: List[int], cutoff_date: datetime.datetime) -> Dict[int, Set[int]]: +def get_user_stream_map(user_ids: List[int], cutoff_date: datetime) -> Dict[int, Set[int]]: """Skipping streams where the user's subscription status has changed when constructing digests is critical to ensure correctness for streams without shared history, guest users, and long-term idle @@ -337,7 +337,7 @@ def bulk_get_digest_context( assert user.realm_id == realm.id # Convert from epoch seconds to a datetime object. - cutoff_date = datetime.datetime.fromtimestamp(int(cutoff), tz=datetime.timezone.utc) + cutoff_date = datetime.fromtimestamp(int(cutoff), tz=timezone.utc) maybe_clear_recent_topics_cache(realm.id, cutoff) diff --git a/zerver/lib/export.py b/zerver/lib/export.py index 845deeaaaa..c9732b8396 100644 --- a/zerver/lib/export.py +++ b/zerver/lib/export.py @@ -6,7 +6,6 @@ # it the lists in `ALL_ZULIP_TABLES` and similar data structures and # (2) if it doesn't belong in EXCLUDED_TABLES, add a Config object for # it to get_realm_config. -import datetime import glob import logging import os @@ -14,6 +13,7 @@ import shutil import subprocess import tempfile from contextlib import suppress +from datetime import datetime from functools import lru_cache from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Set, Tuple, TypedDict @@ -436,7 +436,7 @@ def floatify_datetime_fields(data: TableData, table: TableName) -> None: dt = item[field] if dt is None: continue - assert isinstance(dt, datetime.datetime) + assert isinstance(dt, datetime) assert not timezone_is_naive(dt) item[field] = dt.timestamp() diff --git a/zerver/lib/import_realm.py b/zerver/lib/import_realm.py index 145343bbb9..d2898de736 100644 --- a/zerver/lib/import_realm.py +++ b/zerver/lib/import_realm.py @@ -1,8 +1,8 @@ -import datetime import logging import os import shutil from concurrent.futures import ProcessPoolExecutor, as_completed +from datetime import datetime, timezone from mimetypes import guess_type from typing import Any, Dict, List, Optional, Tuple @@ -169,9 +169,7 @@ def fix_datetime_fields(data: TableData, table: TableName) -> None: for item in data[table]: for field_name in DATE_FIELDS[table]: if item[field_name] is not None: - item[field_name] = datetime.datetime.fromtimestamp( - item[field_name], tz=datetime.timezone.utc - ) + item[field_name] = datetime.fromtimestamp(item[field_name], tz=timezone.utc) def fix_upload_links(data: TableData, message_table: TableName) -> None: diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py index 53bbb32b16..576f12d0c4 100644 --- a/zerver/lib/markdown/__init__.py +++ b/zerver/lib/markdown/__init__.py @@ -1,7 +1,6 @@ # Zulip's main Markdown implementation. See docs/subsystems/markdown.md for # detailed documentation on our Markdown syntax. import cgi -import datetime import html import logging import mimetypes @@ -11,6 +10,7 @@ import urllib import urllib.parse from collections import deque from dataclasses import dataclass +from datetime import datetime, timezone from functools import lru_cache from typing import ( Any, @@ -1403,9 +1403,7 @@ class Timestamp(markdown.inlinepatterns.Pattern): timestamp = dateutil.parser.parse(time_input_string, tzinfos=common_timezones) except ValueError: try: - timestamp = datetime.datetime.fromtimestamp( - float(time_input_string), tz=datetime.timezone.utc - ) + timestamp = datetime.fromtimestamp(float(time_input_string), tz=timezone.utc) except ValueError: timestamp = None @@ -1420,9 +1418,9 @@ class Timestamp(markdown.inlinepatterns.Pattern): # Use HTML5