AAP-59874: Update to Python 3.12 (#16208)

* update to Python 3.12

* remove use of utcnow

* switch to timezone.utc

datetime.UTC is an alias of datetime.timezone.utc. if we're doing the double import for datetime it's more straightforward to just import timezone as well and get it directly

* debug python env version issue

* change python version

* pin to SHA and remove debug portion
This commit is contained in:
jessicamack
2026-01-07 11:57:24 -05:00
committed by GitHub
parent 48c7534b57
commit de86b93690
29 changed files with 124 additions and 85 deletions

View File

@@ -198,8 +198,8 @@ def generate_receptor_tls(instance_obj):
.issuer_name(ca_cert.issuer)
.public_key(csr.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.utcnow())
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
.not_valid_before(datetime.datetime.now(datetime.UTC))
.not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3650))
.add_extension(
csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).value,
critical=csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).critical,

View File

@@ -5,7 +5,7 @@ import signal
import sys
import time
import traceback
from datetime import datetime
from datetime import datetime, timezone
from uuid import uuid4
import json
@@ -301,7 +301,7 @@ class WorkerPool(object):
'\n'
'{% endfor %}'
)
now = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')
now = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
return tmpl.render(pool=self, workers=self.workers, meta=self.debug_meta, dt=now)
def write(self, preferred_queue, body):

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved
import sys
from distutils.util import strtobool
from argparse import RawTextHelpFormatter
from django.core.management.base import BaseCommand
@@ -9,6 +8,24 @@ from django.conf import settings
from awx.main.models import CredentialType, Credential, ExecutionEnvironment
def strtobool(val):
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'.
False values are 'n', 'no', 'f', 'false', 'off', and '0'.
Raises ValueError if 'val' is anything else.
This replaces the deprecated distutils.util.strtobool removed in Python 3.12.
"""
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError(f"invalid truth value {val!r}")
class Command(BaseCommand):
"""Create default execution environments, intended for new installs"""

View File

@@ -422,7 +422,7 @@ class BaseTask(object):
except IOError as e:
if e.errno not in (errno.EAGAIN, errno.EACCES):
os.close(self.lock_fd)
logger.error("I/O error({0}) while trying to aquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
logger.error("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
raise
else:
if not emitted_lockfile_log:

View File

@@ -10,7 +10,7 @@ import time
from collections import namedtuple
from contextlib import redirect_stdout
from datetime import datetime
from distutils.version import LooseVersion as Version
from packaging.version import Version
from io import StringIO
# dispatcherd

View File

@@ -88,7 +88,7 @@ class TestSwaggerGeneration:
JSON['info']['version'] = release
if not request.config.getoption('--genschema'):
JSON['modified'] = datetime.datetime.utcnow().isoformat()
JSON['modified'] = datetime.datetime.now(datetime.UTC).isoformat()
# Make some basic assertions about the rendered JSON so we can
# be sure it doesn't break across DRF upgrades and view/serializer

View File

@@ -139,7 +139,7 @@ def test_send_notifications_job_id(mocker):
mocker.patch('awx.main.models.UnifiedJob.objects.get')
system.send_notifications([], job_id=1)
assert UnifiedJob.objects.get.called
assert UnifiedJob.objects.get.called_with(id=1)
UnifiedJob.objects.get.assert_called_with(id=1)
@mock.patch('awx.main.models.UnifiedJob.objects.get')
@@ -156,7 +156,7 @@ def test_send_notifications_list(mock_notifications_filter, mock_job_get, mocker
assert mock_notifications[0].save.called
assert mock_job.notifications.add.called
assert mock_job.notifications.add.called_with(*mock_notifications)
mock_job.notifications.add.assert_called_with(*mock_notifications)
@pytest.mark.parametrize(
@@ -1508,8 +1508,8 @@ def test_fcntl_ioerror():
@mock.patch('os.open')
@mock.patch('logging.getLogger')
def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me):
@mock.patch('awx.main.tasks.jobs.logger')
def test_acquire_lock_open_fail_logged(logger_mock, os_open, mock_me):
err = OSError()
err.errno = 3
err.strerror = 'dummy message'
@@ -1519,21 +1519,18 @@ def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me):
os_open.side_effect = err
logger = mock.Mock()
logging_getLogger.return_value = logger
ProjectUpdate = jobs.RunProjectUpdate()
with pytest.raises(OSError):
ProjectUpdate.acquire_lock(instance)
assert logger.err.called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
logger_mock.error.assert_called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
@mock.patch('os.open')
@mock.patch('os.close')
@mock.patch('logging.getLogger')
@mock.patch('awx.main.tasks.jobs.logger')
@mock.patch('fcntl.lockf')
def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os_close, os_open, mock_me):
def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logger_mock, os_close, os_open, mock_me):
err = IOError()
err.errno = 3
err.strerror = 'dummy message'
@@ -1544,16 +1541,15 @@ def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os
os_open.return_value = 3
logger = mock.Mock()
logging_getLogger.return_value = logger
fcntl_lockf.side_effect = err
ProjectUpdate = jobs.RunProjectUpdate()
with pytest.raises(IOError):
ProjectUpdate.acquire_lock(instance)
os_close.assert_called_with(3)
assert logger.err.called_with("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
logger_mock.error.assert_called_with(
"I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message')
)
@pytest.mark.parametrize('injector_cls', [cls for cls in ManagedCredentialType.registry.values() if cls.injectors])

View File

@@ -8,7 +8,7 @@ import logging.handlers
import sys
import traceback
import os
from datetime import datetime
from datetime import datetime, timezone
# Django
from django.conf import settings
@@ -50,7 +50,7 @@ class RSysLogHandler(logging.handlers.SysLogHandler):
# because the alternative is blocking the
# socket.send() in the Python process, which we definitely don't
# want to do)
dt = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
dt = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
msg = f'{dt} ERROR rsyslogd was unresponsive: '
exc = traceback.format_exc()
try:

View File

@@ -393,7 +393,7 @@ class Licenser(object):
end_date = parse(sub['subscriptions']['endDate'])
except Exception:
continue
now = datetime.utcnow()
now = datetime.now(timezone.utc)
now = now.replace(tzinfo=end_date.tzinfo)
if end_date < now:
# If the sub has a past end date, skip it