Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace uses of deprecated datetime.utcnow() #2468

Merged
merged 9 commits into from
Jun 10, 2023
4 changes: 1 addition & 3 deletions docs/source/plugin/time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ Here is a full example of that, adapted from the built-in ``.t`` command::

import datetime

import pytz

from sopel import plugin
from sopel.tools.time import format_time, get_timezone

Expand All @@ -24,7 +22,7 @@ Here is a full example of that, adapted from the built-in ``.t`` command::
@plugin.require_chanmsg
def my_command(bot, trigger):
"""Give time in a channel."""
time = pytz.UTC.localize(datetime.datetime.utcnow())
time = datetime.datetime.now(datetime.timezone.utc)
timezone = get_timezone(
bot.db,
bot.settings,
Expand Down
4 changes: 2 additions & 2 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from __future__ import annotations

from ast import literal_eval
import datetime
from datetime import datetime, timezone
import inspect
import itertools
import logging
Expand Down Expand Up @@ -1045,7 +1045,7 @@ def error(

if trigger:
message = '{} from {} at {}. Message was: {}'.format(
message, trigger.nick, str(datetime.datetime.utcnow()), trigger.group(0)
message, trigger.nick, str(datetime.now(timezone.utc)), trigger.group(0)
)

LOGGER.exception(message)
Expand Down
10 changes: 5 additions & 5 deletions sopel/coretasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import base64
import collections
import copy
import datetime
from datetime import datetime, timedelta, timezone
import functools
import logging
import re
Expand Down Expand Up @@ -906,7 +906,7 @@ def _send_who(bot, mask):

target_id = bot.make_identifier(mask)
if not target_id.is_nick():
bot.channels[target_id].last_who = datetime.datetime.utcnow()
bot.channels[target_id].last_who = datetime.now(timezone.utc)


@plugin.interval(30)
Expand All @@ -916,10 +916,10 @@ def _periodic_send_who(bot):
# WHO not needed to update 'away' status
return

# Loops through the channels to find the one that has the longest time since the last WHO
# request, and issues a WHO request only if the last request for the channel was more than
# Loop through the channels to find the one that has the longest time since the last WHO
# request, and issue a WHO request only if the last request for the channel was more than
# 120 seconds ago.
who_trigger_time = datetime.datetime.utcnow() - datetime.timedelta(seconds=120)
who_trigger_time = datetime.now(timezone.utc) - timedelta(seconds=120)
selected_channel = None
for channel_name, channel in bot.channels.items():
if channel.last_who is None:
Expand Down
6 changes: 3 additions & 3 deletions sopel/irc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import abc
from collections import deque
from datetime import datetime
from datetime import datetime, timezone
import logging
import os
import threading
Expand Down Expand Up @@ -471,7 +471,7 @@ def on_error(self) -> None:
# quit if too many errors
dt_seconds: float = 0.0
if self.last_error_timestamp is not None:
dt = datetime.utcnow() - self.last_error_timestamp
dt = datetime.now(timezone.utc) - self.last_error_timestamp
dt_seconds = dt.total_seconds()

if dt_seconds < 5:
Expand All @@ -480,7 +480,7 @@ def on_error(self) -> None:
# remove 1 error per full 5s that passed since last error
self.error_count = int(max(0, self.error_count - dt_seconds // 5))

self.last_error_timestamp = datetime.utcnow()
self.last_error_timestamp = datetime.now(timezone.utc)
self.error_count = self.error_count + 1

def rebuild_nick(self) -> None:
Expand Down
6 changes: 3 additions & 3 deletions sopel/modules/remind.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from __future__ import annotations

import collections
from datetime import datetime
from datetime import datetime, timezone
import io # don't use `codecs` for loading the DB; it will split lines on some IRC formatting
import logging
import os
Expand Down Expand Up @@ -263,7 +263,7 @@ def remind_in(bot, trigger):
timezone,
trigger.nick,
trigger.sender,
datetime.utcfromtimestamp(timestamp))
datetime.fromtimestamp(timestamp, timezone.utc))
bot.reply('Okay, will remind at %s' % human_time)
else:
bot.reply('Okay, will remind in %s secs' % duration)
Expand Down Expand Up @@ -495,7 +495,7 @@ def remind_at(bot, trigger):
reminder.timezone.zone,
trigger.nick,
trigger.sender,
datetime.utcfromtimestamp(timestamp))
datetime.fromtimestamp(timestamp, timezone.utc))
bot.reply('Okay, will remind at %s' % human_time)
else:
bot.reply('Okay, will remind in %s secs' % duration)
Expand Down
10 changes: 5 additions & 5 deletions sopel/modules/uptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@
"""
from __future__ import annotations

import datetime
from datetime import datetime, timedelta, timezone

from sopel import plugin


def setup(bot):
if "start_time" not in bot.memory:
bot.memory["start_time"] = datetime.datetime.utcnow()
bot.memory["start_time"] = datetime.now(timezone.utc)


@plugin.command('uptime')
@plugin.example('.uptime', user_help=True)
@plugin.output_prefix('[uptime] ')
def uptime(bot, trigger):
"""Return the uptime of Sopel."""
delta = datetime.timedelta(seconds=round((datetime.datetime.utcnow() -
bot.memory["start_time"])
.total_seconds()))
delta = timedelta(seconds=round((datetime.now(timezone.utc) -
bot.memory["start_time"])
.total_seconds()))
bot.say("I've been sitting here for {} and I keep going!".format(delta))
48 changes: 23 additions & 25 deletions sopel/plugins/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from __future__ import annotations

import abc
import datetime
from datetime import datetime, timedelta, timezone
import functools
import inspect
import itertools
Expand All @@ -36,8 +36,6 @@
)
from urllib.parse import urlparse

import pytz

from sopel import tools
from sopel.config.core_section import (
COMMAND_DEFAULT_HELP_PREFIX, COMMAND_DEFAULT_PREFIX, URL_DEFAULT_SCHEMES)
Expand Down Expand Up @@ -461,25 +459,25 @@ def check_url_callback(self, bot, url):
class RuleMetrics:
"""Tracker of a rule's usage."""
def __init__(self) -> None:
self.started_at: Optional[datetime.datetime] = None
self.ended_at: Optional[datetime.datetime] = None
self.started_at: Optional[datetime] = None
self.ended_at: Optional[datetime] = None
self.last_return_value: Any = None

def start(self) -> None:
"""Record a starting time (before execution)."""
self.started_at = pytz.utc.localize(datetime.datetime.utcnow())
self.started_at = datetime.now(timezone.utc)

def end(self) -> None:
"""Record a ending time (after execution)."""
self.ended_at = pytz.utc.localize(datetime.datetime.utcnow())
self.ended_at = datetime.now(timezone.utc)

def set_return_value(self, value: Any) -> None:
"""Set the last return value of a rule."""
self.last_return_value = value

@property
def last_time(self) -> Optional[datetime.datetime]:
"""Last recorded start/end time for the associated rule"""
def last_time(self) -> Optional[datetime]:
"""Last recorded start/end time for the associated rule."""
# detect if we just started something or if it ended
last_time = self.started_at
if self.ended_at and self.started_at < self.ended_at:
Expand All @@ -489,7 +487,7 @@ def last_time(self) -> Optional[datetime.datetime]:

def is_limited(
self,
time_limit: datetime.datetime,
time_limit: datetime,
) -> bool:
"""Determine if the rule hits the time limit."""
if not self.started_at:
Expand Down Expand Up @@ -744,7 +742,7 @@ def is_unblockable(self) -> bool:
def is_user_rate_limited(
self,
nick: Identifier,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
"""Tell when the rule reached the ``nick``'s rate limit.

Expand All @@ -758,7 +756,7 @@ def is_user_rate_limited(
def is_channel_rate_limited(
self,
channel: Identifier,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
"""Tell when the rule reached the ``channel``'s rate limit.

Expand All @@ -771,7 +769,7 @@ def is_channel_rate_limited(
@abc.abstractmethod
def is_global_rate_limited(
self,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
"""Tell when the rule reached the global rate limit.

Expand Down Expand Up @@ -1174,43 +1172,43 @@ def get_global_metrics(self) -> RuleMetrics:
return self._metrics_global

@property
def user_rate_limit(self) -> datetime.timedelta:
return datetime.timedelta(seconds=self._user_rate_limit)
def user_rate_limit(self) -> timedelta:
return timedelta(seconds=self._user_rate_limit)

@property
def channel_rate_limit(self) -> datetime.timedelta:
return datetime.timedelta(seconds=self._channel_rate_limit)
def channel_rate_limit(self) -> timedelta:
return timedelta(seconds=self._channel_rate_limit)

@property
def global_rate_limit(self) -> datetime.timedelta:
return datetime.timedelta(seconds=self._global_rate_limit)
def global_rate_limit(self) -> timedelta:
return timedelta(seconds=self._global_rate_limit)

def is_user_rate_limited(
self,
nick: Identifier,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
if at_time is None:
at_time = pytz.utc.localize(datetime.datetime.utcnow())
at_time = datetime.now(timezone.utc)
metrics = self.get_user_metrics(nick)
return metrics.is_limited(at_time - self.user_rate_limit)

def is_channel_rate_limited(
self,
channel: Identifier,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
if at_time is None:
at_time = pytz.utc.localize(datetime.datetime.utcnow())
at_time = datetime.now(timezone.utc)
metrics = self.get_channel_metrics(channel)
return metrics.is_limited(at_time - self.channel_rate_limit)

def is_global_rate_limited(
self,
at_time: Optional[datetime.datetime] = None,
at_time: Optional[datetime] = None,
) -> bool:
if at_time is None:
at_time = pytz.utc.localize(datetime.datetime.utcnow())
at_time = datetime.now(timezone.utc)
metrics = self.get_global_metrics()
return metrics.is_limited(at_time - self.global_rate_limit)

Expand Down
10 changes: 5 additions & 5 deletions sopel/tools/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ class Duration(NamedTuple):


def validate_timezone(zone: Optional[str]) -> str:
"""Return an IETF timezone from the given IETF zone or common abbreviation.
"""Normalize and validate an IANA timezone name.

:param zone: in a strict or a human-friendly format
:return: the valid IETF timezone properly formatted
:return: the valid IANA timezone properly formatted
:raise ValueError: when ``zone`` is not a valid timezone
(including empty string and ``None`` value)

Expand Down Expand Up @@ -107,7 +107,7 @@ def validate_format(tformat: str) -> str:
.. versionadded:: 6.0
"""
try:
time = datetime.datetime.utcnow()
time = datetime.datetime.now(datetime.timezone.utc)
time.strftime(tformat)
except (ValueError, TypeError):
raise ValueError('Invalid time format.')
Expand Down Expand Up @@ -232,7 +232,7 @@ def format_time(
:param channel: channel whose time format to use, if set (optional)
:param time: the time value to format (optional)

``time``, if given, should be a ``datetime.datetime`` object, and will be
``time``, if given, should be a ``~datetime.datetime`` object, and will be
treated as being in the UTC timezone if it is :ref:`naïve
<datetime-naive-aware>`. If ``time`` is not given, the current time will
be used.
Expand All @@ -257,7 +257,7 @@ def format_time(

# get an aware datetime
if not time:
time = pytz.utc.localize(datetime.datetime.utcnow())
time = datetime.datetime.now(datetime.timezone.utc)
elif not time.tzinfo:
time = pytz.utc.localize(time)

Expand Down
10 changes: 4 additions & 6 deletions sopel/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"""
from __future__ import annotations

import datetime
from datetime import datetime, timezone
import re
from typing import (
Callable,
Expand Down Expand Up @@ -190,17 +190,15 @@ def __init__(
self.tags[tag[0]] = None

# Client time or server time
self.time = datetime.datetime.utcnow().replace(
tzinfo=datetime.timezone.utc
)
self.time = datetime.now(timezone.utc)
if 'time' in self.tags:
# ensure "time" is a string (typecheck)
tag_time = self.tags['time'] or ''
try:
self.time = datetime.datetime.strptime(
self.time = datetime.strptime(
tag_time,
"%Y-%m-%dT%H:%M:%S.%fZ",
).replace(tzinfo=datetime.timezone.utc)
).replace(tzinfo=timezone.utc)
except ValueError:
pass # Server isn't conforming to spec, ignore the server-time

Expand Down
3 changes: 1 addition & 2 deletions test/plugins/test_plugins_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import re

import pytest
import pytz

from sopel import bot, loader, plugin, trigger
from sopel.plugins import rules
Expand Down Expand Up @@ -469,7 +468,7 @@ def test_manager_has_action_command_aliases():
# tests for :class:`Manager`

def test_rulemetrics():
now = pytz.utc.localize(datetime.datetime.utcnow())
now = datetime.datetime.now(datetime.timezone.utc)
time_window = datetime.timedelta(seconds=3600)
metrics = rules.RuleMetrics()

Expand Down
2 changes: 1 addition & 1 deletion test/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,7 @@ def test_user_quit(

assert 'MrPraline' in mockbot.channels['#test'].users

servertime = datetime.utcnow() + timedelta(seconds=10)
servertime = datetime.now(timezone.utc) + timedelta(seconds=10)
mockbot.on_message(
"@time={servertime} :{user} QUIT :Ping timeout: 246 seconds".format(
servertime=servertime.strftime('%Y-%m-%dT%H:%M:%SZ'),
Expand Down