Skip to content

Instantly share code, notes, and snippets.

@imaurer
Created October 28, 2025 10:38
Show Gist options
  • Select an option

  • Save imaurer/c42b0ddbc56aa318c8db5d50b088fd0f to your computer and use it in GitHub Desktop.

Select an option

Save imaurer/c42b0ddbc56aa318c8db5d50b088fd0f to your computer and use it in GitHub Desktop.

Determining the Minimum Supported Pydantic Version (Binary Cut)

This note documents how we determined the lowest Pydantic 2.x version that works across our supported Python versions (3.10–3.14) using uv and pytest. It also records the final requirement change made to pyproject.toml.

Objective

  • Identify the minimum Pydantic version that passes our test suite across Python 3.10, 3.11, 3.12, 3.13, and 3.14.
  • Update our dependency constraint accordingly.

Tooling

  • uv for isolated environments, dependency resolution, and Python switching
  • pytest for running the test suite

Strategy (Binary Cut)

Rather than sweeping all versions linearly, we:

  • Verified a recent upper bound (2.12.3), which we expected to pass.
  • Probed an early lower bound (2.0.3), which failed on newer Pythons due to pydantic-core build issues.
  • Bisected across representative releases (2.6.x, 2.9.x, 2.10.x, 2.11.x, 2.12.x) to locate the first version that passes on all supported Python versions.

uv Python Version Switching

uv can select a Python interpreter per run via --python:

uv run --isolated --python=3.12 --extra dev pytest -q

This aligns with our Makefile pattern for matrix testing.

Commands Used

Validate a recent upper bound:

# Pydantic 2.12.3 across all supported Pythons
for py in 3.10 3.11 3.12 3.13 3.14; do
  echo "===> Python $py, pydantic 2.12.3"
  uv run --isolated --python="$py" --extra dev --with 'pydantic==2.12.3' pytest -q
done

Probe an early lower bound:

# Pydantic 2.0.3 across all supported Pythons
for py in 3.10 3.11 3.12 3.13 3.14; do
  echo "===> Python $py, pydantic 2.0.3"
  uv run --isolated --python="$py" --extra dev --with 'pydantic==2.0.3' pytest -q
done

Binary cut over representative versions:

PY_VERSIONS=(3.10 3.11 3.12 3.13 3.14)
CANDIDATES=(2.6.4 2.9.2 2.10.6 2.11.10 2.12.0 2.12.3)

for v in "${CANDIDATES[@]}"; do
  echo -e "\n=== pydantic $v across Pythons ==="
  for py in "${PY_VERSIONS[@]}"; do
    echo -n "Py ${py} pydantic ${v}: "
    if uv run --isolated --python="$py" --extra dev --with "pydantic==${v}" pytest -q >/dev/null; then
      echo PASS
    else
      echo FAIL
    fi
  done
done

Sanity probe to print versions:

import sys, pydantic
print(sys.version.split()[0], 'pydantic', pydantic.__version__)
try:
    import pydantic_core
    print('pydantic_core', pydantic_core.__version__)
except Exception as e:
    print('pydantic_core import failed:', e)

Results Summary

  • 2.12.3: PASS on 3.10, 3.11, 3.12, 3.13, 3.14
  • 2.12.0: PASS on 3.10, 3.11, 3.12, 3.13, 3.14
  • 2.11.10: PASS on 3.10–3.13, FAIL on 3.14 (pydantic-core via PyO3 blocks 3.14)
  • 2.10.6: PASS on 3.10–3.13, FAIL on 3.14
  • 2.9.2: PASS on 3.10–3.13, FAIL on 3.14
  • 2.6.4: PASS on 3.10–3.12, FAIL on 3.13–3.14
  • 2.0.3: PASS on 3.10–3.12, FAIL on 3.13–3.14

Root cause for pre-2.12 failures on Python 3.14:

  • Older pydantic-core releases lack cp314 wheels and fall back to native builds with PyO3 versions that reject Python 3.14 at build time.
  • Pydantic 2.12.x provides compatibility/wheels for 3.14.

Conclusion

  • Minimum supported pydantic version: 2.12.0 (covers Python 3.10–3.14)
  • pyproject.toml updated to:
[project]
dependencies = [
    "pydantic>=2.12.0",
]

If we ever restrict our Python range to ≤3.13, the minimum could be relaxed to >=2.9.2; however, with 3.14 in scope, >=2.12.0 is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment