Skip to content

Commit 544ff88

Browse files
authored
tests: speed up the test suite (#2414)
1 parent c2940ff commit 544ff88

File tree

21 files changed

+253
-211
lines changed

21 files changed

+253
-211
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ jobs:
140140
env:
141141
UV_PYTHON: ${{ matrix.python-version }}
142142
CI: true
143+
COVERAGE_PROCESS_START: ./pyproject.toml
143144
steps:
144145
- uses: actions/checkout@v4
145146

@@ -151,20 +152,20 @@ jobs:
151152
with:
152153
deno-version: v2.x
153154

154-
- run: mkdir coverage
155+
- run: mkdir .coverage
155156

156157
# run tests with just `pydantic-ai-slim` dependencies
157-
- run: uv run --package pydantic-ai-slim coverage run -m pytest
158+
- run: uv run --package pydantic-ai-slim coverage run -m pytest -n auto --dist=loadgroup
158159
env:
159-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-slim
160+
COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-slim
160161

161-
- run: uv run coverage run -m pytest
162+
- run: uv run coverage run -m pytest -n auto --dist=loadgroup
162163
env:
163-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-standard
164+
COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-standard
164165

165-
- run: uv run --all-extras coverage run -m pytest
166+
- run: uv run --all-extras coverage run -m pytest -n auto --dist=loadgroup
166167
env:
167-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-all-extras
168+
COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-all-extras
168169

169170
- run: uv run --all-extras python tests/import_examples.py
170171

@@ -173,15 +174,15 @@ jobs:
173174
if: matrix.python-version != '3.9'
174175
run: |
175176
unset UV_FROZEN
176-
uv run --all-extras --resolution lowest-direct coverage run -m pytest
177+
uv run --all-extras --resolution lowest-direct coverage run -m pytest -n auto --dist=loadgroup
177178
env:
178-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-lowest-versions
179+
COVERAGE_FILE: .coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-lowest-versions
179180

180181
- name: store coverage files
181182
uses: actions/upload-artifact@v4
182183
with:
183184
name: coverage-${{ matrix.python-version }}
184-
path: coverage
185+
path: .coverage
185186
include-hidden-files: true
186187

187188
coverage:
@@ -197,15 +198,15 @@ jobs:
197198
uses: actions/download-artifact@v4
198199
with:
199200
merge-multiple: true
200-
path: coverage
201+
path: .coverage
201202

202203
- uses: astral-sh/setup-uv@v5
203204
with:
204205
enable-cache: true
205206

206207
- run: uv sync --package pydantic-ai-slim --only-dev
207-
- run: rm coverage/.coverage.*-py3.9-* # Exclude 3.9 coverage as it gets the wrong line numbers, causing invalid failures.
208-
- run: uv run coverage combine coverage
208+
- run: rm .coverage/.coverage.*-py3.9-* # Exclude 3.9 coverage as it gets the wrong line numbers, causing invalid failures.
209+
- run: uv run coverage combine
209210

210211
- run: uv run coverage html --show-contexts --title "Pydantic AI coverage for ${{ github.sha }}"
211212

@@ -228,7 +229,10 @@ jobs:
228229

229230
- run: uv run coverage report --fail-under 100
230231
- run: uv run diff-cover coverage.xml --fail-under 100
232+
231233
- run: uv run strict-no-cover
234+
env:
235+
COVERAGE_FILE: .coverage/.coverage
232236

233237
test-mcp-run-python:
234238
runs-on: ubuntu-latest

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
1010
- **Format code**: `make format`
1111
- **Lint code**: `make lint`
1212
- **Type checking**: `make typecheck` (uses pyright) or `make typecheck-both` (pyright + mypy)
13-
- **Run tests**: `make test` (with coverage) or `make test-fast` (parallel, no coverage)
13+
- **Run tests**: `make test` (with coverage)
1414
- **Build docs**: `make docs` or `make docs-serve` (local development)
1515

1616
### Single Test Commands

Makefile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,10 @@ typecheck-both: typecheck-pyright typecheck-mypy
6060

6161
.PHONY: test
6262
test: ## Run tests and collect coverage data
63-
uv run coverage run -m pytest
63+
COVERAGE_PROCESS_START=./pyproject.toml uv run coverage run -m pytest -n auto --dist=loadgroup
64+
@uv run coverage combine
6465
@uv run coverage report
6566

66-
.PHONY: test-fast
67-
test-fast: ## Same as test except no coverage and 4x faster depending on hardware
68-
uv run pytest -n auto --dist=loadgroup
69-
7067
.PHONY: test-all-python
7168
test-all-python: ## Run tests on Python 3.9 to 3.13
7269
UV_PROJECT_ENVIRONMENT=.venv39 uv run --python 3.9 --all-extras --all-packages coverage run -p -m pytest

pydantic_ai_slim/pydantic_ai/_agent_graph.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ async def process_function_tools( # noqa: C901
620620
result_data = await tool_manager.handle_call(call)
621621
except exceptions.UnexpectedModelBehavior as e:
622622
ctx.state.increment_retries(ctx.deps.max_result_retries, e)
623-
raise e # pragma: no cover
623+
raise e # pragma: lax no cover
624624
except ToolRetryError as e:
625625
ctx.state.increment_retries(ctx.deps.max_result_retries, e)
626626
yield _messages.FunctionToolCallEvent(call)

pydantic_ai_slim/pydantic_ai/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from typing import TYPE_CHECKING
66

77
if sys.version_info < (3, 11):
8-
from exceptiongroup import ExceptionGroup
8+
from exceptiongroup import ExceptionGroup as ExceptionGroup # pragma: lax no cover
99
else:
10-
ExceptionGroup = ExceptionGroup
10+
ExceptionGroup = ExceptionGroup # pragma: lax no cover
1111

1212
if TYPE_CHECKING:
1313
from .messages import RetryPromptPart

pydantic_ai_slim/pydantic_ai/models/anthropic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ async def _messages_create(
256256
except APIStatusError as e:
257257
if (status_code := e.status_code) >= 400:
258258
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
259-
raise # pragma: no cover
259+
raise # pragma: lax no cover
260260

261261
def _process_response(self, response: BetaMessage) -> ModelResponse:
262262
"""Process a non-streamed response, and prepare a message to return."""

pydantic_ai_slim/pydantic_ai/models/bedrock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,4 +665,4 @@ async def __anext__(self) -> T:
665665
if type(e.__cause__) is StopIteration:
666666
raise StopAsyncIteration
667667
else:
668-
raise e # pragma: no cover
668+
raise e # pragma: lax no cover

pydantic_ai_slim/pydantic_ai/models/cohere.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ async def _chat(
183183
except ApiError as e:
184184
if (status_code := e.status_code) and status_code >= 400:
185185
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
186-
raise # pragma: no cover
186+
raise # pragma: lax no cover
187187

188188
def _process_response(self, response: V2ChatResponse) -> ModelResponse:
189189
"""Process a non-streamed response, and prepare a message to return."""

pydantic_ai_slim/pydantic_ai/models/gemini.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ async def _make_request(
236236

237237
if gemini_labels := model_settings.get('gemini_labels'):
238238
if self._system == 'google-vertex':
239-
request_data['labels'] = gemini_labels
239+
request_data['labels'] = gemini_labels # pragma: lax no cover
240240

241241
headers = {'Content-Type': 'application/json', 'User-Agent': get_user_agent()}
242242
url = f'/{self._model_name}:{"streamGenerateContent" if streamed else "generateContent"}'
@@ -366,11 +366,11 @@ async def _map_user_prompt(self, part: UserPromptPart) -> list[_GeminiPartUnion]
366366
inline_data={'data': downloaded_item['data'], 'mime_type': downloaded_item['data_type']}
367367
)
368368
content.append(inline_data)
369-
else:
369+
else: # pragma: lax no cover
370370
file_data = _GeminiFileDataPart(file_data={'file_uri': item.url, 'mime_type': item.media_type})
371371
content.append(file_data)
372372
else:
373-
assert_never(item)
373+
assert_never(item) # pragma: lax no cover
374374
return content
375375

376376
def _map_response_schema(self, o: OutputObjectDefinition) -> dict[str, Any]:

pydantic_ai_slim/pydantic_ai/models/google.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ async def _map_user_prompt(self, part: UserPromptPart) -> list[PartDict]:
407407
content.append(inline_data_dict) # type: ignore
408408
elif isinstance(item, VideoUrl) and item.is_youtube:
409409
file_data_dict = {'file_data': {'file_uri': item.url, 'mime_type': item.media_type}}
410-
if item.vendor_metadata:
410+
if item.vendor_metadata: # pragma: no branch
411411
file_data_dict['video_metadata'] = item.vendor_metadata
412412
content.append(file_data_dict) # type: ignore
413413
elif isinstance(item, FileUrl):
@@ -421,7 +421,9 @@ async def _map_user_prompt(self, part: UserPromptPart) -> list[PartDict]:
421421
inline_data = {'data': downloaded_item['data'], 'mime_type': downloaded_item['data_type']}
422422
content.append({'inline_data': inline_data}) # type: ignore
423423
else:
424-
content.append({'file_data': {'file_uri': item.url, 'mime_type': item.media_type}})
424+
content.append(
425+
{'file_data': {'file_uri': item.url, 'mime_type': item.media_type}}
426+
) # pragma: lax no cover
425427
else:
426428
assert_never(item)
427429
return content

0 commit comments

Comments
 (0)