From 4b73e1264b91cfcbfdaee2319a481ae735c24510 Mon Sep 17 00:00:00 2001 From: Dirk Julich Date: Mon, 3 Aug 2026 13:44:40 +0200 Subject: [PATCH] AAP-85334: Replace Black + Flake8 with Ruff Migrate Python linting and formatting toolchain from Black (formatter) and Flake8 (linter) to Ruff, which provides both in a single tool. - pyproject.toml: remove [tool.black], add [tool.ruff] config sections - tox.ini: replace black+flake8 deps/commands with ruff in linters env - awxkit/tox.ini: replace flake8 with ruff in lint env - Makefile: replace `black` target with `format` + `lint`, keep `black` as legacy alias, update `check` and `api-lint` - pre-commit.sh: use `ruff format --check`, honor AWX_IGNORE_RUFF - requirements_dev.txt: swap black/flake8 for ruff - Dockerfile.j2: install ruff instead of black - CONTRIBUTING.md: update tool references - Fix noqa comments to use # separator instead of ; (ruff requirement) - Add noqa: F841 for false positive in awxkit/cli/__init__.py Co-Authored-By: Claude Opus 4.6 --- CONTRIBUTING.md | 4 ++-- Makefile | 20 +++++++++++++------ awx/main/tests/functional/api/test_generic.py | 2 +- .../tests/functional/api/test_job_template.py | 10 +++++----- awx/main/tests/live/tests/conftest.py | 2 +- awx_collection/plugins/modules/import.py | 2 +- awx_collection/test/awx/conftest.py | 4 ++-- awx_collection/test/awx/test_export.py | 2 +- awxkit/awxkit/cli/__init__.py | 8 +++----- awxkit/tox.ini | 9 ++------- pre-commit.sh | 8 ++++---- pyproject.toml | 17 ++++++++++++---- requirements/requirements_dev.txt | 3 +-- .../roles/dockerfile/templates/Dockerfile.j2 | 2 +- tox.ini | 13 ++++-------- 15 files changed, 55 insertions(+), 51 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 143ed5fefa..c58eeea388 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,7 +109,7 @@ Install the pre-commit hook before contributing: make pre-commit ``` -When you attempt to perform a `git commit` there will be a pre-commit hook that gets run before the commit is allowed to your local repository. For example, python's [black](https://pypi.org/project/black/) will be run to test the formatting of any python files. +When you attempt to perform a `git commit` there will be a pre-commit hook that gets run before the commit is allowed to your local repository. For example, [ruff](https://docs.astral.sh/ruff/) will be run to check the formatting of any python files. While you can use environment variables to skip the pre-commit hooks GitHub will run similar tests and prevent merging of PRs if the tests do not pass. @@ -148,7 +148,7 @@ Fixes and Features for AWX will go through the Github pull request process. Subm Here are a few things you can do to help the visibility of your change, and increase the likelihood that it will be accepted: - No issues when running linters/code checkers - - Python: black: `(container)/awx_devel$ make black` + - Python: ruff: `(container)/awx_devel$ make format` - No issues from unit tests - Python: py.test: `(container)/awx_devel$ make test` - Write tests for new functionality, update/add tests for bug fixes diff --git a/Makefile b/Makefile index 9ff90e2364..06649d962c 100644 --- a/Makefile +++ b/Makefile @@ -348,9 +348,17 @@ run-ws-heartbeat: reports: mkdir -p $@ -black: reports - @command -v black >/dev/null 2>&1 || { echo "could not find black on your PATH, you may need to \`pip install black\`, or set AWX_IGNORE_BLACK=1" && exit 1; } - @(set -o pipefail && $@ $(BLACK_ARGS) awx awxkit awx_collection | tee reports/$@.report) +## awx_collection is excluded: ansible-test sanity runs its own linters which conflict with ruff +format: reports + @command -v ruff >/dev/null 2>&1 || { echo "could not find ruff on your PATH, you may need to \`pip install ruff\`" && exit 1; } + @(set -o pipefail && ruff format $(RUFF_FORMAT_ARGS) awx awxkit | tee reports/format.report) + +lint: reports + @command -v ruff >/dev/null 2>&1 || { echo "could not find ruff on your PATH, you may need to \`pip install ruff\`" && exit 1; } + ruff check awx awxkit + +## Legacy alias +black: format $(GIT_COMMON_DIR)/hooks/pre-commit: ln -sf ../../pre-commit.sh $(GIT_COMMON_DIR)/hooks/pre-commit @@ -369,11 +377,11 @@ genschema-yaml: awx-link reports fi; \ $(MANAGEMENT_COMMAND) spectacular --format openapi --file schema.yaml -check: black +check: format api-lint: - BLACK_ARGS="--check" $(MAKE) black - flake8 awx + RUFF_FORMAT_ARGS="--check" $(MAKE) format + $(MAKE) lint yamllint -s . ## Run egg_info_dev to generate awx.egg-info for development. diff --git a/awx/main/tests/functional/api/test_generic.py b/awx/main/tests/functional/api/test_generic.py index 87571c8eea..3e2dd0b075 100644 --- a/awx/main/tests/functional/api/test_generic.py +++ b/awx/main/tests/functional/api/test_generic.py @@ -6,7 +6,7 @@ from awx.api.versioning import reverse from django.test.utils import override_settings from ansible_base.jwt_consumer.common.util import generate_x_trusted_proxy_header -from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401; pylint: disable=unused-import +from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401 # pylint: disable=unused-import class HeaderTrackingMiddleware(object): diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index 4cdf43d37b..97dfa362f7 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -16,7 +16,7 @@ from rest_framework.exceptions import ValidationError # DAB from ansible_base.jwt_consumer.common.util import generate_x_trusted_proxy_header -from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401; pylint: disable=unused-import +from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401 # pylint: disable=unused-import @pytest.mark.django_db @@ -161,7 +161,7 @@ def test_invalid_json_body(patch, job_template_factory, alice, json_body): objs = job_template_factory('jt', organization='org1') objs.job_template.admin_role.members.add(alice) resp = patch(reverse('api:job_template_detail', kwargs={'pk': objs.job_template.id}), json_body, alice, expect=400) - assert resp.data['detail'] == (u'JSON parse error - not a JSON object') + assert resp.data['detail'] == ('JSON parse error - not a JSON object') @pytest.mark.django_db @@ -432,7 +432,7 @@ class TestJobTemplateCallbackProxyIntegration: data={'host_config_key': 'abcd'}, user=admin_user, expect=expected, - **headers + **headers, ) @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=[]) @@ -462,7 +462,7 @@ class TestJobTemplateCallbackProxyIntegration: data={'host_config_key': 'abcd'}, user=admin_user, expect=201, - **headers + **headers, ) @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=['my.proxy.example.org']) @@ -483,5 +483,5 @@ class TestJobTemplateCallbackProxyIntegration: data={'host_config_key': 'abcd'}, user=admin_user, expect=400, - **headers + **headers, ) diff --git a/awx/main/tests/live/tests/conftest.py b/awx/main/tests/live/tests/conftest.py index 92cfe213af..00d4d795cb 100644 --- a/awx/main/tests/live/tests/conftest.py +++ b/awx/main/tests/live/tests/conftest.py @@ -15,7 +15,7 @@ from awx.api.versioning import reverse # These tests are invoked from the awx/main/tests/live/ subfolder # so any fixtures from higher-up conftest files must be explicitly included from awx.main.tests.functional.conftest import * # noqa -from awx.main.tests.conftest import load_all_credentials # noqa: F401; pylint: disable=unused-import +from awx.main.tests.conftest import load_all_credentials # noqa: F401 # pylint: disable=unused-import from awx.main.tests import data from awx.main.models import Project, JobTemplate, Organization, Inventory, WorkflowJob, UnifiedJob diff --git a/awx_collection/plugins/modules/import.py b/awx_collection/plugins/modules/import.py index ad53a91dd7..cd294766c1 100644 --- a/awx_collection/plugins/modules/import.py +++ b/awx_collection/plugins/modules/import.py @@ -56,7 +56,7 @@ import logging # In this module we don't use EXPORTABLE_RESOURCES, we just want to validate that our installed awxkit has import/export try: - from awxkit.api.pages.api import EXPORTABLE_RESOURCES # noqa: F401; pylint: disable=unused-import + from awxkit.api.pages.api import EXPORTABLE_RESOURCES # noqa: F401 # pylint: disable=unused-import HAS_EXPORTABLE_RESOURCES = True except ImportError: diff --git a/awx_collection/test/awx/conftest.py b/awx_collection/test/awx/conftest.py index fb122ac536..6298a9540c 100644 --- a/awx_collection/test/awx/conftest.py +++ b/awx_collection/test/awx/conftest.py @@ -21,9 +21,9 @@ from ansible_base.rbac.models import RoleDefinition, DABPermission from ansible_base.rbac import permission_registry from awx.main.migrations._dab_rbac import setup_managed_role_definitions -from awx.main.tests.conftest import load_all_credentials # noqa: F401; pylint: disable=unused-import +from awx.main.tests.conftest import load_all_credentials # noqa: F401 # pylint: disable=unused-import from awx.main.tests.functional.conftest import _request -from awx.main.tests.functional.conftest import credentialtype_scm, credentialtype_ssh # noqa: F401; pylint: disable=unused-import +from awx.main.tests.functional.conftest import credentialtype_scm, credentialtype_ssh # noqa: F401 # pylint: disable=unused-import from awx.main.models import ( Organization, Project, diff --git a/awx_collection/test/awx/test_export.py b/awx_collection/test/awx/test_export.py index b9b3dceb0e..1075627cef 100644 --- a/awx_collection/test/awx/test_export.py +++ b/awx_collection/test/awx/test_export.py @@ -7,7 +7,7 @@ import pytest from awx.main.models.execution_environments import ExecutionEnvironment from awx.main.models.jobs import JobTemplate -from awx.main.tests.functional.conftest import user, system_auditor # noqa: F401; pylint: disable=unused-import +from awx.main.tests.functional.conftest import user, system_auditor # noqa: F401 # pylint: disable=unused-import ASSETS = set([ diff --git a/awxkit/awxkit/cli/__init__.py b/awxkit/awxkit/cli/__init__.py index fd2aee6a69..e1b2439862 100644 --- a/awxkit/awxkit/cli/__init__.py +++ b/awxkit/awxkit/cli/__init__.py @@ -26,10 +26,8 @@ def run(stdout=sys.stdout, stderr=sys.stderr, argv=[]): sys.exit(1) except ConnectionError as e: cli.parser.print_help() - msg = ( - '\nThere was a network error of some kind trying to reach ' - '{}.\nYou might need to specify (or double-check) ' - '--conf.host'.format(cli.get_config('host')) + msg = '\nThere was a network error of some kind trying to reach {}.\nYou might need to specify (or double-check) --conf.host'.format( + cli.get_config('host') ) if isinstance(e, SSLError): msg = ( @@ -60,7 +58,7 @@ def run(stdout=sys.stdout, stderr=sys.stderr, argv=[]): sys.stdout.write(e.__class__.__name__) print('') sys.exit(1) - except Exception as e: + except Exception as e: # noqa: F841 if cli.verbose: e = traceback.format_exc() cprint(e, 'red', file=stderr) diff --git a/awxkit/tox.ini b/awxkit/tox.ini index 801d8b0931..a2a61aaeef 100644 --- a/awxkit/tox.ini +++ b/awxkit/tox.ini @@ -26,11 +26,9 @@ commands = [testenv:lint] deps = {[testenv]deps} - flake8 + ruff commands = - flake8 awxkit -# pylama --report report.pylama awxkit -# py.test awxkit --pylama --junitxml=report.pylama {posargs} + ruff check awxkit - coverage erase [testenv:coveralls] @@ -39,9 +37,6 @@ commands= - coverage report -m - coveralls -[flake8] -max-line-length = 120 - [pytest] addopts = -v --tb=native testpaths = test diff --git a/pre-commit.sh b/pre-commit.sh index 546bcab23f..a87ee85d55 100755 --- a/pre-commit.sh +++ b/pre-commit.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -if [ -z $AWX_IGNORE_BLACK ] ; then +if [ -z "$AWX_IGNORE_BLACK" ] && [ -z "$AWX_IGNORE_RUFF" ] ; then python_files_changed=$(git diff --cached --name-only --diff-filter=AM awx/ awxkit/ tools/ | grep -E '\.py$') if [ "x$python_files_changed" != "x" ] ; then - black --check $python_files_changed || \ + ruff format --check $python_files_changed || \ if [ $? != 0 ] ; then - echo 'To fix this, run `make black` to auto-format your code prior to commit, or set AWX_IGNORE_BLACK=1' - exit 1 + echo 'To fix this, run `make format` to auto-format your code prior to commit, or set AWX_IGNORE_RUFF=1' + exit 1 fi fi fi diff --git a/pyproject.toml b/pyproject.toml index 4697861719..26690f8ca1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,17 @@ build-backend = "setuptools.build_meta" # causes the "version" key in setup.cfg to be ignored. # [tool.setuptools_scm] -[tool.black] +[tool.ruff] line-length = 160 -fast = true -skip-string-normalization = true -extend-exclude = "awx_collection" +extend-exclude = ["awx_collection"] + +[tool.ruff.format] +quote-style = "preserve" + +[tool.ruff.lint] +select = [ + "E722", + "F401", "F402", "F405", "F523", "F541", + "F811", "F821", "F822", "F841", + "W291", "W605", +] diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt index d9848967bf..6da3cb8758 100644 --- a/requirements/requirements_dev.txt +++ b/requirements/requirements_dev.txt @@ -6,7 +6,7 @@ openapi-spec-validator # OpenAPI 3.0 schema validation # pprofile - re-add once https://github.com/vpelletier/pprofile/issues/41 is addressed ipython>=7.31.1 # https://github.com/ansible/awx/security/dependabot/30 unittest2 -black +ruff pytest!=7.0.0 pytest-asyncio pytest-cov @@ -21,7 +21,6 @@ jupyter backports.tempfile # support in unit tests for py32+ tempfile.TemporaryDirectory gprof2dot atomicwrites -flake8 yamllint pip>=25.3 # PEP 660 – Editable installs for pyproject.toml based builds (wheel based) awx-tui diff --git a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 index 009065f3c0..5460c31c39 100644 --- a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 +++ b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 @@ -184,7 +184,7 @@ RUN dnf module -y enable nodejs:18 && dnf module -y install nodejs:18/common && unzip RUN pip3.12 install -vv git+https://github.com/coderanger/supervisor-stdout.git@973ba19967cdaf46d9c1634d1675fc65b9574f6e -RUN pip3.12 install -vv black setuptools-scm build +RUN pip3.12 install -vv ruff setuptools-scm build # This package randomly fails to download. # It is nice to have in the dev env, but not necessary. diff --git a/tox.ini b/tox.ini index be5a05b5d5..100c0f38a4 100644 --- a/tox.ini +++ b/tox.ini @@ -3,22 +3,17 @@ isolated_build = True [testenv:linters] deps = - black - flake8 + ruff yamllint allowlist_externals = make setenv = - BLACK_ARGS = --check PYTHON = python3 commands = - make black - flake8 awx awxkit awx_collection + # awx_collection is excluded: ansible-test sanity runs its own linters which conflict with ruff + ruff format --check awx awxkit + ruff check awx awxkit yamllint -s . -[flake8] -select = F401,F402,F821,F823,F841,F811,E265,E266,F541,W605,E722,F822,F523,W291,F405 -exclude = env,awx_collection_build,.tox - [testenv:pip-compile-docs] description = Compile docs build lockfiles deps =