diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4d948ce24..f99407e676 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,13 +130,21 @@ jobs: CO_API_KEY: ${{ secrets.COHERE_API_KEY }} test: - name: test on ${{ matrix.python-version }} + name: test ${{ matrix.packages }} on ${{ matrix.python-version }} runs-on: ubuntu-latest timeout-minutes: 10 strategy: fail-fast: false matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + packages: [slim, standard, all-extras, lowest-versions, import-examples] + exclude: + - python-version: "3.9" + packages: lowest-versions + - python-version: "3.9" + packages: import-examples + - python-version: "3.10" + packages: import-examples env: UV_PYTHON: ${{ matrix.python-version }} CI: true @@ -154,34 +162,40 @@ jobs: - run: mkdir .coverage - # run tests with just `pydantic-ai-slim` dependencies - - run: uv run --package pydantic-ai-slim coverage run -m pytest -n auto --dist=loadgroup + - name: test slim + run: cd pydantic_ai_slim && uv run --group dev coverage run -m pytest -n auto --dist=loadgroup && cd .. + if: matrix.packages == 'slim' env: - COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-slim + COVERAGE_FILE: ../.coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-slim - - run: uv run coverage run -m pytest -n auto --dist=loadgroup + - name: test standard + run: uv run --group dev coverage run -m pytest -n auto --dist=loadgroup + if: matrix.packages == 'standard' env: COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-standard - - run: uv run --all-extras coverage run -m pytest -n auto --dist=loadgroup + - name: test all extras + run: uv run --group dev --all-extras coverage run -m pytest -n auto --dist=loadgroup + if: matrix.packages == 'all-extras' env: COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-all-extras - - run: uv run --all-extras python tests/import_examples.py - - # this must run last as it modifies the environment! - name: test lowest versions - if: matrix.python-version != '3.9' + if: matrix.packages == 'lowest-versions' run: | unset UV_FROZEN - uv run --all-extras --resolution lowest-direct coverage run -m pytest -n auto --dist=loadgroup + uv run --group dev --all-extras --resolution lowest-direct coverage run -m pytest -n auto --dist=loadgroup env: COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-lowest-versions + - name: test import examples + if: matrix.packages == 'import-examples' + run: uv run --all-extras python tests/import_examples.py + - name: store coverage files uses: actions/upload-artifact@v4 with: - name: coverage-${{ matrix.python-version }} + name: coverage-${{ matrix.python-version }}-${{ matrix.packages }} path: .coverage include-hidden-files: true diff --git a/Makefile b/Makefile index d8ffeb12f1..3ef6b05736 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ .PHONY: install install: .uv .pre-commit .deno ## Install the package, dependencies, and pre-commit for local development - uv sync --frozen --all-extras --all-packages --group lint --group docs + uv sync --frozen --all-extras --all-packages --group dev --group lint --group docs pre-commit install --install-hooks .PHONY: install-all-python diff --git a/pyproject.toml b/pyproject.toml index 24bca053ae..8c90c5a825 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,7 +82,25 @@ members = [ ] [dependency-groups] -# dev dependencies are defined in `pydantic-ai-slim/pyproject.toml` to allow for minimal testing +dev = [ + "anyio>=4.5.0", + "asgi-lifespan>=2.1.0", + "devtools>=0.12.2", + "coverage[toml]>=7.10.2", + "dirty-equals>=0.9.0", + "duckduckgo-search>=7.0.0", + "inline-snapshot>=0.19.3", + "pytest>=8.3.3", + "pytest-examples>=0.0.14", + "pytest-mock>=3.14.0", + "pytest-pretty>=1.3.0", + "pytest-recording>=0.13.2", + "diff-cover>=9.2.0", + "boto3-stubs[bedrock-runtime]", + "strict-no-cover @ git+https://github.com/pydantic/strict-no-cover.git@7fc59da2c4dff919db2095a0f0e47101b657131d", + "pytest-xdist>=3.6.1", + "coverage-enable-subprocess>=0.1.0", +] lint = ["mypy>=1.11.2", "pyright>=1.1.390", "ruff>=0.6.9"] docs = [ "pydantic-ai[a2a]", diff --git a/tests/import_examples.py b/tests/import_examples.py index bc0636c371..c44d6106fa 100644 --- a/tests/import_examples.py +++ b/tests/import_examples.py @@ -5,21 +5,17 @@ """ import os -import sys from pathlib import Path -if sys.version_info < (3, 11): - print('Skipping import_examples.py because it requires Python 3.11+') -else: - os.environ.update(OPENAI_API_KEY='fake-key', GEMINI_API_KEY='fake-key', GROQ_API_KEY='fake-key') +os.environ.update(OPENAI_API_KEY='fake-key', GEMINI_API_KEY='fake-key', GROQ_API_KEY='fake-key') - examples_dir = Path(__file__).parent.parent / 'examples' / 'pydantic_ai_examples' - assert examples_dir.is_dir(), f'No examples directory found at {examples_dir}' - count = 0 - for example in examples_dir.glob('*.py'): - print(f'Importing {example.stem}...') - __import__(f'pydantic_ai_examples.{example.stem}') - count += 1 +examples_dir = Path(__file__).parent.parent / 'examples' / 'pydantic_ai_examples' +assert examples_dir.is_dir(), f'No examples directory found at {examples_dir}' +count = 0 +for example in examples_dir.glob('*.py'): + print(f'Importing {example.stem}...') + __import__(f'pydantic_ai_examples.{example.stem}') + count += 1 - print(f'Imported {count} examples') - assert count > 5, 'No examples found' +print(f'Imported {count} examples') +assert count > 5, 'No examples found' diff --git a/uv.lock b/uv.lock index 93701f47e2..cffb2dd0f2 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", @@ -3294,6 +3294,26 @@ logfire = [ ] [package.dev-dependencies] +dev = [ + { name = "anyio" }, + { name = "asgi-lifespan" }, + { name = "boto3-stubs", extra = ["bedrock-runtime"] }, + { name = "coverage", extra = ["toml"] }, + { name = "coverage-enable-subprocess" }, + { name = "devtools" }, + { name = "diff-cover", version = "9.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9.17'" }, + { name = "diff-cover", version = "9.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.17'" }, + { name = "dirty-equals" }, + { name = "duckduckgo-search" }, + { name = "inline-snapshot" }, + { name = "pytest" }, + { name = "pytest-examples" }, + { name = "pytest-mock" }, + { name = "pytest-pretty" }, + { name = "pytest-recording" }, + { name = "pytest-xdist" }, + { name = "strict-no-cover" }, +] docs = [ { name = "black" }, { name = "mkdocs" }, @@ -3323,6 +3343,25 @@ requires-dist = [ provides-extras = ["a2a", "examples", "logfire"] [package.metadata.requires-dev] +dev = [ + { name = "anyio", specifier = ">=4.5.0" }, + { name = "asgi-lifespan", specifier = ">=2.1.0" }, + { name = "boto3-stubs", extras = ["bedrock-runtime"] }, + { name = "coverage", extras = ["toml"], specifier = ">=7.10.2" }, + { name = "coverage-enable-subprocess", specifier = ">=0.1.0" }, + { name = "devtools", specifier = ">=0.12.2" }, + { name = "diff-cover", specifier = ">=9.2.0" }, + { name = "dirty-equals", specifier = ">=0.9.0" }, + { name = "duckduckgo-search", specifier = ">=7.0.0" }, + { name = "inline-snapshot", specifier = ">=0.19.3" }, + { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-examples", specifier = ">=0.0.14" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "pytest-pretty", specifier = ">=1.3.0" }, + { name = "pytest-recording", specifier = ">=0.13.2" }, + { name = "pytest-xdist", specifier = ">=3.6.1" }, + { name = "strict-no-cover", git = "https://github.com/pydantic/strict-no-cover.git?rev=7fc59da2c4dff919db2095a0f0e47101b657131d" }, +] docs = [ { name = "black", specifier = ">=24.10.0" }, { name = "mkdocs", specifier = ">=1.6.1" }, @@ -3456,28 +3495,6 @@ vertexai = [ { name = "requests" }, ] -[package.dev-dependencies] -dev = [ - { name = "anyio" }, - { name = "asgi-lifespan" }, - { name = "boto3-stubs", extra = ["bedrock-runtime"] }, - { name = "coverage", extra = ["toml"] }, - { name = "coverage-enable-subprocess" }, - { name = "devtools" }, - { name = "diff-cover", version = "9.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9.17'" }, - { name = "diff-cover", version = "9.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.17'" }, - { name = "dirty-equals" }, - { name = "duckduckgo-search" }, - { name = "inline-snapshot" }, - { name = "pytest" }, - { name = "pytest-examples" }, - { name = "pytest-mock" }, - { name = "pytest-pretty" }, - { name = "pytest-recording" }, - { name = "pytest-xdist" }, - { name = "strict-no-cover" }, -] - [package.metadata] requires-dist = [ { name = "ag-ui-protocol", marker = "extra == 'ag-ui'", specifier = ">=0.1.8" }, @@ -3513,27 +3530,6 @@ requires-dist = [ ] provides-extras = ["a2a", "ag-ui", "anthropic", "bedrock", "cli", "cohere", "duckduckgo", "evals", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "retries", "tavily", "vertexai"] -[package.metadata.requires-dev] -dev = [ - { name = "anyio", specifier = ">=4.5.0" }, - { name = "asgi-lifespan", specifier = ">=2.1.0" }, - { name = "boto3-stubs", extras = ["bedrock-runtime"] }, - { name = "coverage", extras = ["toml"], specifier = ">=7.10.2" }, - { name = "coverage-enable-subprocess", specifier = ">=0.1.0" }, - { name = "devtools", specifier = ">=0.12.2" }, - { name = "diff-cover", specifier = ">=9.2.0" }, - { name = "dirty-equals", specifier = ">=0.9.0" }, - { name = "duckduckgo-search", specifier = ">=7.0.0" }, - { name = "inline-snapshot", specifier = ">=0.19.3" }, - { name = "pytest", specifier = ">=8.3.3" }, - { name = "pytest-examples", specifier = ">=0.0.14" }, - { name = "pytest-mock", specifier = ">=3.14.0" }, - { name = "pytest-pretty", specifier = ">=1.3.0" }, - { name = "pytest-recording", specifier = ">=0.13.2" }, - { name = "pytest-xdist", specifier = ">=3.6.1" }, - { name = "strict-no-cover", git = "https://github.com/pydantic/strict-no-cover.git?rev=7fc59da2c4dff919db2095a0f0e47101b657131d" }, -] - [[package]] name = "pydantic-core" version = "2.33.2"