From b6f991d3fe32cba60801639f988fc00d3a32e841 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Mon, 2 Mar 2026 12:23:28 +0000 Subject: [PATCH] Move contrib CI into tox --- .github/workflows/contrib-tests.yml | 110 +++------------------------- pyproject.toml | 78 ++++++++++++++++++++ 2 files changed, 88 insertions(+), 100 deletions(-) diff --git a/.github/workflows/contrib-tests.yml b/.github/workflows/contrib-tests.yml index ceb6af74..1fd31f5f 100644 --- a/.github/workflows/contrib-tests.yml +++ b/.github/workflows/contrib-tests.yml @@ -33,83 +33,11 @@ permissions: jobs: contrib_matrix: - name: "${{ matrix.target.label }} / py${{ matrix.python-version }}" + name: "py${{ matrix.python-version }}" runs-on: ubuntu-latest strategy: matrix: - target: - - contrib: aiohttp - integration_path: tests/integration/contrib/aiohttp - unit_path: tests/unit/contrib/aiohttp - spec: ">=3.8,<4.0" - label: "aiohttp-3.x" - - contrib: aiohttp - integration_path: tests/integration/contrib/aiohttp - unit_path: tests/unit/contrib/aiohttp - spec: ">=3.11,<4.0" - label: "aiohttp-3.11+" - - contrib: django - integration_path: tests/integration/contrib/django - unit_path: tests/unit/contrib/django - spec: ">=4.0,<5.0" - label: "django-4.x" - - contrib: django - integration_path: tests/integration/contrib/django - unit_path: tests/unit/contrib/django - spec: ">=5.0,<6.0" - label: "django-5.x" - - contrib: django - integration_path: tests/integration/contrib/django - unit_path: tests/unit/contrib/django - spec: ">=6.0,<7.0" - label: "django-6.x" - - contrib: falcon - integration_path: tests/integration/contrib/falcon - spec: ">=4.0,<5.0" - label: "falcon-4.x" - - contrib: fastapi - integration_path: tests/integration/contrib/fastapi - spec: ">=0.111,<0.120" - label: "fastapi-0.11x" - - contrib: fastapi - integration_path: tests/integration/contrib/fastapi - spec: ">=0.120,<0.129" - label: "fastapi-0.12x" - - contrib: flask - integration_path: tests/integration/contrib/flask - unit_path: tests/unit/contrib/flask - spec: ">=2.0,<3.0" - label: "flask-2.x" - - contrib: flask - integration_path: tests/integration/contrib/flask - unit_path: tests/unit/contrib/flask - spec: ">=3.0,<4.0" - label: "flask-3.x" - - contrib: requests - integration_path: tests/integration/contrib/requests - unit_path: tests/unit/contrib/requests - spec: "" - label: "requests-default" - - contrib: starlette - integration_path: tests/integration/contrib/starlette - spec: ">=0.40.0,<0.50.0" - label: "starlette-0.4x" - - contrib: starlette - integration_path: tests/integration/contrib/starlette - spec: ">=0.50.0,<0.60.0" - label: "starlette-0.5x" - - contrib: werkzeug - integration_path: tests/integration/contrib/werkzeug - spec: "" - label: "werkzeug-default" python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] - exclude: - - target: - label: "django-6.x" - python-version: "3.10" - - target: - label: "django-6.x" - python-version: "3.11" fail-fast: false steps: - uses: actions/checkout@v6 @@ -128,9 +56,6 @@ jobs: with: poetry-version: "2.3.1" - - name: Configure poetry - run: poetry config virtualenvs.in-project true - - name: Set up cache uses: actions/cache@v5 id: cache @@ -138,37 +63,22 @@ jobs: path: | ~/.cache/pypoetry ~/.cache/pip - key: deps-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ matrix.target.label }}-${{ hashFiles('**/poetry.lock') }} + key: deps-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-contrib-${{ hashFiles('**/poetry.lock', 'pyproject.toml') }} restore-keys: | - deps-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ matrix.target.label }}- + deps-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-contrib- deps-${{ runner.os }}-${{ matrix.python-version }}- - - name: Install dependencies - run: poetry install --extras "${{ matrix.target.contrib }}" - - - name: Install framework variant - if: matrix.target.spec != '' - run: poetry run pip install --upgrade --force-reinstall --no-cache-dir "${{ matrix.target.contrib }}${{ matrix.target.spec }}" - - - name: Validate environment - run: | - poetry run python -m pip check - poetry run python -c "import importlib; name='${{ matrix.target.contrib }}'; mod = importlib.import_module(name); print(name, getattr(mod, '__version__', 'unknown'))" + - name: Install tox + run: python -m pip install tox - name: Test - shell: bash env: - PYTEST_ADDOPTS: "--color=yes" - run: | - paths=("${{ matrix.target.integration_path }}") - unit_path="${{ matrix.target.unit_path }}" - if [ -d "$unit_path" ]; then - paths+=("$unit_path") - fi - poetry run pytest "${paths[@]}" + TOX_SKIP_ENV: ${{ contains(fromJSON('["3.10", "3.11"]'), matrix.python-version) && '^contrib-django-6x$' || '' }} + run: tox -m contrib -p auto - name: Upload coverage uses: codecov/codecov-action@v5 with: - flags: contrib,${{ matrix.target.contrib }},${{ matrix.target.label }},py${{ matrix.python-version }} - name: contrib-${{ matrix.target.contrib }}-${{ matrix.target.label }}-py${{ matrix.python-version }} + files: reports/coverage-*.xml + flags: contrib,py${{ matrix.python-version }} + name: contrib-py${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index 91414b1c..8d4ee985 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,3 +163,81 @@ src = "openapi_core/__init__.py" [[tool.tbump.file]] src = "pyproject.toml" + +[tool.tox] +min_version = "4.21" +env_list = [ + "contrib-aiohttp-3x", + "contrib-aiohttp-311plus", + "contrib-django-4x", + "contrib-django-5x", + "contrib-django-6x", + "contrib-falcon-4x", + "contrib-fastapi-011x", + "contrib-fastapi-012x", + "contrib-flask-2x", + "contrib-flask-3x", + "contrib-requests-default", + "contrib-starlette-04x", + "contrib-starlette-05x", + "contrib-werkzeug-default", +] +isolated_build = true +skip_missing_interpreters = false + +[tool.tox.env_run_base] +description = "Run contrib integration and unit tests for selected framework range." +package = "skip" +allowlist_externals = ["poetry"] +labels = ["contrib"] +commands_pre = [ + ["python", "-c", "import os,subprocess; extra=os.environ.get('POETRY_EXTRA',''); cmd=['poetry','install','--with','dev','--without','docs','--no-interaction']; extra and cmd.extend(['--extras', extra]); env=dict(os.environ); env['POETRY_VIRTUALENVS_CREATE']='false'; env['POETRY_CACHE_DIR']='.tox/.poetry-cache/' + os.environ.get('TOX_ENV_NAME', 'default'); subprocess.check_call(cmd, env=env)"], + ["python", "-c", "import os,subprocess,sys; pkg=os.environ['CONTRIB_PACKAGE']; spec=os.environ.get('CONTRIB_SPEC',''); target=f'{pkg}{spec}'; label=target if spec else 'from-lock'; print(f'framework target: {label}'); spec and subprocess.check_call([sys.executable,'-m','pip','install','--upgrade','--force-reinstall','--no-cache-dir',target])"], + ["python", "-m", "pip", "check"], + ["python", "-c", "import importlib, os; name=os.environ['CONTRIB_PACKAGE']; mod=importlib.import_module(name); print(name, getattr(mod, '__version__', 'unknown'))"], +] +commands = [ + ["python", "-c", "import os,shlex,subprocess,sys; paths=shlex.split(os.environ['CONTRIB_PATHS']); cmd=[sys.executable,'-m','pytest','--override-ini','addopts=','--capture=no','--verbose','--showlocals','--color=yes','--junitxml=reports/junit-{env_name}.xml','--cov=openapi_core','--cov-report=term-missing','--cov-report=xml:reports/coverage-{env_name}.xml',*paths]; env=dict(os.environ); env['COVERAGE_FILE']='.coverage.{env_name}'; raise SystemExit(subprocess.call(cmd, env=env))"], +] + +[tool.tox.env."contrib-aiohttp-3x"] +set_env = { POETRY_EXTRA = "aiohttp", CONTRIB_PACKAGE = "aiohttp", CONTRIB_SPEC = ">=3.8,<4.0", CONTRIB_PATHS = "tests/integration/contrib/aiohttp tests/unit/contrib/aiohttp" } + +[tool.tox.env."contrib-aiohttp-311plus"] +set_env = { POETRY_EXTRA = "aiohttp", CONTRIB_PACKAGE = "aiohttp", CONTRIB_SPEC = ">=3.11,<4.0", CONTRIB_PATHS = "tests/integration/contrib/aiohttp tests/unit/contrib/aiohttp" } + +[tool.tox.env."contrib-django-4x"] +set_env = { POETRY_EXTRA = "django", CONTRIB_PACKAGE = "django", CONTRIB_SPEC = ">=4.0,<5.0", CONTRIB_PATHS = "tests/integration/contrib/django tests/unit/contrib/django" } + +[tool.tox.env."contrib-django-5x"] +set_env = { POETRY_EXTRA = "django", CONTRIB_PACKAGE = "django", CONTRIB_SPEC = ">=5.0,<6.0", CONTRIB_PATHS = "tests/integration/contrib/django tests/unit/contrib/django" } + +[tool.tox.env."contrib-django-6x"] +set_env = { POETRY_EXTRA = "django", CONTRIB_PACKAGE = "django", CONTRIB_SPEC = ">=6.0,<7.0", CONTRIB_PATHS = "tests/integration/contrib/django tests/unit/contrib/django" } + +[tool.tox.env."contrib-falcon-4x"] +set_env = { POETRY_EXTRA = "falcon", CONTRIB_PACKAGE = "falcon", CONTRIB_SPEC = ">=4.0,<5.0", CONTRIB_PATHS = "tests/integration/contrib/falcon" } + +[tool.tox.env."contrib-fastapi-011x"] +set_env = { POETRY_EXTRA = "fastapi", CONTRIB_PACKAGE = "fastapi", CONTRIB_SPEC = ">=0.111,<0.120", CONTRIB_PATHS = "tests/integration/contrib/fastapi" } + +[tool.tox.env."contrib-fastapi-012x"] +set_env = { POETRY_EXTRA = "fastapi", CONTRIB_PACKAGE = "fastapi", CONTRIB_SPEC = ">=0.120,<0.129", CONTRIB_PATHS = "tests/integration/contrib/fastapi" } + +[tool.tox.env."contrib-flask-2x"] +set_env = { POETRY_EXTRA = "flask", CONTRIB_PACKAGE = "flask", CONTRIB_SPEC = ">=2.0,<3.0", CONTRIB_PATHS = "tests/integration/contrib/flask tests/unit/contrib/flask" } + +[tool.tox.env."contrib-flask-3x"] +set_env = { POETRY_EXTRA = "flask", CONTRIB_PACKAGE = "flask", CONTRIB_SPEC = ">=3.0,<4.0", CONTRIB_PATHS = "tests/integration/contrib/flask tests/unit/contrib/flask" } + +[tool.tox.env."contrib-requests-default"] +set_env = { POETRY_EXTRA = "requests", CONTRIB_PACKAGE = "requests", CONTRIB_SPEC = "", CONTRIB_PATHS = "tests/integration/contrib/requests tests/unit/contrib/requests" } + +[tool.tox.env."contrib-starlette-04x"] +set_env = { POETRY_EXTRA = "starlette", CONTRIB_PACKAGE = "starlette", CONTRIB_SPEC = ">=0.40.0,<0.50.0", CONTRIB_PATHS = "tests/integration/contrib/starlette" } + +[tool.tox.env."contrib-starlette-05x"] +set_env = { POETRY_EXTRA = "starlette", CONTRIB_PACKAGE = "starlette", CONTRIB_SPEC = ">=0.50.0,<0.60.0", CONTRIB_PATHS = "tests/integration/contrib/starlette" } + +[tool.tox.env."contrib-werkzeug-default"] +set_env = { CONTRIB_PACKAGE = "werkzeug", CONTRIB_SPEC = "", CONTRIB_PATHS = "tests/integration/contrib/werkzeug" }