From 095f4db58ea959522a6d981971f02cdf348fe729 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:03:03 +0000 Subject: [PATCH] ci(deps): lock file maintenance (main) (#2702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci(deps): lock file maintenance * Update Python compatibility markers and modernize code Update resolution markers to support Python 3.12 as primary version with 3.11 as fallback. Modernize Python 3.10+ syntax by replacing typing constructs with built-in generics (Optional → None union, List → list, Tuple → tuple). Update imports to use traceback at module level and refactor error handling from assert to raise AssertionError. Simplify failure summary output by removing truncation logic. Replace uv.lock with updated dependencies reflecting Python version changes. * chore: update pre-commit dependencies Update ruff to v0.15.12, gitleaks to v8.30.1, and mypy to v1.20.2. Also remove unnecessary space in string join operation in test fixture. * Fix CLI tool invocations with missing configuration flags Update pytest, prek, and ruff commands to include their required configuration flags. Add `--group tests` to pytest invocation, add `-c .pre-commit-config.yaml` to prek invocation, and add `--config pyproject.toml` to ruff invocation. Also remove extra space in string join operation for consistency. * test: Update pytest invocation with test dependency group The pytest command in the CLI test now includes the `--group tests` flag to ensure the test dependencies are available when running pytest through the uv package manager. * Refactor imports in class_generator_template.j2 Reorganize imports to improve readability and add conditional import for MissingRequiredArgumentError. The exception import is now only included when there are required arguments that will actually use it, reducing unnecessary dependencies in generated classes with no required arguments. * Remove extra blank line in class_generator_template.j2 * Improve file formatting with prek and ruff fallback Add ruff as a fallback formatter when prek fails. Extract project root path calculation to module level and pass configuration paths explicitly to prek. Introduce _run_ruff_fallback() helper that runs ruff check and ruff format sequentially. Update logging to clarify fallback behavior and fix typo in prek stdout log message. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Meni Yakove --- .pre-commit-config.yaml | 6 +-- class_generator/cli.py | 2 +- class_generator/formatters/file_writer.py | 47 ++++++++++++++++--- .../manifests/class_generator_template.j2 | 6 ++- .../tests/manifests/test_parse_explain.j2 | 25 ++++------ class_generator/tests/test_class_generator.py | 10 ++-- class_generator/tests/test_cli.py | 4 +- tests/scripts/generate_pytest_test.py | 2 +- 8 files changed, 68 insertions(+), 34 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 56dc627eed..cf6383d723 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,18 +45,18 @@ repos: ] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.0 + rev: v0.15.12 hooks: - id: ruff - id: ruff-format - repo: https://github.com/gitleaks/gitleaks - rev: v8.30.0 + rev: v8.30.1 hooks: - id: gitleaks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.19.1 + rev: v1.20.2 hooks: - id: mypy exclude: ^(tests/|examples/|docs/) diff --git a/class_generator/cli.py b/class_generator/cli.py index 05d643b771..e84d0637a5 100644 --- a/class_generator/cli.py +++ b/class_generator/cli.py @@ -489,7 +489,7 @@ def handle_test_generation(add_tests: bool) -> None: # Run the generated test file LOGGER.info("Running generated tests...") test_file = "class_generator/tests/test_class_generator.py" - exit_code = os.system(f"uv run pytest {test_file}") + exit_code = os.system(f"uv run --group tests pytest {test_file}") # os.system returns the exit status shifted left by 8 bits if exit_code != 0: diff --git a/class_generator/formatters/file_writer.py b/class_generator/formatters/file_writer.py index 9e662cf7de..2bd3c377b0 100644 --- a/class_generator/formatters/file_writer.py +++ b/class_generator/formatters/file_writer.py @@ -1,14 +1,19 @@ """File writing utilities for generated code.""" +from pathlib import Path + from pyhelper_utils.shell import run_command from simple_logger.logger import get_logger LOGGER = get_logger(name=__name__) +# Project root directory (contains pyproject.toml and .pre-commit-config.yaml) +_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent + def write_and_format_rendered(filepath: str, output: str) -> None: """ - Write rendered content to file and format it with prek. + Write rendered content to file and format with prek, falling back to ruff. Args: filepath: Path to write the file to @@ -17,20 +22,50 @@ def write_and_format_rendered(filepath: str, output: str) -> None: with open(filepath, "w", encoding="utf-8") as fd: fd.write(output) - # Run prek on the file + pre_commit_config = str(_PROJECT_ROOT / ".pre-commit-config.yaml") + pyproject_toml = str(_PROJECT_ROOT / "pyproject.toml") + + # Try prek first (runs all pre-commit hooks including ruff) + prek_success = False try: rc, stdout, stderr = run_command( - command=["uvx", "prek", "run", "--files", filepath], + command=["uvx", "prek", "-c", pre_commit_config, "run", "--files", filepath], verify_stderr=False, check=False, log_errors=False, ) if not rc: if stderr: - LOGGER.warning(f"Prek hooks failed for {filepath}. This is non-fatal and generation will continue.") + LOGGER.warning(f"Prek hooks failed for {filepath}, falling back to ruff.") LOGGER.debug(f"prek stderr: {stderr}") + else: + prek_success = True + if stdout: LOGGER.info(f"{filepath} fixed by prek") - LOGGER.debug(f"rek stdout: {stdout}") + LOGGER.debug(f"prek stdout: {stdout}") except Exception as e: - LOGGER.error(f"Failed to run prek hooks for {filepath}: {e}. This is non-fatal and generation will continue.") + LOGGER.warning(f"Failed to run prek for {filepath}, falling back to ruff: {e}") + + if not prek_success: + _run_ruff_fallback(filepath=filepath, pyproject_toml=pyproject_toml) + + +def _run_ruff_fallback(filepath: str, pyproject_toml: str) -> None: + """Run ruff check --fix and ruff format as fallback when prek fails.""" + for ruff_cmd in ( + ["uvx", "ruff", "check", "--fix", "--config", pyproject_toml, filepath], + ["uvx", "ruff", "format", "--config", pyproject_toml, filepath], + ): + try: + _, stdout, _ = run_command( + command=ruff_cmd, + verify_stderr=False, + check=False, + log_errors=False, + ) + if stdout: + LOGGER.info(f"{filepath} fixed by ruff") + LOGGER.debug(f"ruff stdout: {stdout}") + except Exception as e: + LOGGER.error(f"Ruff fallback failed for {filepath}: {e}") diff --git a/class_generator/manifests/class_generator_template.j2 b/class_generator/manifests/class_generator_template.j2 index d339b7ee4d..f3fd39bbef 100644 --- a/class_generator/manifests/class_generator_template.j2 +++ b/class_generator/manifests/class_generator_template.j2 @@ -14,8 +14,12 @@ {% endfor %} from typing import Any -from ocp_resources.resource import {{ base_class }} + +{% if all_required_args %} from ocp_resources.exceptions import MissingRequiredArgumentError +{% endif %} +from ocp_resources.resource import {{ base_class }} + class {{ kind }}({{ base_class }}): """ diff --git a/class_generator/tests/manifests/test_parse_explain.j2 b/class_generator/tests/manifests/test_parse_explain.j2 index 2b5dd17faa..0bc29aa8ff 100644 --- a/class_generator/tests/manifests/test_parse_explain.j2 +++ b/class_generator/tests/manifests/test_parse_explain.j2 @@ -1,16 +1,15 @@ # Generated using https://github.com/RedHatQE/openshift-python-wrapper/blob/main/scripts/resource/README.md#adding-tests import filecmp +import traceback from pathlib import Path -import pytest -from typing import List, Tuple, Optional from class_generator.constants import TESTS_MANIFESTS_DIR from class_generator.core.generator import class_generator from class_generator.utils import execute_parallel_tasks -def _test_single_resource(kind: str, tmp_path: Path) -> Optional[Tuple[str, str]]: +def _test_single_resource(kind: str, tmp_path: Path) -> tuple[str, str] | None: """ Test a single resource kind and return failure info if test fails. @@ -45,9 +44,9 @@ def _test_single_resource(kind: str, tmp_path: Path) -> Optional[Tuple[str, str] if not filecmp.cmp(output_file, expected_file): try: # Read both files to show diff details - with open(output_file, "r") as f: + with open(output_file) as f: generated_content = f.read() - with open(expected_file, "r") as f: + with open(expected_file) as f: expected_content = f.read() # Find first difference for debugging @@ -60,7 +59,7 @@ def _test_single_resource(kind: str, tmp_path: Path) -> Optional[Tuple[str, str] diff_info += f"\nGenerated lines: {len(generated_lines)}, Expected lines: {len(expected_lines)}" # Find first differing line - for i, (gen_line, exp_line) in enumerate(zip(generated_lines, expected_lines)): + for i, (gen_line, exp_line) in enumerate(zip(generated_lines, expected_lines, strict=True)): if gen_line != exp_line: diff_info += f"\nFirst difference at line {i + 1}:" diff_info += f"\nGenerated: {repr(gen_line[:100])}..." @@ -81,8 +80,6 @@ def _test_single_resource(kind: str, tmp_path: Path) -> Optional[Tuple[str, str] return None except Exception as e: - import traceback - error_details = f"Exception during generation: {str(e)}\n" error_details += f"Traceback:\n{traceback.format_exc()}" return (kind, error_details) @@ -97,17 +94,17 @@ def test_parse_explain(tmp_path: Path) -> None: {% endfor %} ] - failures: List[Tuple[str, str]] = [] + failures: list[tuple[str, str]] = [] # Process test results and collect failures - def process_test_result(kind: str, result: Optional[Tuple[str, str]]) -> None: + def process_test_result(kind: str, result: tuple[str, str] | None) -> None: if result is not None: failures.append(result) def handle_test_error(kind: str, exc: Exception) -> None: failures.append((kind, f"Task execution failed: {str(exc)}")) - def create_test_task(kind: str) -> Optional[Tuple[str, str]]: + def create_test_task(kind: str) -> tuple[str, str] | None: return _test_single_resource(kind, tmp_path / f"test-{kind}") # Run tests in parallel @@ -136,12 +133,10 @@ def test_parse_explain(tmp_path: Path) -> None: # Create a concise failure message for pytest failed_kinds = [kind for kind, _ in failures] - failure_summary = f"{len(failures)} resource(s) failed: {', '.join(failed_kinds[:5])}" - if len(failed_kinds) > 5: - failure_summary += f" and {len(failed_kinds) - 5} more" + failure_summary = f"{len(failures)} resource(s) failed: {','.join(failed_kinds)}" # Fail the test with summary - detailed output is already printed above - assert False, failure_summary + raise AssertionError(failure_summary) # If we get here, all tests passed print(f"\nAll {len(resource_kinds)} resource generation tests passed successfully!") diff --git a/class_generator/tests/test_class_generator.py b/class_generator/tests/test_class_generator.py index c2f17ff0a2..4bd6ae1bc8 100644 --- a/class_generator/tests/test_class_generator.py +++ b/class_generator/tests/test_class_generator.py @@ -96,15 +96,15 @@ def test_parse_explain(tmp_path: Path) -> None: "Deployment", "ImageContentSourcePolicy", "Machine", + "NMState", "Pod", "Secret", - "NMState", "ServiceMeshMember", - "ServingRuntime", + "Ingress", "OAuth", "Pipeline", - "Ingress", "RouteAdvertisements", + "ServingRuntime", ] failures: list[tuple[str, str]] = [] @@ -146,9 +146,7 @@ def create_test_task(kind: str) -> tuple[str, str] | None: # Create a concise failure message for pytest failed_kinds = [kind for kind, _ in failures] - failure_summary = f"{len(failures)} resource(s) failed: {', '.join(failed_kinds[:5])}" - if len(failed_kinds) > 5: - failure_summary += f" and {len(failed_kinds) - 5} more" + failure_summary = f"{len(failures)} resource(s) failed: {','.join(failed_kinds)}" # Fail the test with summary - detailed output is already printed above raise AssertionError(failure_summary) diff --git a/class_generator/tests/test_cli.py b/class_generator/tests/test_cli.py index bdf66051d2..117ae5981e 100644 --- a/class_generator/tests/test_cli.py +++ b/class_generator/tests/test_cli.py @@ -170,7 +170,9 @@ def test_add_tests(self): assert result.exit_code == 0 mock_test_gen.assert_called_once() - mock_os_system.assert_called_once_with("uv run pytest class_generator/tests/test_class_generator.py") + mock_os_system.assert_called_once_with( + "uv run --group tests pytest class_generator/tests/test_class_generator.py" + ) def test_add_tests_requires_kind(self): """Test that --add-tests cannot be used alone.""" diff --git a/tests/scripts/generate_pytest_test.py b/tests/scripts/generate_pytest_test.py index ef4ae32d81..0242b43f8a 100644 --- a/tests/scripts/generate_pytest_test.py +++ b/tests/scripts/generate_pytest_test.py @@ -517,7 +517,7 @@ def run_ruff_on_files(filepaths: list[str]) -> bool: # Run ruff format and check on all files for op in ("format", "check"): - cmd_str = f"uvx ruff {op} {' '.join(filepaths)}" + cmd_str = f"uvx ruff --config pyproject.toml {op} {' '.join(filepaths)}" rc, _, _ = run_command( command=shlex.split(cmd_str), verify_stderr=False,