From de86b93690bd322df6b26cd60f8c08a75ad59272 Mon Sep 17 00:00:00 2001 From: jessicamack Date: Wed, 7 Jan 2026 11:57:24 -0500 Subject: [PATCH] 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 --- .github/workflows/ci.yml | 9 ++++- .readthedocs.yaml | 2 +- Makefile | 4 +- awx/api/views/instance_install_bundle.py | 4 +- awx/main/dispatch/pool.py | 4 +- ...register_default_execution_environments.py | 19 ++++++++- awx/main/tasks/jobs.py | 2 +- awx/main/tasks/system.py | 2 +- .../tests/docs/test_swagger_generation.py | 2 +- awx/main/tests/unit/test_tasks.py | 24 +++++------ awx/main/utils/handlers.py | 4 +- awx/main/utils/licensing.py | 2 +- .../plugins/module_utils/controller_api.py | 4 +- awxkit/awxkit/api/mixins/has_status.py | 8 ++-- awxkit/awxkit/awx/__init__.py | 14 ++++++- awxkit/awxkit/cli/format.py | 20 +++++++++- awxkit/awxkit/cli/options.py | 4 +- awxkit/awxkit/utils/__init__.py | 4 +- awxkit/awxkit/ws.py | 2 +- awxkit/setup.py | 4 +- awxkit/test/test_utils.py | 4 +- awxkit/tox.ini | 2 +- docs/schedules.md | 4 +- pytest.ini | 3 -- requirements/requirements.in | 4 +- requirements/requirements.txt | 6 +-- requirements/updater.sh | 2 +- .../roles/dockerfile/templates/Dockerfile.j2 | 40 +++++++++---------- tools/scripts/firehose.py | 6 +-- 29 files changed, 124 insertions(+), 85 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 091d9d5829..4a203fbc1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,14 +183,19 @@ jobs: path: awx-operator - name: Setup python, referencing action at awx relative path - uses: ./awx/.github/actions/setup-python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 with: - python-version: '3.13' + python-version: '3.12' - name: Install playbook dependencies run: | python -m pip install docker + - name: Check Python version + working-directory: awx + run: | + make print-PYTHON + - name: Build AWX image working-directory: awx run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9376ce0e34..c0ce9f24f6 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,7 +7,7 @@ build: os: ubuntu-22.04 tools: python: >- - 3.11 + 3.12 commands: - pip install --user tox - python3 -m tox -e docs --notest -v diff --git a/Makefile b/Makefile index 921d76c78c..ec84eab015 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -include awx/ui/Makefile -PYTHON := $(notdir $(shell for i in python3.11 python3; do command -v $$i; done|sed 1q)) +PYTHON := $(notdir $(shell for i in python3.12 python3; do command -v $$i; done|sed 1q)) SHELL := bash DOCKER_COMPOSE ?= docker compose OFFICIAL ?= no @@ -79,7 +79,7 @@ RECEPTOR_IMAGE ?= quay.io/ansible/receptor:devel SRC_ONLY_PKGS ?= cffi,pycparser,psycopg,twilio # These should be upgraded in the AWX and Ansible venv before attempting # to install the actual requirements -VENV_BOOTSTRAP ?= pip==21.2.4 setuptools==80.9.0 setuptools_scm[toml]==8.0.4 wheel==0.42.0 cython==3.1.3 +VENV_BOOTSTRAP ?= pip==25.3 setuptools==80.9.0 setuptools_scm[toml]==9.2.2 wheel==0.45.1 cython==3.1.3 NAME ?= awx diff --git a/awx/api/views/instance_install_bundle.py b/awx/api/views/instance_install_bundle.py index d23a32efba..09b3b21442 100644 --- a/awx/api/views/instance_install_bundle.py +++ b/awx/api/views/instance_install_bundle.py @@ -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, diff --git a/awx/main/dispatch/pool.py b/awx/main/dispatch/pool.py index 001274baa5..802a5d5da6 100644 --- a/awx/main/dispatch/pool.py +++ b/awx/main/dispatch/pool.py @@ -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): diff --git a/awx/main/management/commands/register_default_execution_environments.py b/awx/main/management/commands/register_default_execution_environments.py index 8686fe9a64..25674246af 100644 --- a/awx/main/management/commands/register_default_execution_environments.py +++ b/awx/main/management/commands/register_default_execution_environments.py @@ -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""" diff --git a/awx/main/tasks/jobs.py b/awx/main/tasks/jobs.py index b5e6af244a..1cd6205ba9 100644 --- a/awx/main/tasks/jobs.py +++ b/awx/main/tasks/jobs.py @@ -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: diff --git a/awx/main/tasks/system.py b/awx/main/tasks/system.py index be3b826162..9643059119 100644 --- a/awx/main/tasks/system.py +++ b/awx/main/tasks/system.py @@ -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 diff --git a/awx/main/tests/docs/test_swagger_generation.py b/awx/main/tests/docs/test_swagger_generation.py index f374e7642f..39b8b8408a 100644 --- a/awx/main/tests/docs/test_swagger_generation.py +++ b/awx/main/tests/docs/test_swagger_generation.py @@ -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 diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index cd4d343b87..ce4515b88c 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -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]) diff --git a/awx/main/utils/handlers.py b/awx/main/utils/handlers.py index 22d02d1bde..95f078ca84 100644 --- a/awx/main/utils/handlers.py +++ b/awx/main/utils/handlers.py @@ -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: diff --git a/awx/main/utils/licensing.py b/awx/main/utils/licensing.py index 80b78757e2..20417940b8 100644 --- a/awx/main/utils/licensing.py +++ b/awx/main/utils/licensing.py @@ -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 diff --git a/awx_collection/plugins/module_utils/controller_api.py b/awx_collection/plugins/module_utils/controller_api.py index d4718c3237..c559156116 100644 --- a/awx_collection/plugins/module_utils/controller_api.py +++ b/awx_collection/plugins/module_utils/controller_api.py @@ -24,9 +24,9 @@ try: from ansible.module_utils.compat.version import LooseVersion as Version except ImportError: try: - from distutils.version import LooseVersion as Version + from packaging.version import Version except ImportError: - raise AssertionError('To use this plugin or module with ansible-core 2.11, you need to use Python < 3.12 with distutils.version present') + raise AssertionError('To use this plugin or module you need to use Python >= 3.12') try: import yaml diff --git a/awxkit/awxkit/api/mixins/has_status.py b/awxkit/awxkit/api/mixins/has_status.py index ba0ca546a7..bea749be7d 100644 --- a/awxkit/awxkit/api/mixins/has_status.py +++ b/awxkit/awxkit/api/mixins/has_status.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone import json from awxkit.utils import poll_until @@ -35,10 +35,10 @@ class HasStatus(object): return self def wait_until_completed(self, interval=5, timeout=60, **kwargs): - start_time = datetime.utcnow() + start_time = datetime.now(timezone.utc) HasStatus.wait_until_status(self, self.completed_statuses, interval=interval, timeout=timeout, **kwargs) if not getattr(self, 'event_processing_finished', True): - elapsed = datetime.utcnow() - start_time + elapsed = datetime.now(timezone.utc) - start_time time_left = timeout - elapsed.total_seconds() poll_until(lambda: getattr(self.get(), 'event_processing_finished', True), interval=interval, timeout=time_left, **kwargs) return self @@ -92,7 +92,7 @@ class HasStatus(object): except Exception as e: msg += '\nFailed to obtain dependency stdout: {}'.format(e) - msg += '\nTIME WHEN STATUS WAS FOUND: {} (UTC)\n'.format(datetime.utcnow()) + msg += '\nTIME WHEN STATUS WAS FOUND: {} (UTC)\n'.format(datetime.now(timezone.utc)) raise AssertionError(msg) diff --git a/awxkit/awxkit/awx/__init__.py b/awxkit/awxkit/awx/__init__.py index 16fb42da38..02350adf88 100644 --- a/awxkit/awxkit/awx/__init__.py +++ b/awxkit/awxkit/awx/__init__.py @@ -1,5 +1,15 @@ -from distutils.version import LooseVersion +from packaging.version import Version def version_cmp(x, y): - return LooseVersion(x)._cmp(y) + """Compare two version strings. + Returns -1 if x < y, 0 if x == y, 1 if x > y + """ + vx = Version(x) + vy = Version(y) + if vx < vy: + return -1 + elif vx > vy: + return 1 + else: + return 0 diff --git a/awxkit/awxkit/cli/format.py b/awxkit/awxkit/cli/format.py index f294a9e045..5e81e3295c 100644 --- a/awxkit/awxkit/cli/format.py +++ b/awxkit/awxkit/cli/format.py @@ -1,13 +1,29 @@ import locale import json -from distutils.util import strtobool - import yaml from awxkit.cli.utils import colored from awxkit import config +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}") + + def get_config_credentials(): """Load username and password from config.credentials.default. diff --git a/awxkit/awxkit/cli/options.py b/awxkit/awxkit/cli/options.py index ecedf3918d..a6dcbe9ded 100644 --- a/awxkit/awxkit/cli/options.py +++ b/awxkit/awxkit/cli/options.py @@ -6,10 +6,8 @@ import re import sys import yaml -from distutils.util import strtobool - from .custom import CustomAction -from .format import add_output_formatting_arguments +from .format import add_output_formatting_arguments, strtobool from .resource import DEPRECATED_RESOURCES_REVERSE diff --git a/awxkit/awxkit/utils/__init__.py b/awxkit/awxkit/utils/__init__.py index 772a21227f..787fc270af 100644 --- a/awxkit/awxkit/utils/__init__.py +++ b/awxkit/awxkit/utils/__init__.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, tzinfo +from datetime import datetime, timedelta, tzinfo, timezone import inspect import logging import random @@ -364,7 +364,7 @@ def are_same_endpoint(first, second): def utcnow(): """Provide a wrapped copy of the built-in utcnow that can be easily mocked.""" - return datetime.utcnow() + return datetime.now(timezone.utc) class UTC(tzinfo): diff --git a/awxkit/awxkit/ws.py b/awxkit/awxkit/ws.py index 78c58bf2d2..fc722bc0b4 100644 --- a/awxkit/awxkit/ws.py +++ b/awxkit/awxkit/ws.py @@ -205,7 +205,7 @@ class WSClient(object): message = json.loads(message) log.debug('received message: {}'.format(message)) if self._add_received_time: - message['received_time'] = datetime.datetime.utcnow() + message['received_time'] = datetime.datetime.now(datetime.UTC) if all([message.get('group_name') == 'jobs', message.get('status') == 'pending', message.get('unified_job_id'), self._should_subscribe_to_pending_job]): if bool(message.get('project_id')) == (self._should_subscribe_to_pending_job['events'] == 'project_update_events'): diff --git a/awxkit/setup.py b/awxkit/setup.py index 4c84ff6c84..55f4fd7303 100644 --- a/awxkit/setup.py +++ b/awxkit/setup.py @@ -92,7 +92,7 @@ setup( 'requests', 'setuptools', ], - python_requires=">=3.8", + python_requires=">=3.12", extras_require={'formatting': ['jq'], 'websockets': ['websocket-client==0.57.0'], 'crypto': ['cryptography']}, license='Apache 2.0', classifiers=[ @@ -104,7 +104,7 @@ setup( 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.12', 'Topic :: System :: Software Distribution', 'Topic :: System :: Systems Administration', ], diff --git a/awxkit/test/test_utils.py b/awxkit/test/test_utils.py index 4306d86376..8f9391c857 100644 --- a/awxkit/test/test_utils.py +++ b/awxkit/test/test_utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from datetime import datetime +from datetime import datetime, timezone import sys from unittest import mock @@ -379,7 +379,7 @@ class TestUpdatePayload(object): def test_to_ical(): - now = datetime.utcnow() + now = datetime.now(timezone.utc) ical_datetime = utils.to_ical(now) date = str(now.date()).replace('-', '') time = str(now.time()).split('.')[0].replace(':', '') diff --git a/awxkit/tox.ini b/awxkit/tox.ini index 97e7949dc9..71e3081dd1 100644 --- a/awxkit/tox.ini +++ b/awxkit/tox.ini @@ -8,7 +8,7 @@ skip_missing_interpreters = true # skipsdist = true [testenv] -basepython = python3.11 +basepython = python3.12 setenv = PYTHONPATH = {toxinidir}:{env:PYTHONPATH:}:. deps = diff --git a/docs/schedules.md b/docs/schedules.md index e2e5e9cd22..ea7f4b237f 100644 --- a/docs/schedules.md +++ b/docs/schedules.md @@ -129,14 +129,14 @@ the following dates are saved in the database: - `main_schedule.rrule` - the original `RRULE` string provided by the user - `main_schedule.dtstart` - the _first_ datetime in the list of all occurrences (coerced to UTC) - `main_schedule.dtend` - the _last_ datetime in the list of all occurrences (coerced to UTC) -- `main_schedule.next_run` - the _next_ datetime in list after `utcnow()` (coerced to UTC) +- `main_schedule.next_run` - the _next_ datetime in list after `now(datetime.UTC)` (coerced to UTC) AWX makes use of [Celery Periodic Tasks (celerybeat)](http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html) to run a periodic task that discovers new jobs that need to run at a regular interval (by default, every 30 seconds). When this task starts, it queries the database for Schedules where `Schedule.next_run` is between -`scheduler_last_runtime()` and `utcnow()`. For each of these, a new job is +`scheduler_last_runtime()` and `now(datetime.UTC)`. For each of these, a new job is launched, and `Schedule.next_run` is changed to the next chronological datetime in the list of all occurrences. diff --git a/pytest.ini b/pytest.ini index 5dd99bae2d..0634837d81 100644 --- a/pytest.ini +++ b/pytest.ini @@ -35,9 +35,6 @@ filterwarnings = # FIXME: Delete this entry once `zope` is updated. once:Deprecated call to `pkg_resources.declare_namespace.'zope'.`.\nImplementing implicit namespace packages .as specified in PEP 420. is preferred to `pkg_resources.declare_namespace`. See https.//setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages:DeprecationWarning: - # FIXME: Delete this entry once the use of `distutils` is exterminated from the repo. - once:The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives:DeprecationWarning:_pytest.assertion.rewrite - # FIXME: Delete this entry once `coreapi` is deleted from the dependencies # FIXME: and is no longer imported at runtime. once:CoreAPI compatibility is deprecated and will be removed in DRF 3.17:rest_framework.RemovedInDRF317Warning:rest_framework.schemas.coreapi diff --git a/requirements/requirements.in b/requirements/requirements.in index 6f17cc0d8e..6b5330fbfb 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -1,4 +1,4 @@ -aiohttp>=3.9.4 # CVE-2024-30251 +aiohttp>=3.12.14 # CVE-2024-30251 ansi2html # Used to format the stdout from jobs into html for display jq # used for indirect host counting feature asciichartpy @@ -65,7 +65,7 @@ urllib3<2.4.0, >=1.26.19 # CVE-2024-37891. capped by kubernetes 34.1.0 reqs uWSGI>=2.0.28 uwsgitop wheel>=0.38.1 # CVE-2022-40898 -pip==21.2.4 # see UPGRADE BLOCKERs +pip==25.3 # see UPGRADE BLOCKERs setuptools==80.9.0 # see UPGRADE BLOCKERs setuptools_scm[toml] setuptools-rust>=0.11.4 # cryptography build dep diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 4750b79343..388a41f80a 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -475,7 +475,7 @@ service-identity==24.2.0 # via twisted setuptools-rust==1.10.2 # via -r /awx_devel/requirements/requirements.in -setuptools-scm[toml]==8.1.0 +setuptools-scm[toml]==9.2.2 # via -r /awx_devel/requirements/requirements.in six==1.17.0 # via @@ -542,7 +542,7 @@ uwsgitop==0.12 # via -r /awx_devel/requirements/requirements.in websocket-client==1.8.0 # via kubernetes -wheel==0.42.0 +wheel==0.45.1 # via -r /awx_devel/requirements/requirements.in wrapt==1.17.3 # via opentelemetry-instrumentation @@ -556,7 +556,7 @@ zstandard==0.25.0 # via aiohttp # The following packages are considered to be unsafe in a requirements file: -pip==21.2.4 +pip==25.3 # via -r /awx_devel/requirements/requirements.in setuptools==80.9.0 # via diff --git a/requirements/updater.sh b/requirements/updater.sh index f7bbcafdd9..8687281deb 100755 --- a/requirements/updater.sh +++ b/requirements/updater.sh @@ -16,7 +16,7 @@ _cleanup() { generate_requirements() { venv="`pwd`/venv" echo $venv - /usr/bin/python3.11 -m venv "${venv}" + /usr/bin/python3.12 -m venv "${venv}" # shellcheck disable=SC1090 source ${venv}/bin/activate diff --git a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 index 41664f82cb..3560b928d3 100644 --- a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 +++ b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 @@ -48,12 +48,12 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \ patch \ postgresql \ postgresql-devel \ - python3.11 \ - "python3.11-devel" \ - "python3.11-pip" \ - "python3.11-setuptools" \ - "python3.11-packaging" \ - "python3.11-psycopg2" \ + python3.12 \ + "python3.12-devel" \ + "python3.12-pip" \ + "python3.12-setuptools" \ + "python3.12-packaging" \ + "python3.12-psycopg2" \ swig \ unzip \ xmlsec1-devel \ @@ -63,7 +63,7 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \ RUN mkdir -p ~/.ssh && chmod 0700 ~/.ssh RUN ssh-keyscan github.com > ~/.ssh/known_hosts -RUN pip3.11 install -vv build +RUN pip3.12 install -vv build {% if image_architecture == 'ppc64le' %} RUN dnf -y update && dnf install -y wget && \ @@ -133,12 +133,12 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \ krb5-workstation \ nginx \ postgresql \ - python3.11 \ - "python3.11-devel" \ - "python3.11-pip*" \ - "python3.11-setuptools" \ - "python3.11-packaging" \ - "python3.11-psycopg2" \ + python3.12 \ + "python3.12-devel" \ + "python3.12-pip*" \ + "python3.12-setuptools" \ + "python3.12-packaging" \ + "python3.12-psycopg2" \ rsync \ rsyslog \ subversion \ @@ -149,7 +149,7 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \ xmlsec1-openssl && \ dnf -y clean all -RUN pip3.11 install -vv virtualenv supervisor dumb-init build +RUN pip3.12 install -vv virtualenv supervisor dumb-init build RUN rm -rf /root/.cache && rm -rf /tmp/* @@ -181,8 +181,8 @@ RUN dnf -y install \ unzip && \ npm install -g n && n 16.13.1 && npm install -g npm@8.5.0 && dnf remove -y nodejs -RUN pip3.11 install -vv git+https://github.com/coderanger/supervisor-stdout.git@973ba19967cdaf46d9c1634d1675fc65b9574f6e -RUN pip3.11 install -vv black setuptools-scm build +RUN pip3.12 install -vv git+https://github.com/coderanger/supervisor-stdout.git@973ba19967cdaf46d9c1634d1675fc65b9574f6e +RUN pip3.12 install -vv black setuptools-scm build # This package randomly fails to download. # It is nice to have in the dev env, but not necessary. @@ -251,8 +251,8 @@ ADD tools/scripts/awx-python /usr/bin/awx-python {% endif %} {% if (build_dev|bool) or (kube_dev|bool) %} -RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.11/site-packages/awx.egg-link -RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.11/site-packages/awx.pth +RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.12/site-packages/awx.egg-link +RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.12/site-packages/awx.pth RUN ln -sf /awx_devel/tools/docker-compose/awx-manage /usr/local/bin/awx-manage RUN ln -sf /awx_devel/tools/scripts/awx-python /usr/bin/awx-python RUN ln -sf /awx_devel/tools/scripts/rsyslog-4xx-recovery /usr/bin/rsyslog-4xx-recovery @@ -284,8 +284,8 @@ RUN for dir in \ /var/lib/awx/.local \ /var/lib/awx/venv \ /var/lib/awx/venv/awx/bin \ - /var/lib/awx/venv/awx/lib/python3.11 \ - /var/lib/awx/venv/awx/lib/python3.11/site-packages \ + /var/lib/awx/venv/awx/lib/python3.12 \ + /var/lib/awx/venv/awx/lib/python3.12/site-packages \ /var/lib/awx/projects \ /var/lib/awx/rsyslog \ /var/run/awx-rsyslog \ diff --git a/tools/scripts/firehose.py b/tools/scripts/firehose.py index 69a1c40ec8..9f8e2d01e5 100755 --- a/tools/scripts/firehose.py +++ b/tools/scripts/firehose.py @@ -310,12 +310,12 @@ if __name__ == '__main__': if events > 0: for k_id in created_job_ids: generate_events(events, str(k_id), time_delta) - print(datetime.datetime.utcnow().isoformat()) + print(datetime.datetime.now(datetime.UTC).isoformat()) conn.close() finally: # restore all indexes - print(datetime.datetime.utcnow().isoformat()) + print(datetime.datetime.now(datetime.UTC).isoformat()) print('restoring indexes and constraints (this may take awhile)') workers = [] @@ -343,4 +343,4 @@ if __name__ == '__main__': sql = f'ALTER TABLE main_jobevent ADD CONSTRAINT {conname} {condef}' cleanup(sql) - print(datetime.datetime.utcnow().isoformat()) + print(datetime.datetime.now(datetime.UTC).isoformat())