Browse Source

ref: use datetime.timezone.utc instead of pytz.utc / pytz.UTC (#54705)

django 4.x switches to zoneinfo-based timezones -- this gets us mostly
off of pytz for non-user timezones
anthony sottile 1 year ago
parent
commit
7d17434e01

+ 1 - 1
setup.cfg

@@ -94,7 +94,7 @@ sentry =
 # Currently, the black formatter doesn't wrap long strings: https://github.com/psf/black/issues/182#issuecomment-385325274
 # Currently, the black formatter doesn't wrap long strings: https://github.com/psf/black/issues/182#issuecomment-385325274
 # We already have a lot of E501's - these are lines black didn't wrap.
 # We already have a lot of E501's - these are lines black didn't wrap.
 # But rather than append # noqa: E501 to all of them, we just ignore E501 for now.
 # But rather than append # noqa: E501 to all of them, we just ignore E501 for now.
-extend-ignore = E203,E501,E402,E731,B007,B009,B010,B011,B020,B023,B024,B026,B027
+extend-ignore = E203,E501,E402,E731,B007,B009,B010,B011,B020,B023,B024,B026,B027,S008
 
 
 per-file-ignores =
 per-file-ignores =
     # these scripts must have minimal dependencies so opt out of the usual sentry rules
     # these scripts must have minimal dependencies so opt out of the usual sentry rules

+ 2 - 3
src/sentry/api/base.py

@@ -3,7 +3,7 @@ from __future__ import annotations
 import functools
 import functools
 import logging
 import logging
 import time
 import time
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 from typing import Any, Callable, Iterable, List, Mapping, Optional, Tuple, Type
 from typing import Any, Callable, Iterable, List, Mapping, Optional, Tuple, Type
 from urllib.parse import quote as urlquote
 from urllib.parse import quote as urlquote
 
 
@@ -12,7 +12,6 @@ from django.conf import settings
 from django.http import HttpResponse
 from django.http import HttpResponse
 from django.http.request import HttpRequest
 from django.http.request import HttpRequest
 from django.views.decorators.csrf import csrf_exempt
 from django.views.decorators.csrf import csrf_exempt
-from pytz import utc
 from rest_framework import status
 from rest_framework import status
 from rest_framework.authentication import BaseAuthentication, SessionAuthentication
 from rest_framework.authentication import BaseAuthentication, SessionAuthentication
 from rest_framework.exceptions import ParseError
 from rest_framework.exceptions import ParseError
@@ -538,7 +537,7 @@ class StatsMixin:
             if end:
             if end:
                 end = to_datetime(float(end))
                 end = to_datetime(float(end))
             else:
             else:
-                end = datetime.utcnow().replace(tzinfo=utc)
+                end = datetime.utcnow().replace(tzinfo=timezone.utc)
         except ValueError:
         except ValueError:
             raise ParseError(detail="until must be a numeric timestamp.")
             raise ParseError(detail="until must be a numeric timestamp.")
 
 

+ 4 - 5
src/sentry/api/endpoints/organization_details.py

@@ -1,13 +1,12 @@
 import logging
 import logging
 from copy import copy
 from copy import copy
 from dataclasses import dataclass
 from dataclasses import dataclass
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 
 
 from django.db import IntegrityError, models, router, transaction
 from django.db import IntegrityError, models, router, transaction
 from django.db.models.query_utils import DeferredAttribute
 from django.db.models.query_utils import DeferredAttribute
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import timezone
-from pytz import UTC
+from django.utils import timezone as django_timezone
 from rest_framework import serializers, status
 from rest_framework import serializers, status
 
 
 from bitfield.types import BitHandler
 from bitfield.types import BitHandler
@@ -295,7 +294,7 @@ class OrganizationSerializer(BaseOrganizationSerializer):
         return attrs
         return attrs
 
 
     def save_trusted_relays(self, incoming, changed_data, organization):
     def save_trusted_relays(self, incoming, changed_data, organization):
-        timestamp_now = datetime.utcnow().replace(tzinfo=UTC).isoformat()
+        timestamp_now = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat()
         option_key = "sentry:trusted-relays"
         option_key = "sentry:trusted-relays"
         try:
         try:
             # get what we already have
             # get what we already have
@@ -701,7 +700,7 @@ def send_delete_confirmation(delete_confirmation_args: DeleteConfirmationArgs):
         "username": username,
         "username": username,
         "user_ip_address": user_ip_address,
         "user_ip_address": user_ip_address,
         "deletion_datetime": deletion_datetime,
         "deletion_datetime": deletion_datetime,
-        "eta": timezone.now() + timedelta(seconds=countdown),
+        "eta": django_timezone.now() + timedelta(seconds=countdown),
         "url": url,
         "url": url,
     }
     }
 
 

+ 4 - 6
src/sentry/dynamic_sampling/rules/helpers/latest_releases.py

@@ -1,11 +1,9 @@
 import re
 import re
 from collections import namedtuple
 from collections import namedtuple
 from dataclasses import dataclass, field
 from dataclasses import dataclass, field
-from datetime import datetime
+from datetime import datetime, timezone
 from typing import Callable, Dict, List, Optional, Tuple
 from typing import Callable, Dict, List, Optional, Tuple
 
 
-from pytz import UTC
-
 from sentry.dynamic_sampling.rules.helpers.time_to_adoptions import Platform
 from sentry.dynamic_sampling.rules.helpers.time_to_adoptions import Platform
 from sentry.dynamic_sampling.rules.utils import BOOSTED_RELEASES_LIMIT, get_redis_client_for_ds
 from sentry.dynamic_sampling.rules.utils import BOOSTED_RELEASES_LIMIT, get_redis_client_for_ds
 from sentry.models import Project, Release
 from sentry.models import Project, Release
@@ -87,7 +85,7 @@ class BoostedReleases:
         # We get release models in order to have all the information to extend the releases we get from the cache.
         # We get release models in order to have all the information to extend the releases we get from the cache.
         models = self._get_releases_models()
         models = self._get_releases_models()
 
 
-        current_timestamp = datetime.utcnow().replace(tzinfo=UTC).timestamp()
+        current_timestamp = datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()
 
 
         extended_boosted_releases = []
         extended_boosted_releases = []
         expired_boosted_releases = []
         expired_boosted_releases = []
@@ -155,7 +153,7 @@ class ProjectBoostedReleases:
         self.redis_client.hset(
         self.redis_client.hset(
             cache_key,
             cache_key,
             self._generate_cache_key_for_boosted_release(release_id, environment),
             self._generate_cache_key_for_boosted_release(release_id, environment),
-            datetime.utcnow().replace(tzinfo=UTC).timestamp(),
+            datetime.utcnow().replace(tzinfo=timezone.utc).timestamp(),
         )
         )
         # In order to avoid having the boosted releases hash in memory for an indefinite amount of time, we will expire
         # In order to avoid having the boosted releases hash in memory for an indefinite amount of time, we will expire
         # it after a specific timeout.
         # it after a specific timeout.
@@ -210,7 +208,7 @@ class ProjectBoostedReleases:
         """
         """
         cache_key = self._generate_cache_key_for_boosted_releases_hash()
         cache_key = self._generate_cache_key_for_boosted_releases_hash()
         boosted_releases = self.redis_client.hgetall(cache_key)
         boosted_releases = self.redis_client.hgetall(cache_key)
-        current_timestamp = datetime.utcnow().replace(tzinfo=UTC).timestamp()
+        current_timestamp = datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()
 
 
         LRBRelease = namedtuple("LRBRelease", ["key", "timestamp"])
         LRBRelease = namedtuple("LRBRelease", ["key", "timestamp"])
         lrb_release = None
         lrb_release = None

+ 3 - 4
src/sentry/event_manager.py

@@ -8,7 +8,7 @@ import re
 import time
 import time
 import uuid
 import uuid
 from dataclasses import dataclass
 from dataclasses import dataclass
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 from io import BytesIO
 from io import BytesIO
 from typing import (
 from typing import (
     TYPE_CHECKING,
     TYPE_CHECKING,
@@ -32,7 +32,6 @@ from django.db import IntegrityError, OperationalError, connection, router, tran
 from django.db.models import Func
 from django.db.models import Func
 from django.db.models.signals import post_save
 from django.db.models.signals import post_save
 from django.utils.encoding import force_str
 from django.utils.encoding import force_str
-from pytz import UTC
 
 
 from sentry import (
 from sentry import (
     eventstore,
     eventstore,
@@ -1286,7 +1285,7 @@ def _tsdb_record_all_metrics(jobs: Sequence[Job]) -> None:
 
 
 @metrics.wraps("save_event.nodestore_save_many")
 @metrics.wraps("save_event.nodestore_save_many")
 def _nodestore_save_many(jobs: Sequence[Job]) -> None:
 def _nodestore_save_many(jobs: Sequence[Job]) -> None:
-    inserted_time = datetime.utcnow().replace(tzinfo=UTC).timestamp()
+    inserted_time = datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()
     for job in jobs:
     for job in jobs:
         # Write the event to Nodestore
         # Write the event to Nodestore
         subkeys = {}
         subkeys = {}
@@ -2210,7 +2209,7 @@ def save_attachment(
     if start_time is not None:
     if start_time is not None:
         timestamp = to_datetime(start_time)
         timestamp = to_datetime(start_time)
     else:
     else:
-        timestamp = datetime.utcnow().replace(tzinfo=UTC)
+        timestamp = datetime.utcnow().replace(tzinfo=timezone.utc)
 
 
     try:
     try:
         data = attachment.data
         data = attachment.data

+ 4 - 4
src/sentry/notifications/notifications/rules.py

@@ -1,6 +1,7 @@
 from __future__ import annotations
 from __future__ import annotations
 
 
 import logging
 import logging
+from datetime import timezone
 from typing import Any, Iterable, Mapping, MutableMapping
 from typing import Any, Iterable, Mapping, MutableMapping
 from urllib.parse import urlencode
 from urllib.parse import urlencode
 
 
@@ -115,17 +116,16 @@ class AlertRuleNotification(ProjectNotification):
     def get_recipient_context(
     def get_recipient_context(
         self, recipient: RpcActor, extra_context: Mapping[str, Any]
         self, recipient: RpcActor, extra_context: Mapping[str, Any]
     ) -> MutableMapping[str, Any]:
     ) -> MutableMapping[str, Any]:
-        timezone = pytz.timezone("UTC")
-
+        tz = timezone.utc
         if recipient.actor_type == ActorType.USER:
         if recipient.actor_type == ActorType.USER:
             user_tz = UserOption.objects.get_value(user=recipient, key="timezone", default="UTC")
             user_tz = UserOption.objects.get_value(user=recipient, key="timezone", default="UTC")
             try:
             try:
-                timezone = pytz.timezone(user_tz)
+                tz = pytz.timezone(user_tz)
             except pytz.UnknownTimeZoneError:
             except pytz.UnknownTimeZoneError:
                 pass
                 pass
         return {
         return {
             **super().get_recipient_context(recipient, extra_context),
             **super().get_recipient_context(recipient, extra_context),
-            "timezone": timezone,
+            "timezone": tz,
         }
         }
 
 
     def get_context(self) -> MutableMapping[str, Any]:
     def get_context(self) -> MutableMapping[str, Any]:

+ 2 - 3
src/sentry/profiles/task.py

@@ -1,14 +1,13 @@
 from __future__ import annotations
 from __future__ import annotations
 
 
 from copy import deepcopy
 from copy import deepcopy
-from datetime import datetime
+from datetime import datetime, timezone
 from time import time
 from time import time
 from typing import Any, List, Mapping, MutableMapping, Optional, Tuple
 from typing import Any, List, Mapping, MutableMapping, Optional, Tuple
 
 
 import msgpack
 import msgpack
 import sentry_sdk
 import sentry_sdk
 from django.conf import settings
 from django.conf import settings
-from pytz import UTC
 from symbolic.proguard import ProguardMapper
 from symbolic.proguard import ProguardMapper
 
 
 from sentry import quotas
 from sentry import quotas
@@ -703,7 +702,7 @@ def _track_outcome(
         key_id=None,
         key_id=None,
         outcome=outcome,
         outcome=outcome,
         reason=reason,
         reason=reason,
-        timestamp=datetime.utcnow().replace(tzinfo=UTC),
+        timestamp=datetime.utcnow().replace(tzinfo=timezone.utc),
         event_id=event_id,
         event_id=event_id,
         category=DataCategory.PROFILE_INDEXED,
         category=DataCategory.PROFILE_INDEXED,
         quantity=1,
         quantity=1,

+ 2 - 4
src/sentry/projectoptions/manager.py

@@ -1,8 +1,6 @@
 import bisect
 import bisect
 import uuid
 import uuid
-from datetime import datetime
-
-from pytz import utc
+from datetime import datetime, timezone
 
 
 from sentry.utils import json
 from sentry.utils import json
 
 
@@ -78,7 +76,7 @@ class ProjectOptionsManager:
         ProjectOption.objects.set_value(
         ProjectOption.objects.set_value(
             project,
             project,
             "sentry:relay-rev-lastchange",
             "sentry:relay-rev-lastchange",
-            json.datetime_to_str(datetime.utcnow().replace(tzinfo=utc)),
+            json.datetime_to_str(datetime.utcnow().replace(tzinfo=timezone.utc)),
         )
         )
 
 
     def register(self, key, default=None, epoch_defaults=None):
     def register(self, key, default=None, epoch_defaults=None):

+ 8 - 9
src/sentry/receivers/onboarding.py

@@ -1,11 +1,10 @@
 from __future__ import annotations
 from __future__ import annotations
 
 
 import logging
 import logging
-from datetime import datetime
+from datetime import datetime, timezone
 
 
-import pytz
 from django.db.models import F
 from django.db.models import F
-from django.utils import timezone
+from django.utils import timezone as django_timezone
 
 
 from sentry import analytics
 from sentry import analytics
 from sentry.models import (
 from sentry.models import (
@@ -47,7 +46,7 @@ logger = logging.getLogger("sentry")
 # Used to determine if we should or not record an analytic data
 # Used to determine if we should or not record an analytic data
 # for a first event of a project with a minified stack trace
 # for a first event of a project with a minified stack trace
 START_DATE_TRACKING_FIRST_EVENT_WITH_MINIFIED_STACK_TRACE_PER_PROJ = datetime(
 START_DATE_TRACKING_FIRST_EVENT_WITH_MINIFIED_STACK_TRACE_PER_PROJ = datetime(
-    2022, 12, 14, tzinfo=pytz.UTC
+    2022, 12, 14, tzinfo=timezone.utc
 )
 )
 
 
 
 
@@ -238,7 +237,7 @@ def record_first_replay(project, **kwargs):
         organization_id=project.organization_id,
         organization_id=project.organization_id,
         task=OnboardingTask.SESSION_REPLAY,
         task=OnboardingTask.SESSION_REPLAY,
         status=OnboardingTaskStatus.COMPLETE,
         status=OnboardingTaskStatus.COMPLETE,
-        date_completed=timezone.now(),
+        date_completed=django_timezone.now(),
     )
     )
 
 
     if success:
     if success:
@@ -306,7 +305,7 @@ def record_member_joined(organization_id: int, organization_member_id: int, **kw
         status=OnboardingTaskStatus.PENDING,
         status=OnboardingTaskStatus.PENDING,
         values={
         values={
             "status": OnboardingTaskStatus.COMPLETE,
             "status": OnboardingTaskStatus.COMPLETE,
-            "date_completed": timezone.now(),
+            "date_completed": django_timezone.now(),
             "data": {"invited_member_id": organization_member_id},
             "data": {"invited_member_id": organization_member_id},
         },
         },
     )
     )
@@ -491,7 +490,7 @@ def record_alert_rule_created(user, project, rule, rule_type, **kwargs):
             "status": OnboardingTaskStatus.COMPLETE,
             "status": OnboardingTaskStatus.COMPLETE,
             "user_id": user.id if user else None,
             "user_id": user.id if user else None,
             "project_id": project.id,
             "project_id": project.id,
-            "date_completed": timezone.now(),
+            "date_completed": django_timezone.now(),
         },
         },
     )
     )
 
 
@@ -509,7 +508,7 @@ def record_issue_tracker_used(plugin, project, user, **kwargs):
             "status": OnboardingTaskStatus.COMPLETE,
             "status": OnboardingTaskStatus.COMPLETE,
             "user_id": user.id,
             "user_id": user.id,
             "project_id": project.id,
             "project_id": project.id,
-            "date_completed": timezone.now(),
+            "date_completed": django_timezone.now(),
             "data": {"plugin": plugin.slug},
             "data": {"plugin": plugin.slug},
         },
         },
     )
     )
@@ -563,7 +562,7 @@ def record_integration_added(
         if task.status != OnboardingTaskStatus.COMPLETE:
         if task.status != OnboardingTaskStatus.COMPLETE:
             task.status = OnboardingTaskStatus.COMPLETE
             task.status = OnboardingTaskStatus.COMPLETE
             task.user_id = user_id
             task.user_id = user_id
-            task.date_completed = timezone.now()
+            task.date_completed = django_timezone.now()
         task.save()
         task.save()
     else:
     else:
         task = OrganizationOnboardingTask.objects.create(
         task = OrganizationOnboardingTask.objects.create(

+ 1 - 2
src/sentry/relay/config/__init__.py

@@ -16,7 +16,6 @@ from typing import (
 )
 )
 
 
 import sentry_sdk
 import sentry_sdk
-from pytz import utc
 from sentry_sdk import Hub, capture_exception
 from sentry_sdk import Hub, capture_exception
 
 
 from sentry import features, killswitches, quotas, utils
 from sentry import features, killswitches, quotas, utils
@@ -321,7 +320,7 @@ def _get_project_config(
     public_keys = get_public_key_configs(project, full_config, project_keys=project_keys)
     public_keys = get_public_key_configs(project, full_config, project_keys=project_keys)
 
 
     with Hub.current.start_span(op="get_public_config"):
     with Hub.current.start_span(op="get_public_config"):
-        now = datetime.utcnow().replace(tzinfo=utc)
+        now = datetime.utcnow().replace(tzinfo=timezone.utc)
         cfg = {
         cfg = {
             "disabled": False,
             "disabled": False,
             "slug": project.slug,
             "slug": project.slug,

Some files were not shown because too many files changed in this diff