Skip to content

Commit

Permalink
Use timezone-aware datetime objects, for UTC too
Browse files Browse the repository at this point in the history
datetime.datetime.utcnow() is deprecated in Python 3.12, and it's
recommended to switch to timezone-aware objects, so do this. It also
simplifies local time handling, as .astimezone() method can be used
instead of calculating timezone_delta manually.

Part of saltstack#65604
  • Loading branch information
marmarek committed Sep 19, 2024
1 parent 0eace02 commit 32bfb19
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 27 deletions.
4 changes: 2 additions & 2 deletions salt/grains/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2906,12 +2906,12 @@ def ip_fqdn():
if not ret["ipv" + ipv_num]:
ret[key] = []
else:
start_time = datetime.datetime.utcnow()
start_time = datetime.datetime.now(tz=datetime.timezone.utc)
try:
info = socket.getaddrinfo(_fqdn, None, socket_type)
ret[key] = list({item[4][0] for item in info})
except (OSError, UnicodeError):
timediff = datetime.datetime.utcnow() - start_time
timediff = datetime.datetime.now(tz=datetime.timezone.utc) - start_time
if timediff.seconds > 5 and __opts__["__role"] == "master":
log.warning(
'Unable to find IPv%s record for "%s" causing a %s '
Expand Down
34 changes: 14 additions & 20 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,9 @@ def _calculate_fake_duration():
Generate a NULL duration for when states do not run
but we want the results to be consistent.
"""
utc_start_time = datetime.datetime.utcnow()
local_start_time = utc_start_time - (
datetime.datetime.utcnow() - datetime.datetime.now()
)
utc_finish_time = datetime.datetime.utcnow()
utc_start_time = datetime.datetime.now(tz=datetime.timezone.utc)
local_start_time = utc_start_time.astimezone()
utc_finish_time = datetime.datetime.now(tz=datetime.timezone.utc)
start_time = local_start_time.time().isoformat()
delta = utc_finish_time - utc_start_time
# duration in milliseconds.microseconds
Expand Down Expand Up @@ -2153,7 +2151,7 @@ def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low):
instance = cls(**init_kwargs)
# we need to re-record start/end duration here because it is impossible to
# correctly calculate further down the chain
utc_start_time = datetime.datetime.utcnow()
utc_start_time = datetime.datetime.now(tz=datetime.timezone.utc)

instance.format_slots(cdata)
tag = _gen_tag(low)
Expand All @@ -2173,10 +2171,9 @@ def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low):
"comment": f"An exception occurred in this state: {trb}",
}

utc_finish_time = datetime.datetime.utcnow()
timezone_delta = datetime.datetime.utcnow() - datetime.datetime.now()
local_finish_time = utc_finish_time - timezone_delta
local_start_time = utc_start_time - timezone_delta
utc_finish_time = datetime.datetime.now(tz=datetime.timezone.utc)
local_finish_time = utc_finish_time.astimezone()
local_start_time = utc_start_time.astimezone()
ret["start_time"] = local_start_time.time().isoformat()
delta = utc_finish_time - utc_start_time
# duration in milliseconds.microseconds
Expand Down Expand Up @@ -2206,8 +2203,8 @@ def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low):
*cdata["args"], **cdata["kwargs"]
)

utc_start_time = datetime.datetime.utcnow()
utc_finish_time = datetime.datetime.utcnow()
utc_start_time = datetime.datetime.now(tz=datetime.timezone.utc)
utc_finish_time = datetime.datetime.now(tz=datetime.timezone.utc)
delta = utc_finish_time - utc_start_time
duration = (delta.seconds * 1000000 + delta.microseconds) / 1000.0
retry_ret["duration"] = duration
Expand Down Expand Up @@ -2294,10 +2291,8 @@ def call(self, low, chunks=None, running=None, retries=1):
Call a state directly with the low data structure, verify data
before processing.
"""
utc_start_time = datetime.datetime.utcnow()
local_start_time = utc_start_time - (
datetime.datetime.utcnow() - datetime.datetime.now()
)
utc_start_time = datetime.datetime.now(tz=datetime.timezone.utc)
local_start_time = utc_start_time.astimezone()
log.info(
"Running state [%s] at time %s",
low["name"].strip() if isinstance(low["name"], str) else low["name"],
Expand Down Expand Up @@ -2486,10 +2481,9 @@ def call(self, low, chunks=None, running=None, retries=1):
self.__run_num += 1
format_log(ret)
self.check_refresh(low, ret)
utc_finish_time = datetime.datetime.utcnow()
timezone_delta = datetime.datetime.utcnow() - datetime.datetime.now()
local_finish_time = utc_finish_time - timezone_delta
local_start_time = utc_start_time - timezone_delta
utc_finish_time = datetime.datetime.now(tz=datetime.timezone.utc)
local_finish_time = utc_finish_time.astimezone()
local_start_time = utc_start_time.astimezone()
ret["start_time"] = local_start_time.time().isoformat()
delta = utc_finish_time - utc_start_time
# duration in milliseconds.microseconds
Expand Down
2 changes: 1 addition & 1 deletion salt/utils/jid.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def _utc_now():
"""
Helper method so tests do not have to patch the built-in method.
"""
return datetime.datetime.utcnow()
return datetime.datetime.now(tz=datetime.timezone.utc)


def gen_jid(opts):
Expand Down
5 changes: 4 additions & 1 deletion salt/utils/pkg/rpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ def parse_pkginfo(line, osarch=None):

if install_time not in ("(none)", "0"):
install_date = (
datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z"
datetime.datetime.fromtimestamp(
int(install_time), datetime.timezone.utc
).isoformat()
+ "Z"
)
install_date_time_t = int(install_time)
else:
Expand Down
4 changes: 3 additions & 1 deletion tests/pytests/unit/grains/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import tempfile
import textwrap
import uuid
import warnings
from collections import namedtuple

import pytest
Expand Down Expand Up @@ -2151,7 +2152,8 @@ def _check_type(key, value, ip4_empty, ip6_empty):
salt.utils.network, "ip_addrs6", MagicMock(return_value=net_ip6_mock)
), patch.object(
core.socket, "getaddrinfo", side_effect=_getaddrinfo
):
), warnings.catch_warnings():
warnings.simplefilter("error")
get_fqdn = core.ip_fqdn()
ret_keys = ["fqdn_ip4", "fqdn_ip6", "ipv4", "ipv6"]
for key in ret_keys:
Expand Down
9 changes: 7 additions & 2 deletions tests/pytests/unit/state/test_state_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Test functions in state.py that are not a part of a class
"""

import warnings

import pytest

import salt.state
Expand Down Expand Up @@ -139,8 +141,11 @@ def test_state_args_id_not_high():
),
]
)
ret = salt.state.state_args(id_, state, high)
assert ret == set()
with warnings.catch_warnings():
warnings.simplefilter("error")
salt.utils.jid.gen_jid({})
ret = salt.state.state_args(id_, state, high)
assert ret == set()


def test_state_args_state_not_high():
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/utils/test_jid.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import datetime
import os
import warnings

import salt.utils.jid
from tests.support.mock import patch
Expand Down Expand Up @@ -49,3 +50,8 @@ def test_deprecation_58225(self):
self.assertEqual(
str(no_opts), "gen_jid() missing 1 required positional argument: 'opts'"
)

def test_deprecation_65604(self):
with warnings.catch_warnings():
warnings.simplefilter("error")
salt.utils.jid.gen_jid({})

0 comments on commit 32bfb19

Please sign in to comment.