Support Python 3.15 (#10064)

Co-authored-by: Marcelo Trylesinski <[email protected]>
This commit is contained in:
Aditya Vardhan
2026-10-09 23:30:25 -04:00
committed by GitHub
co-authored by Marcelo Trylesinski
parent 86d8f65865
commit 4c6fc3ce0a
20 changed files with 1862 additions and 1304 deletions
+2 -1
View File
@@ -28,7 +28,8 @@ MIN_BASELINE_SAMPLES = 10
# Must stay above the number of tracked jobs so a full run renders every row: `no_baseline` sorts
# into the truncated tail with `normal`, and every newly-minted signature starts there. Today's
# `ci.yml` matrices give 4 pythons x (5 installs + 2 all-extras shards + 2 lowest-versions
# shards) + 4 x 2 durable-exec + 3.11's third all-extras shard = 45.
# shards) + 4 x 2 durable-exec + 3.11's third all-extras shard + 3.15's 5 installs, 2 all-extras
# shards, and locked durable-exec leg = 53.
REPORT_ROW_LIMIT = 60
WARNING_MIN_SECONDS = 60
SLOW_THRESHOLD_MULTIPLIER = 1.25
+7 -1
View File
@@ -21,7 +21,11 @@ pydantic-core = false
pydantic-ai-harness = false
logfire-api = false
cassetter = false
aiohttp = "2026-10-05T17:43:49Z"
listentome = false
asyncpg = "2026-10-06T20:32:41Z"
fastavro = "2026-10-05T22:24:00Z"
rpds-py = "2026-10-04T16:32:38Z"
logfire = false
pydantic-settings = false
pydantic-monty-client = false
@@ -1136,6 +1140,7 @@ requires-dist = [
{ name = "absurd-sdk", marker = "extra == 'absurd'", specifier = ">=0.5.0,<1" },
{ name = "agent-client-protocol", marker = "extra == 'acp'", specifier = ">=0.11,<0.12" },
{ name = "aws-durable-execution-sdk-python", marker = "python_full_version >= '3.11' and extra == 'aws-lambda'", specifier = ">=1.7.0,<2" },
{ name = "beartype", marker = "python_full_version >= '3.15' and extra == 'stackone'", specifier = ">=0.23.0rc2" },
{ name = "browser-use", marker = "python_full_version >= '3.11' and extra == 'browser-use'", specifier = ">=0.13.6,<0.14" },
{ name = "e2b", marker = "extra == 'e2b'", specifier = ">=2.48.0" },
{ name = "exa-py", marker = "extra == 'exa'", specifier = ">=2.16.0" },
@@ -1177,7 +1182,8 @@ requires-dist = [
{ name = "ripgrep", marker = "sys_platform != 'android' and extra == 'coder'", specifier = "==14.1.0" },
{ name = "sprites-py", marker = "extra == 'sprites'", specifier = ">=0.7.0,<0.8" },
{ name = "stackone-defender", marker = "python_full_version >= '3.11' and extra == 'prompt-injection-defender'", specifier = ">=0.7.3,<0.8" },
{ name = "stackone-defender", extras = ["onnx"], marker = "python_full_version >= '3.11' and extra == 'prompt-injection-defender-ml'", specifier = ">=0.7.3,<0.8" },
{ name = "stackone-defender", marker = "python_full_version >= '3.11' and extra == 'prompt-injection-defender-ml'", specifier = ">=0.7.3,<0.8" },
{ name = "stackone-defender", extras = ["onnx"], marker = "python_full_version >= '3.11' and python_full_version < '3.15' and extra == 'prompt-injection-defender-ml'", specifier = ">=0.7.3,<0.8" },
{ name = "youdotcom", marker = "extra == 'youdotcom'", specifier = ">=3.1.2,<3.2" },
]
provides-extras = ["absurd", "acp", "anthropic", "aws-lambda", "browser-use", "cli", "code-mode", "codemode", "coder", "day-ai", "dbos", "dynamic-workflow", "e2b", "exa", "github", "google-workspace", "grain", "linear", "logfire", "logfire-mcp", "modal", "mongodb", "notion", "ordinal", "playwright", "posthog", "prompt-injection-defender", "prompt-injection-defender-ml", "pylon", "researcher", "skills", "slack", "sprites", "stackone", "temporal", "youdotcom"]
+23 -8
View File
@@ -505,7 +505,7 @@ jobs:
# `REPORT_ROW_LIMIT` in `.github/scripts/ci_duration.py` must stay above, or the duration
# report truncates. That constant's comment enumerates all four.
# `all-extras` is the slowest install shape -- it runs sharded in `test-all-extras` instead.
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
install:
- name: pydantic-ai-slim
command: "--package pydantic-ai-slim"
@@ -656,13 +656,17 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
mkdir test-durations
for python in 3.11 3.12 3.13 3.14; do
for python in 3.11 3.12 3.13 3.14 3.15; do
all_extras_shards=2
if [[ "$python" == 3.11 ]]; then all_extras_shards=3; fi
python3 .github/scripts/shard_durations.py fetch --prefix "test-durations-$python-all-extras-" \
--shards "$all_extras_shards" --output "test-durations/$python-all-extras.json"
python3 .github/scripts/shard_durations.py fetch --prefix "test-durations-$python-lowest-versions-" \
--shards 2 --output "test-durations/$python-lowest-versions.json"
# No 3.15 lowest-versions shards exist to fetch: like `test-durable-exec`'s `lowest` leg, they
# would compile our declared minimums from source on 3.15.
if [[ "$python" != 3.15 ]]; then
python3 .github/scripts/shard_durations.py fetch --prefix "test-durations-$python-lowest-versions-" \
--shards 2 --output "test-durations/$python-lowest-versions.json"
fi
done
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
@@ -684,7 +688,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
shard: [1, 2]
include:
- python-version: "3.11"
@@ -746,9 +750,12 @@ jobs:
# Cache only the immutable model used by `TestSentenceTransformers`. The old
# shared HF cache could be populated first by a matrix job that never downloaded
# this model, permanently turning an exact cache hit into a cold model cache.
# The `sentence-transformers` extra only installs below Python 3.14 (its marker in
# `pydantic_ai_slim/pyproject.toml`), so 3.14 and 3.15 skip these three steps: there,
# the download would fail its import and spend its retries before giving up.
- name: restore sentence-transformers test model
id: sentence-transformers-cache
if: matrix.python-version != '3.14'
if: matrix.python-version != '3.14' && matrix.python-version != '3.15'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/huggingface/hub/models--sentence-transformers-testing--stsb-bert-tiny-safetensors
@@ -767,7 +774,7 @@ jobs:
- name: pre-download sentence-transformers test model
id: sentence-transformers-download
if: >-
matrix.python-version != '3.14'
matrix.python-version != '3.14' && matrix.python-version != '3.15'
&& steps.sentence-transformers-cache.outputs.cache-hit != 'true'
continue-on-error: true
run: |
@@ -784,7 +791,7 @@ jobs:
# A failed non-fatal download must not become an immutable exact cache hit.
- name: save sentence-transformers test model
if: >-
matrix.python-version != '3.14'
matrix.python-version != '3.14' && matrix.python-version != '3.15'
&& steps.sentence-transformers-cache.outputs.cache-hit != 'true'
&& steps.sentence-transformers-download.outcome == 'success'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -895,6 +902,12 @@ jobs:
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
resolution: [locked, lowest]
# 3.15 runs the `locked` leg only: `lowest` installs our declared minimums, and 13 of them
# have no 3.15 wheel (pydantic-core 2.41, numpy 2.3, tiktoken 0.12, ...), so the leg would
# compile them from source past this job's timeout.
include:
- python-version: "3.15"
resolution: locked
env:
CI: true
# Python 3.11 and 3.14 cover version endpoints; both 3.12 resolutions cover Temporal dependency branches.
@@ -994,6 +1007,7 @@ jobs:
strategy:
fail-fast: false
matrix:
# No 3.15: our declared minimums (pydantic-core 2.41, numpy 2.3, tiktoken 0.12, ...) have no 3.15 wheels.
python-version: ["3.11", "3.12", "3.13", "3.14"]
shard: [1, 2]
env:
@@ -1150,6 +1164,7 @@ jobs:
strategy:
fail-fast: false
matrix:
# No 3.15: the examples need duckdb and datasets, which are skipped there (see `examples/pyproject.toml`).
python-version: ["3.11", "3.12", "3.13", "3.14"]
env:
CI: true
+2 -2
View File
@@ -65,7 +65,7 @@ concurrency:
env:
COLUMNS: 150
# `ci.yml` sets this at workflow level, which does not cross the `workflow_call` boundary
# (#7847), so the called workflow needs its own copy. Inert while this matrix is 3.11/3.14 --
# (#7847), so the called workflow needs its own copy. Inert while this matrix is 3.11/3.15 --
# both managed downloads -- but it stops a future matrix entry silently resolving to a
# system interpreter.
UV_PYTHON_PREFERENCE: only-managed
@@ -81,7 +81,7 @@ jobs:
matrix:
# The endpoints of `ci.yml`'s own `test` matrix. Keep these in step with it: an
# endpoint left behind here silently stops asking the question on that interpreter.
python-version: ['3.11', '3.14']
python-version: ['3.11', '3.15']
env:
CI: true
# `BLOCKBUSTER_ENABLED` as on `ci.yml`'s all-extras shards, whose time budget can't absorb its
+1 -1
View File
@@ -106,7 +106,7 @@ The repo contains a `uv` workspace defining multiple Python packages:
The project uses:
- [`uv`](https://docs.astral.sh/uv/getting-started/installation/), supporting Python 3.11 through 3.14
- [`uv`](https://docs.astral.sh/uv/getting-started/installation/), supporting Python 3.11 through 3.15
- Install all dependencies with `make install`
- `pre-commit`, can be installed with `uv tool install pre-commit`
- `ruff` via `make lint` and `make format`
+3 -1
View File
@@ -19,6 +19,7 @@ install-all-python: ## Install and synchronize an interpreter for every python v
UV_PROJECT_ENVIRONMENT=.venv312 uv sync --python 3.12 --frozen --all-extras --no-extra mcp-tasks --all-packages --group lint
UV_PROJECT_ENVIRONMENT=.venv313 uv sync --python 3.13 --frozen --all-extras --no-extra mcp-tasks --all-packages --group lint
UV_PROJECT_ENVIRONMENT=.venv314 uv sync --python 3.14 --frozen --all-extras --no-extra mcp-tasks --all-packages --group lint
UV_PROJECT_ENVIRONMENT=.venv315 uv sync --python 3.15 --frozen --all-extras --no-extra mcp-tasks --all-packages --group lint
.PHONY: sync
sync: .uv ## Update local packages and uv.lock
@@ -73,11 +74,12 @@ test: ## Run tests without coverage (fast, for local dev)
COLUMNS=150 $(if $(PYTEST_PYTHON),UV_PROJECT_ENVIRONMENT=.venv$(subst .,,$(PYTEST_PYTHON))) uv run $(if $(PYTEST_PYTHON),--python $(PYTEST_PYTHON)) pytest -n auto --dist=loadgroup --durations=20
.PHONY: test-all-python
test-all-python: ## Run tests on Python 3.11 to 3.14
test-all-python: ## Run tests on Python 3.11 to 3.15
COLUMNS=150 UV_PROJECT_ENVIRONMENT=.venv311 uv run --python 3.11 --all-extras --no-extra mcp-tasks --all-packages coverage run -p -m pytest
COLUMNS=150 UV_PROJECT_ENVIRONMENT=.venv312 uv run --python 3.12 --all-extras --no-extra mcp-tasks --all-packages coverage run -p -m pytest
COLUMNS=150 UV_PROJECT_ENVIRONMENT=.venv313 uv run --python 3.13 --all-extras --no-extra mcp-tasks --all-packages coverage run -p -m pytest
COLUMNS=150 UV_PROJECT_ENVIRONMENT=.venv314 uv run --python 3.14 --all-extras --no-extra mcp-tasks --all-packages coverage run -p -m pytest
COLUMNS=150 UV_PROJECT_ENVIRONMENT=.venv315 uv run --python 3.15 --all-extras --no-extra mcp-tasks --all-packages coverage run -p -m pytest
@uv run coverage combine
@uv run coverage report
+1
View File
@@ -32,6 +32,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
+2 -1
View File
@@ -67,7 +67,8 @@ replaces the rejected result before the model sees it.
- `block_high_risk`: ask the built-in defense to reject detected high or critical
risk results. The default is report-only.
- `semantic_detection`: add local ML classification beyond known patterns. This
requires the `prompt-injection-defender-ml` extra.
requires the `prompt-injection-defender-ml` extra. On Python 3.15 that extra
installs pattern detection only, until ONNX Runtime publishes Python 3.15 wheels.
- `tool_filter`: classify all tools, selected tool names, or tools accepted by a
[`ToolSelector`][pydantic_ai.tools.ToolSelector].
- `on_detection`: run a sync or async callback for each flagged verdict. A
+7 -2
View File
@@ -62,8 +62,13 @@ dependencies = [
# support (#6661) so the workspace lock can fork onto SDK v2 for the `mcp-tasks` extra.
"mcp[cli]>=1.28.1",
"modal>=1.0.4",
"duckdb>=1.4.2",
"datasets>=4.0.0",
# duckdb has no cp315 wheels, and a source build outlasts the CI timeout, so the 3.15 all-extras install
# skips it. The examples don't support 3.15 until then (no classifier, no 3.15 `test-examples` cell).
# `datasets` is only used by the same example (`data_analyst.py`), and on 3.15 it would pull pyarrow and
# xxhash bumps into the lock for nothing, so it's skipped there too.
# Markers removable once duckdb ships cp315 wheels (tracked in #9455): https://pypi.org/project/duckdb/#files
"duckdb>=1.4.2; python_version < '3.15'",
"datasets>=4.0.0; python_version < '3.15'",
"pandas>=2.3.3",
# Needed by the interactive cancel_and_resume example. Declaring it here also pulls prompt_toolkit
# into the lowest-versions test env, which un-skips the CLI tests; 3.0.29 is the first release whose
+1
View File
@@ -34,6 +34,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
+1
View File
@@ -32,6 +32,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
+1
View File
@@ -32,6 +32,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
+20
View File
@@ -35,6 +35,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Operating System :: OS Independent",
@@ -220,6 +221,16 @@ openai = "2026-10-06T21:31:20Z"
# google-genai 2.25.0 is the `google-genai` floor: it sends Gemini blob fields as `mime_type`/`file_uri`.
# TODO(adtyavrdhn): Remove after 2026-09-29 17:24 UTC.
google-genai = "2026-09-22T17:23:02Z"
# First releases with cp315 wheels, so Python 3.15 CI installs them instead of compiling from source.
# rpds-py 2026.9.1 is its first cp315 wheel built against the frozen 3.15 ABI (3.15.0rc1); earlier ones
# were built against betas, which broke greenlet's on 3.15.0.
# Each admits that release's artifacts through one second after its last upload.
# TODO(adtyavrdhn): Remove after 2026-10-13 20:33 UTC, when the last of these (asyncpg) leaves the cooldown,
# then run `uv lock` and `uv lock --script .github/scripts/pydantic-ai-runner`.
aiohttp = "2026-10-05T17:43:49Z"
asyncpg = "2026-10-06T20:32:41Z"
fastavro = "2026-10-05T22:24:00Z"
rpds-py = "2026-10-04T16:32:38Z"
[dependency-groups]
dev = [
@@ -258,6 +269,11 @@ dev = [
# `[mcp]` extra ships `fastmcp-slim[client]` which lacks server support, so devs need the full
# `fastmcp` package alongside, tracking the same range as the `[mcp]` extra (#6661).
"fastmcp>=3.3.0,<5",
# FastMCP 3 pulls in `beartype` 0.22 (via `py-key-value-aio`), which imports `typing.no_type_check_decorator`
# (removed in 3.15).
# Removable once beartype 0.23.0 final is released (tracked in #9455):
# https://github.com/beartype/beartype/issues/618
"beartype>=0.23.0rc2; python_version >= '3.15'",
# The MCP SDK v2 wire types as a standalone pure-Pydantic distribution, so the tests can check
# the compat readers' field-name pairs against the real v2 models while running against v1.
"mcp-types>=2.0.0",
@@ -535,6 +551,10 @@ filterwarnings = [
"ignore:UnsupportedFieldAttributeWarning:UserWarning",
# coolname (prefect dep) uses codecs.open(), deprecated in Python 3.14
"ignore:codecs.open\\(\\) is deprecated:DeprecationWarning",
# Python 3.15 deprecates `isinstance()` against protocols without `@runtime_checkable`, which
# `py-key-value-aio`'s `AsyncKeyValue` (via FastMCP) lacks; this suite's `error` filter would fail on it.
# Removable once `py-key-value-aio` decorates its protocols (tracked in #9455).
"ignore:<class 'key_value\\.aio\\..*'> isn't explicitly decorated with @runtime_checkable:DeprecationWarning",
# outlines' llamacpp backend calls llama-cpp-python's `Llama.from_pretrained`, which forwards
# `local_dir_use_symlinks` to `hf_hub_download`; huggingface-hub >= 1.0 deprecated (and ignores)
# that arg and warns about it. The arg is a no-op and the call site isn't ours to change.
@@ -79,7 +79,8 @@ replaces the rejected result before the model sees it.
- `block_high_risk`: ask the built-in defense to reject detected high or critical
risk results. The default is report-only.
- `semantic_detection`: add local ML classification beyond known patterns. This
requires the `prompt-injection-defender-ml` extra.
requires the `prompt-injection-defender-ml` extra. On Python 3.15 that extra
installs pattern detection only, until ONNX Runtime publishes Python 3.15 wheels.
- `tool_filter`: classify all tools, selected tool names, or tools accepted by a
[`ToolSelector`](https://pydantic.dev/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.ToolSelector).
- `on_detection`: run a sync or async callback for each flagged verdict. A
@@ -3,6 +3,7 @@
from __future__ import annotations
import importlib.util
import sys
from collections.abc import Awaitable, Callable
from dataclasses import KW_ONLY, dataclass, field
from typing import Any
@@ -169,9 +170,16 @@ class PromptInjectionDefender(AbstractCapability[AgentDepsT]):
self._defense = self.defense
else:
if self.semantic_detection and importlib.util.find_spec('onnxruntime') is None:
# The ML extra skips ONNX Runtime on 3.15, which it has no wheels for yet (tracked in #9455).
# A conditional expression rather than an `if`, so branch coverage doesn't need one interpreter
# to take both arms of this version check.
remedy = (
'ONNX Runtime has no Python 3.15 wheels yet, so semantic detection needs Python 3.14 or earlier.'
if sys.version_info >= (3, 15)
else 'Install it with: uv add "pydantic-ai-harness[prompt-injection-defender-ml]"'
)
raise UserError(
'PromptInjectionDefender requires ONNX Runtime when `semantic_detection=True`. '
'Install it with: uv add "pydantic-ai-harness[prompt-injection-defender-ml]"'
f'PromptInjectionDefender requires ONNX Runtime when `semantic_detection=True`. {remedy}'
)
# Construct `PromptDefense` directly; the `create_prompt_defense` factory is untyped and fails pyright strict.
self._defense = PromptDefense(
+12 -2
View File
@@ -37,6 +37,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Software Development :: Libraries",
"Typing :: Typed",
]
@@ -71,14 +72,23 @@ mongodb = ["pymongo>=4.17.0"]
skills = ["pyyaml>=6.0.2"]
browser-use = ["browser-use>=0.13.6,<0.14 ; python_full_version >= '3.11'"]
playwright = ["idna>=3.4", "playwright>=1.61.0"]
stackone = ["fastmcp>=3.4.5,<4", "pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
# FastMCP 3 resolves `beartype` 0.22 (via `py-key-value-aio`), which imports `typing.no_type_check_decorator` (removed in 3.15).
# The beartype line is removable once beartype 0.23.0 final is released (tracked in #9455):
# https://github.com/beartype/beartype/issues/618
stackone = ["fastmcp>=3.4.5,<4", "beartype>=0.23.0rc2 ; python_version >= '3.15'", "pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
ordinal = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
grain = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
day-ai = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
posthog = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
pylon = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
prompt-injection-defender = ["stackone-defender>=0.7.3,<0.8 ; python_full_version >= '3.11'"]
prompt-injection-defender-ml = ["stackone-defender[onnx]>=0.7.3,<0.8 ; python_full_version >= '3.11'"]
# The base requirement keeps pattern detection installed on 3.15, where onnxruntime has no wheels yet.
# Fold back into one `[onnx]` line once onnxruntime ships cp315 wheels (tracked in #9455):
# https://pypi.org/project/onnxruntime/#files
prompt-injection-defender-ml = [
"stackone-defender>=0.7.3,<0.8 ; python_full_version >= '3.11'",
"stackone-defender[onnx]>=0.7.3,<0.8 ; python_full_version >= '3.11' and python_version < '3.15'",
]
github = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
slack = ["pydantic-ai-slim[mcp]=={{ major }}.{{ minor }}.{{ patch }}{% if distance %}.dev{{ distance }}+{{ commit }}{% endif %}"]
coder = ["ripgrep==14.1.0 ; sys_platform != 'android'"]
+1
View File
@@ -66,6 +66,7 @@ def _shown(screen: int) -> bytes:
@posix_only
@pytest.mark.subprocess(reason='a child process must inherit the relayed stderr, which is the behavior under test')
def test_relay_keeps_fork_notices_off_the_terminal(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(stderr_relay.sys, 'platform', 'darwin')
child = f'import os; os.write(2, b"child error\\n"); os.write(2, {_NOTICE!r}); os.write(2, b"child done\\n")'
@@ -3,6 +3,7 @@
from __future__ import annotations
import importlib.util
import sys
from typing import Any
import pytest
@@ -69,13 +70,24 @@ def test_defense_with_semantic_detection_raises() -> None:
PromptInjectionDefender(_observe(), semantic_detection=True)
def test_semantic_detection_without_onnx_raises(monkeypatch: pytest.MonkeyPatch) -> None:
@pytest.mark.parametrize(
('version_info', 'remedy'),
[
((3, 14), 'uv add "pydantic-ai-harness[prompt-injection-defender-ml]"'),
((3, 15), 'needs Python 3.14 or earlier'),
],
)
def test_semantic_detection_without_onnx_raises(
monkeypatch: pytest.MonkeyPatch, version_info: tuple[int, int], remedy: str
) -> None:
def no_spec(name: str) -> None:
return None
monkeypatch.setattr(importlib.util, 'find_spec', no_spec)
with pytest.raises(UserError, match='ONNX Runtime'):
monkeypatch.setattr(sys, 'version_info', version_info)
with pytest.raises(UserError, match='ONNX Runtime') as exc_info:
PromptInjectionDefender(semantic_detection=True)
assert remedy in str(exc_info.value)
@requires_onnx
+6
View File
@@ -665,6 +665,12 @@ def test_the_fork_handler_hands_back_a_lock_nobody_holds():
@pytest.mark.skipif(not hasattr(os, 'fork'), reason='fork is POSIX-only')
# Forking while another thread holds the lock is the scenario under test. Python 3.12+ warns about forking a
# multithreaded process; from 3.15, `os.fork()` raises that warning under this suite's `error` filter instead
# of swallowing it.
@pytest.mark.filterwarnings(
'ignore:This process .* is multi-threaded, use of fork\\(\\) may lead to deadlocks:DeprecationWarning'
)
def test_a_banner_does_not_hang_a_process_forked_mid_claim():
"""`fork` clones one thread, so a lock another was holding is held forever in the child.
Generated
+1746 -1280
View File
File diff suppressed because it is too large Load Diff