From 3a164c38c097e10b31d1e6d23a116b6f9f579cf0 Mon Sep 17 00:00:00 2001 From: Ioannis Bonatakis Date: Thu, 2 Apr 2026 08:24:00 +0200 Subject: [PATCH 1/3] Replace deprecated datetime.utcnow() with timezone-aware datetime.now(UTC) datetime.utcnow() is deprecated since Python 3.12 and returns a naive datetime object. Replaced with datetime.now(timezone.utc) across all affected modules and utilities to produce timezone-aware datetimes. --- .github/workflows/scripts/label-and-assign.py | 2 +- salt/beacons/status.py | 2 +- salt/client/ssh/__init__.py | 2 +- salt/modules/system.py | 4 +-- salt/modules/tls.py | 30 ++++++++++------ salt/modules/vsphere.py | 2 +- salt/modules/win_timezone.py | 6 ++-- salt/modules/x509.py | 34 +++++++------------ salt/spm/pkgdb/sqlite3.py | 2 +- salt/utils/aws.py | 10 +++--- salt/utils/timeutil.py | 4 +-- salt/utils/versions.py | 2 +- setup.py | 4 +-- .../pytests/functional/modules/test_system.py | 16 ++++----- tests/pytests/unit/test_fileserver.py | 2 +- tests/pytests/unit/utils/test_aws.py | 16 ++++----- tests/pytests/unit/utils/test_x509.py | 2 +- tests/unit/modules/test_x509.py | 6 ++-- tools/changelog.py | 4 +-- tools/utils/repo.py | 4 +-- 20 files changed, 76 insertions(+), 78 deletions(-) diff --git a/.github/workflows/scripts/label-and-assign.py b/.github/workflows/scripts/label-and-assign.py index 631ad44f6a89..de3d0aa79f44 100644 --- a/.github/workflows/scripts/label-and-assign.py +++ b/.github/workflows/scripts/label-and-assign.py @@ -72,7 +72,7 @@ def label_and_assign_issue(options): json.dumps( { "username": next_triage_account.login, - "when": str(datetime.datetime.utcnow()), + "when": str(datetime.datetime.now(datetime.UTC)), } ) ) diff --git a/salt/beacons/status.py b/salt/beacons/status.py index 8c1210e7dbc7..6a2ef1879812 100644 --- a/salt/beacons/status.py +++ b/salt/beacons/status.py @@ -118,7 +118,7 @@ def beacon(config): Return status for requested information """ log.debug(config) - ctime = datetime.datetime.utcnow().isoformat() + ctime = datetime.datetime.now(datetime.UTC).isoformat() whitelist = [] config = salt.utils.beacons.remove_hidden_options(config, whitelist) diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index d666dd2cc1ab..342a13169448 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -512,7 +512,7 @@ def _update_roster(self): '# Automatically added by "{s_user}" at {s_time}\n{hostname}:\n' " host: {hostname}\n user: {user}\n passwd: {passwd}\n".format( s_user=getpass.getuser(), - s_time=datetime.datetime.utcnow().isoformat(), + s_time=datetime.datetime.now(datetime.UTC).isoformat(), hostname=self.opts.get("tgt", ""), user=self.opts.get("ssh_user", ""), passwd=self.opts.get("ssh_passwd", ""), diff --git a/salt/modules/system.py b/salt/modules/system.py index 059c4c26ba8e..b26e29e29e0d 100644 --- a/salt/modules/system.py +++ b/salt/modules/system.py @@ -15,7 +15,7 @@ import logging import os.path import re -from datetime import datetime, timedelta, tzinfo +from datetime import datetime, timedelta, timezone, tzinfo import salt.utils.files import salt.utils.path @@ -258,7 +258,7 @@ def _get_offset_time(utc_offset): if utc_offset is not None: minutes = _offset_to_min(utc_offset) offset = timedelta(minutes=minutes) - offset_time = datetime.utcnow() + offset + offset_time = datetime.now(timezone.utc) + offset offset_time = offset_time.replace(tzinfo=_FixedOffset(minutes)) else: offset_time = datetime.now() diff --git a/salt/modules/tls.py b/salt/modules/tls.py index 85e599cccf36..38f47be0f8c1 100644 --- a/salt/modules/tls.py +++ b/salt/modules/tls.py @@ -105,7 +105,7 @@ import os import re import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import salt.utils.data import salt.utils.files @@ -365,7 +365,7 @@ def maybe_fix_ssl_version(ca_name, cacert_path=None, ca_filename=None): try: days = ( datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ") - - datetime.utcnow() + - datetime.now(timezone.utc) ).days except (ValueError, TypeError): days = 365 @@ -593,8 +593,10 @@ def validate(cert, ca_name, crl_file): builder = x509.CertificateRevocationListBuilder() builder = builder.issuer_name(ca_x509.subject) - builder = builder.last_update(datetime.utcnow()) - builder = builder.next_update(datetime.utcnow() + timedelta(days=36500)) + builder = builder.last_update(datetime.now(timezone.utc)) + builder = builder.next_update( + datetime.now(timezone.utc) + timedelta(days=36500) + ) # Load existing revocations from index file if it exists index_file = f"{ca_dir}/index.txt" @@ -845,7 +847,7 @@ def create_ca( err, ) bck = "{}.unloadable.{}".format( - ca_keyp, datetime.utcnow().strftime("%Y%m%d%H%M%S") + ca_keyp, datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S") ) log.info("Saving unloadable CA ssl key in %s", bck) os.rename(ca_keyp, bck) @@ -903,7 +905,9 @@ def create_ca( keycontent = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) write_key = True if os.path.exists(ca_keyp): - bck = "{}.{}".format(ca_keyp, datetime.utcnow().strftime("%Y%m%d%H%M%S")) + bck = "{}.{}".format( + ca_keyp, datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S") + ) with salt.utils.files.fopen(ca_keyp) as fic: old_key = salt.utils.stringutils.to_unicode(fic.read()).strip() if old_key.strip() == keycontent.strip(): @@ -1905,8 +1909,10 @@ def create_empty_crl( builder = x509.CertificateRevocationListBuilder() builder = builder.issuer_name(ca_x509.subject) - builder = builder.last_update(datetime.utcnow()) - builder = builder.next_update(datetime.utcnow() + timedelta(days=36500)) + builder = builder.last_update(datetime.now(timezone.utc)) + builder = builder.next_update( + datetime.now(timezone.utc) + timedelta(days=36500) + ) # Mapping digest strings to cryptography hashes hash_algo = getattr(hashes, digest.upper(), hashes.SHA256)() @@ -2021,7 +2027,7 @@ def revoke_cert( ) index_r_data = "R\t{}\t{}\t{}".format( expire_date, - _four_digit_year_to_two_digit(datetime.utcnow()), + _four_digit_year_to_two_digit(datetime.now(timezone.utc)), index_serial_subject, ) @@ -2063,8 +2069,10 @@ def revoke_cert( builder = x509.CertificateRevocationListBuilder() builder = builder.issuer_name(ca_x509.subject) - builder = builder.last_update(datetime.utcnow()) - builder = builder.next_update(datetime.utcnow() + timedelta(days=36500)) + builder = builder.last_update(datetime.now(timezone.utc)) + builder = builder.next_update( + datetime.now(timezone.utc) + timedelta(days=36500) + ) with salt.utils.files.fopen(index_file) as fp_: for line in fp_: diff --git a/salt/modules/vsphere.py b/salt/modules/vsphere.py index ff62e1ca3c65..894612793ca3 100644 --- a/salt/modules/vsphere.py +++ b/salt/modules/vsphere.py @@ -3877,7 +3877,7 @@ def update_host_datetime( host_ref = _get_host_ref(service_instance, host, host_name=host_name) date_time_manager = _get_date_time_mgr(host_ref) try: - date_time_manager.UpdateDateTime(datetime.datetime.utcnow()) + date_time_manager.UpdateDateTime(datetime.datetime.now(datetime.UTC)) except vim.fault.HostConfigFault as err: msg = "'vsphere.update_date_time' failed for host {}: {}".format( host_name, err diff --git a/salt/modules/win_timezone.py b/salt/modules/win_timezone.py index 85807a7503d0..f06e68930fb8 100644 --- a/salt/modules/win_timezone.py +++ b/salt/modules/win_timezone.py @@ -3,7 +3,7 @@ """ import logging -from datetime import datetime +from datetime import datetime, timezone from salt.exceptions import CommandExecutionError @@ -242,7 +242,7 @@ def get_offset(): """ # http://craigglennie.com/programming/python/2013/07/21/working-with-timezones-using-Python-and-pytz-localize-vs-normalize/ tz_object = pytz.timezone(get_zone()) - utc_time = pytz.utc.localize(datetime.utcnow()) + utc_time = datetime.now(timezone.utc) loc_time = utc_time.astimezone(tz_object) norm_time = tz_object.normalize(loc_time) return norm_time.strftime("%z") @@ -262,7 +262,7 @@ def get_zonecode(): salt '*' timezone.get_zonecode """ tz_object = pytz.timezone(get_zone()) - loc_time = tz_object.localize(datetime.utcnow()) + loc_time = datetime.now(timezone.utc).astimezone(tz_object) return loc_time.tzname() diff --git a/salt/modules/x509.py b/salt/modules/x509.py index 0e68d38116be..2e8dcebdb6cb 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -20,7 +20,6 @@ import ast import ctypes -import datetime import glob import hashlib import logging @@ -30,6 +29,7 @@ import sys import tempfile from collections import OrderedDict +from datetime import datetime, timedelta, timezone import salt.exceptions import salt.utils.data @@ -256,15 +256,11 @@ def _parse_openssl_crl(crl_filename): crl["Issuer"] = subject if line.startswith("Last Update: "): crl["Last Update"] = line.replace("Last Update: ", "") - last_update = datetime.datetime.strptime( - crl["Last Update"], "%b %d %H:%M:%S %Y %Z" - ) + last_update = datetime.strptime(crl["Last Update"], "%b %d %H:%M:%S %Y %Z") crl["Last Update"] = last_update.strftime("%Y-%m-%d %H:%M:%S") if line.startswith("Next Update: "): crl["Next Update"] = line.replace("Next Update: ", "") - next_update = datetime.datetime.strptime( - crl["Next Update"], "%b %d %H:%M:%S %Y %Z" - ) + next_update = datetime.strptime(crl["Next Update"], "%b %d %H:%M:%S %Y %Z") crl["Next Update"] = next_update.strftime("%Y-%m-%d %H:%M:%S") if line.startswith("Revoked Certificates:"): break @@ -286,7 +282,7 @@ def _parse_openssl_crl(crl_filename): rev_yaml = salt.utils.data.decode(salt.utils.yaml.safe_load(revoked)) for rev_values in rev_yaml.values(): if "Revocation Date" in rev_values: - rev_date = datetime.datetime.strptime( + rev_date = datetime.strptime( rev_values["Revocation Date"], "%b %d %H:%M:%S %Y %Z" ) rev_values["Revocation Date"] = rev_date.strftime("%Y-%m-%d %H:%M:%S") @@ -1004,20 +1000,14 @@ def create_crl( serial_number = salt.utils.stringutils.to_bytes(serial_number) if "not_after" in rev_item and not include_expired: - not_after = datetime.datetime.strptime( - rev_item["not_after"], "%Y-%m-%d %H:%M:%S" - ) - if datetime.datetime.now() > not_after: + not_after = datetime.strptime(rev_item["not_after"], "%Y-%m-%d %H:%M:%S") + if datetime.now() > not_after: continue if "revocation_date" not in rev_item: - rev_item["revocation_date"] = datetime.datetime.now().strftime( - "%Y-%m-%d %H:%M:%S" - ) + rev_item["revocation_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - rev_date = datetime.datetime.strptime( - rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S" - ) + rev_date = datetime.strptime(rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S") rev_date = rev_date.strftime("%Y%m%d%H%M%SZ") rev_date = salt.utils.stringutils.to_bytes(rev_date) @@ -1538,7 +1528,7 @@ def create_certificate(path=None, text=False, overwrite=True, ca_server=None, ** fmt = "%Y-%m-%d %H:%M:%S" if "not_before" in kwargs: try: - time = datetime.datetime.strptime(kwargs["not_before"], fmt) + time = datetime.strptime(kwargs["not_before"], fmt) except: raise salt.exceptions.SaltInvocationError( "not_before: {} is not in required format {}".format( @@ -1556,7 +1546,7 @@ def create_certificate(path=None, text=False, overwrite=True, ca_server=None, ** if "not_after" in kwargs: try: - time = datetime.datetime.strptime(kwargs["not_after"], fmt) + time = datetime.strptime(kwargs["not_after"], fmt) except: raise salt.exceptions.SaltInvocationError( "not_after: {} is not in required format {}".format( @@ -1963,7 +1953,7 @@ def expired(certificate): ret["path"] = certificate cert = _get_certificate_obj(certificate) - _now = datetime.datetime.utcnow() + _now = datetime.now(timezone.utc) _expiration_date = cert.get_not_after().get_datetime() ret["cn"] = _parse_subject(cert.get_subject())["CN"] @@ -2007,7 +1997,7 @@ def will_expire(certificate, days): cert = _get_certificate_obj(certificate) - _check_time = datetime.datetime.utcnow() + datetime.timedelta(days=days) + _check_time = datetime.now(timezone.utc) + timedelta(days=days) _expiration_date = cert.get_not_after().get_datetime() ret["cn"] = _parse_subject(cert.get_subject())["CN"] diff --git a/salt/spm/pkgdb/sqlite3.py b/salt/spm/pkgdb/sqlite3.py index c6c0fb1384f1..a17ef22e289b 100644 --- a/salt/spm/pkgdb/sqlite3.py +++ b/salt/spm/pkgdb/sqlite3.py @@ -166,7 +166,7 @@ def register_pkg(name, formula_def, conn=None): name, formula_def["version"], formula_def["release"], - datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT"), + datetime.datetime.now(datetime.UTC).strftime("%a, %d %b %Y %H:%M:%S GMT"), formula_def.get("os", None), formula_def.get("os_family", None), formula_def.get("dependencies", None), diff --git a/salt/utils/aws.py b/salt/utils/aws.py index 615aee19040c..a6cf9b523f37 100644 --- a/salt/utils/aws.py +++ b/salt/utils/aws.py @@ -18,7 +18,7 @@ import time import urllib.parse import xml.etree.ElementTree as ET -from datetime import datetime +from datetime import datetime, timezone import requests @@ -121,7 +121,7 @@ def creds(provider): ## if needed if provider["id"] == IROLE_CODE or provider["key"] == IROLE_CODE: # Check to see if we have cache credentials that are still good - if not __Expiration__ or __Expiration__ < datetime.utcnow().strftime( + if not __Expiration__ or __Expiration__ < datetime.now(timezone.utc).strftime( "%Y-%m-%dT%H:%M:%SZ" ): # We don't have any cached credentials, or they are expired, get them @@ -164,7 +164,7 @@ def sig2(method, endpoint, params, provider, aws_api_version): http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html """ - timenow = datetime.utcnow() + timenow = datetime.now(timezone.utc) timestamp = timenow.strftime("%Y-%m-%dT%H:%M:%SZ") # Retrieve access credentials from meta-data, or use provided @@ -201,7 +201,7 @@ def assumed_creds(prov_dict, role_arn, location=None): valid_session_name_re = re.compile("[^a-z0-9A-Z+=,.@-]") # current time in epoch seconds - now = time.mktime(datetime.utcnow().timetuple()) + now = time.mktime(datetime.now(timezone.utc).timetuple()) for key, creds in copy.deepcopy(__AssumeCache__).items(): if (creds["Expiration"] - now) <= 120: @@ -281,7 +281,7 @@ def sig4( http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html """ - timenow = datetime.utcnow() + timenow = datetime.now(timezone.utc) # Retrieve access credentials from meta-data, or use provided if role_arn is None: diff --git a/salt/utils/timeutil.py b/salt/utils/timeutil.py index 0985ec504be6..742a67f2d194 100644 --- a/salt/utils/timeutil.py +++ b/salt/utils/timeutil.py @@ -6,7 +6,7 @@ import logging import re import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone # Import Salt modules @@ -34,7 +34,7 @@ def get_timestamp_at(time_in=None, time_at=None): minutes = 0 hours, minutes = int(hours), int(minutes) dt = timedelta(hours=hours, minutes=minutes) - time_now = datetime.utcnow() + time_now = datetime.now(timezone.utc) time_at = time_now + dt return time.mktime(time_at.timetuple()) elif time_at: diff --git a/salt/utils/versions.py b/salt/utils/versions.py index d64d2d66f3fd..c758bd2ea9c4 100644 --- a/salt/utils/versions.py +++ b/salt/utils/versions.py @@ -244,7 +244,7 @@ def warn_until_date( # Attribute the warning to the calling function, not to warn_until_date() stacklevel = 2 - today = _current_date or datetime.datetime.utcnow().date() + today = _current_date or datetime.datetime.now(datetime.UTC).date() if today >= date: caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) deprecated_message = ( diff --git a/setup.py b/setup.py index 5a0e6077bbe0..51b4fd4cdc8b 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ import subprocess import sys import warnings -from datetime import datetime +from datetime import datetime, timezone # pylint: disable=no-name-in-module from distutils import log @@ -54,7 +54,7 @@ try: DATE = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) except (KeyError, ValueError): - DATE = datetime.utcnow() + DATE = datetime.now(timezone.utc) # Change to salt source's directory prior to running any command try: diff --git a/tests/pytests/functional/modules/test_system.py b/tests/pytests/functional/modules/test_system.py index a3d74462d78f..700a9d3edc01 100644 --- a/tests/pytests/functional/modules/test_system.py +++ b/tests/pytests/functional/modules/test_system.py @@ -68,7 +68,7 @@ def fmt_str(): @pytest.fixture(scope="function") def setup_teardown_vars(file, service, system): _systemd_timesyncd_available_ = None - _orig_time = datetime.datetime.utcnow() + _orig_time = datetime.datetime.now(datetime.UTC) if os.path.isfile("/etc/machine-info"): with salt.utils.files.fopen("/etc/machine-info", "r") as mach_info: @@ -218,7 +218,7 @@ def test_get_system_date_time_utc(setup_teardown_vars, system, fmt_str): """ Test we are able to get the correct time with utc """ - t1 = datetime.datetime.utcnow() + t1 = datetime.datetime.now(datetime.UTC) res = system.get_system_date_time("+0000") t2 = datetime.datetime.strptime(res, fmt_str) msg = f"Difference in times is too large. Now: {t1} Fake: {t2}" @@ -254,10 +254,10 @@ def test_set_system_date_time_utc(setup_teardown_vars, system, hwclock_has_compa Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. """ - cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) + cmp_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=7) result = _set_time(system, cmp_time, offset="+0000") - time_now = datetime.datetime.utcnow() + time_now = datetime.datetime.now(datetime.UTC) msg = "Difference in times is too large. Now: {} Fake: {}".format( time_now, cmp_time @@ -277,11 +277,11 @@ def test_set_system_date_time_utcoffset_east( Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. """ - cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) + cmp_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=7) # 25200 seconds = 7 hours time_to_set = cmp_time - datetime.timedelta(seconds=25200) result = _set_time(system, time_to_set, offset="-0700") - time_now = datetime.datetime.utcnow() + time_now = datetime.datetime.now(datetime.UTC) msg = "Difference in times is too large. Now: {} Fake: {}".format( time_now, cmp_time @@ -301,11 +301,11 @@ def test_set_system_date_time_utcoffset_west( Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. """ - cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) + cmp_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=7) # 7200 seconds = 2 hours time_to_set = cmp_time + datetime.timedelta(seconds=7200) result = _set_time(system, time_to_set, offset="+0200") - time_now = datetime.datetime.utcnow() + time_now = datetime.datetime.now(datetime.UTC) msg = "Difference in times is too large. Now: {} Fake: {}".format( time_now, cmp_time diff --git a/tests/pytests/unit/test_fileserver.py b/tests/pytests/unit/test_fileserver.py index 49be3967dc40..a6dbfb7472e2 100644 --- a/tests/pytests/unit/test_fileserver.py +++ b/tests/pytests/unit/test_fileserver.py @@ -52,7 +52,7 @@ def test_future_file_list_cache_file_ignored(tmp_path): _f.write(b"\x80") # Set modification time to file list cache file to 1 year in the future - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) future = now + datetime.timedelta(days=365) mod_time = time.mktime(future.timetuple()) os.utime(os.path.join(back_cachedir, "base.p"), (mod_time, mod_time)) diff --git a/tests/pytests/unit/utils/test_aws.py b/tests/pytests/unit/utils/test_aws.py index a7ab2710a427..0b199c7e3306 100644 --- a/tests/pytests/unit/utils/test_aws.py +++ b/tests/pytests/unit/utils/test_aws.py @@ -8,7 +8,7 @@ import io import os import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import pytest import requests @@ -79,11 +79,11 @@ def handle_get_mock(_, **args): def test_assumed_creds_not_updating_dictionary_while_iterating(): mock_cache = { "expired": { - "Expiration": time.mktime(datetime.utcnow().timetuple()), + "Expiration": time.mktime(datetime.now(timezone.utc).timetuple()), }, "not_expired_1": { "Expiration": time.mktime( - (datetime.utcnow() + timedelta(days=1)).timetuple() + (datetime.now(timezone.utc) + timedelta(days=1)).timetuple() ), "AccessKeyId": "mock_AccessKeyId", "SecretAccessKey": "mock_SecretAccessKey", @@ -91,7 +91,7 @@ def test_assumed_creds_not_updating_dictionary_while_iterating(): }, "not_expired_2": { "Expiration": time.mktime( - (datetime.utcnow() + timedelta(seconds=300)).timetuple() + (datetime.now(timezone.utc) + timedelta(seconds=300)).timetuple() ), }, } @@ -104,11 +104,11 @@ def test_assumed_creds_not_updating_dictionary_while_iterating(): def test_assumed_creds_deletes_expired_key(): mock_cache = { "expired": { - "Expiration": time.mktime(datetime.utcnow().timetuple()), + "Expiration": time.mktime(datetime.now(timezone.utc).timetuple()), }, "not_expired_1": { "Expiration": time.mktime( - (datetime.utcnow() + timedelta(days=1)).timetuple() + (datetime.now(timezone.utc) + timedelta(days=1)).timetuple() ), "AccessKeyId": "mock_AccessKeyId", "SecretAccessKey": "mock_SecretAccessKey", @@ -116,7 +116,7 @@ def test_assumed_creds_deletes_expired_key(): }, "not_expired_2": { "Expiration": time.mktime( - (datetime.utcnow() + timedelta(seconds=300)).timetuple() + (datetime.now(timezone.utc) + timedelta(seconds=300)).timetuple() ), }, } @@ -153,7 +153,7 @@ def test_creds_with_role_arn_should_always_call_assumed_creds(): access_key_id = "mock_AccessKeyId" secret_access_key = "mock_SecretAccessKey" token = "mock_Token" - expiration = (datetime.utcnow() + timedelta(seconds=900)).strftime( + expiration = (datetime.now(timezone.utc) + timedelta(seconds=900)).strftime( "%Y-%m-%dT%H:%M:%SZ" ) diff --git a/tests/pytests/unit/utils/test_x509.py b/tests/pytests/unit/utils/test_x509.py index c4d7c287e114..0abfa2c94223 100644 --- a/tests/pytests/unit/utils/test_x509.py +++ b/tests/pytests/unit/utils/test_x509.py @@ -1965,7 +1965,7 @@ def dtn(tz=None): with patch("salt.utils.x509.datetime") as fakedate: fakedate.today.return_value = curr_time_naive fakedate.now.side_effect = dtn - fakedate.utcnow.return_value = curr_time_utc_naive + fakedate.now(datetime.UTC).return_value = curr_time_utc_naive builder, _ = x509.build_crl(privkey, [], signing_cert=cert) crl = builder.sign(privkey, algorithm=cprim.hashes.SHA256()) try: diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py index f1ca5bb45a3d..4acf855ea397 100644 --- a/tests/unit/modules/test_x509.py +++ b/tests/unit/modules/test_x509.py @@ -185,7 +185,7 @@ def test_create_certificate_with_not_after(self): fmt = "%Y-%m-%d %H:%M:%S" # We also gonna use the current date in UTC format for verification - not_after = datetime.datetime.utcnow() + not_after = datetime.datetime.now(datetime.UTC) # And set the UTC timezone to the naive datetime resulting from parsing not_after = not_after.replace(tzinfo=M2Crypto.ASN1.UTC) not_after_str = datetime.datetime.strftime(not_after, fmt) @@ -226,7 +226,7 @@ def test_create_certificate_with_not_before(self): fmt = "%Y-%m-%d %H:%M:%S" # We also gonna use the current date in UTC format for verification - not_before = datetime.datetime.utcnow() + not_before = datetime.datetime.now(datetime.UTC) # And set the UTC timezone to the naive datetime resulting from parsing not_before = not_before.replace(tzinfo=M2Crypto.ASN1.UTC) not_before_str = datetime.datetime.strftime(not_before, fmt) @@ -319,7 +319,7 @@ def test_create_certificate_with_not_before_and_not_after(self): fmt = "%Y-%m-%d %H:%M:%S" # Here we gonna use the current date as the not_before date # First we again take the UTC for verification - not_before = datetime.datetime.utcnow() + not_before = datetime.datetime.now(datetime.UTC) # And set the UTC timezone to the naive datetime resulting from parsing not_before = not_before.replace(tzinfo=M2Crypto.ASN1.UTC) not_before_str = datetime.datetime.strftime(not_before, fmt) diff --git a/tools/changelog.py b/tools/changelog.py index 4f54684ad0ae..c35196ed0361 100644 --- a/tools/changelog.py +++ b/tools/changelog.py @@ -107,7 +107,7 @@ def update_rpm(ctx: Context, salt_version: Version, draft: bool = False): capture=True, check=True, ).stdout.decode() - dt = datetime.datetime.utcnow() + dt = datetime.datetime.now(datetime.UTC) date = dt.strftime("%a %b %d %Y") header = f"* {date} Salt Project Packaging - {str_salt_version}\n" parts = orig.split("%changelog") @@ -150,7 +150,7 @@ def update_deb(ctx: Context, salt_version: Version, draft: bool = False): salt_version = _get_salt_version(ctx) changes = _get_pkg_changelog_contents(ctx, salt_version) formated = "\n".join([f" {_.replace('-', '*', 1)}" for _ in changes.split("\n")]) - dt = datetime.datetime.utcnow() + dt = datetime.datetime.now(datetime.UTC) date = dt.strftime("%a, %d %b %Y %H:%M:%S +0000") tmpchanges = "pkg/rpm/salt.spec.1" debian_changelog_path = "pkg/debian/changelog" diff --git a/tools/utils/repo.py b/tools/utils/repo.py index 075f86ab0c9c..e06b1ddae8bb 100644 --- a/tools/utils/repo.py +++ b/tools/utils/repo.py @@ -4,7 +4,7 @@ import json import pathlib import sys -from datetime import datetime +from datetime import datetime, timezone from typing import Any from ptscripts import Context @@ -87,7 +87,7 @@ def create_top_level_repo_path( create_repo_path / "salt-dev" / nightly_build_from - / datetime.utcnow().strftime("%Y-%m-%d") + / datetime.now(timezone.utc).strftime("%Y-%m-%d") ) create_repo_path.mkdir(exist_ok=True, parents=True) with ctx.chdir(create_repo_path.parent): From e1fa91b973f84026226bd576c6184b5e6bcd4e12 Mon Sep 17 00:00:00 2001 From: Ioannis Bonatakis Date: Thu, 2 Apr 2026 08:24:11 +0200 Subject: [PATCH 2/3] Fix pre-commit lint hooks broken on Python 3.13 drop `pip<21.2 and setuptools<58.0 from `.pre-commit-config.yaml` as those versions depend on distutils which was removed in Python 3.13 and seems had dropped in other place already. Nevertheless the pylint keeps complaining about W8410 (3rd-party-module-not-gated) and it was explicitely excluded to avoid the pulint from failing. --- .pre-commit-config.yaml | 8 ++++---- setup.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7134a24600de..b3531adc90ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1815,8 +1815,8 @@ repos: - -- additional_dependencies: - nox==2022.11.21 - - setuptools<58.0 - - pip>=20.2.4,<21.2 + - setuptools + - pip - repo: local hooks: @@ -1831,6 +1831,6 @@ repos: - -- additional_dependencies: - nox==2022.11.21 - - setuptools<58.0 - - pip>=20.2.4,<21.2 + - setuptools + - pip # <---- Pre-Commit ------------------------------------------------------------------------------------------------- diff --git a/setup.py b/setup.py index 51b4fd4cdc8b..7c045472a788 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ The setup script for salt """ -# pylint: disable=file-perms,resource-leakage,deprecated-module +# pylint: disable=file-perms,resource-leakage,deprecated-module,3rd-party-module-not-gated import setuptools # isort:skip import distutils.dist import glob From 586ffa3272e7b2479f82bc9d764433deb23a8c24 Mon Sep 17 00:00:00 2001 From: Ioannis Bonatakis Date: Thu, 2 Apr 2026 08:54:08 +0200 Subject: [PATCH 3/3] Changelog entry Update for issue 65604 Signed-off-by: Ioannis Bonatakis --- changelog/65604.fixed.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog/65604.fixed.md b/changelog/65604.fixed.md index 2e7a9fe75c0c..e0530e010b6f 100644 --- a/changelog/65604.fixed.md +++ b/changelog/65604.fixed.md @@ -1 +1,2 @@ -Fixed some instances of deprecated datetime.datetime.utcnow() +Fixed deprecated datetime.datetime.utcnow() calls across modules and utilities. +Fixed pre-commit lint hooks broken on Python 3.13 due to distutils removal.