Skip to content

Migrate front from pydantic v1 to pydantic v2 - #31

Merged
thorwhalen merged 1 commit into
masterfrom
pydantic-v2-migration
May 19, 2026
Merged

thorwhalen merged 1 commit into
masterfrom
pydantic-v2-migration

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Closes #30.

Why

front pinned pydantic==1.10.12, propagating that pin into streamlitfront,
extrude, and anything else composing front elements. Meanwhile, newer
i2mint packages (e.g. ju via http2py) call pydantic v2 APIs at import
time, so the v1 pin was breaking the wider ecosystem. This PR cuts the
anchor: front now requires pydantic>=2.

This is a hard cut — no v1 compatibility shim. The migration surface
inside front is tiny (one module, ~140 lines), and dual-supporting v1
and v2 would cost more than the migration itself.

What changed

front/py2pydantic.py

  • Doctest examples updated from schema_json() to model_json_schema()
    (v1's schema_json() is removed in v2).
  • func_to_pyd_model_specs now infers the field type from a default's
    type when no annotation is provided. Pydantic v2 requires explicit
    (type, default) tuples; v1's "bare default infers type" path no
    longer works.
  • BaseModel-shadowing field names (e.g. copy, schema) are detected
    proactively now. In v1 a conflict raised NameError, which the code
    caught and used as a trigger for the auto-uppercase rename. In v2,
    pydantic merely warns and leaves the conflict in place, so the
    detection has to be upfront via set(Sig(func).names) & set(dir(BaseModel)).
  • Module docstring expanded — it's auto-extracted for docs.

front/tests/test_py2pydantic.py

  • model.__fields__ → model.model_fields
  • field.type_ → field.annotation
  • Defaults are now pydantic_core.PydanticUndefined for required fields
    rather than None. Test now uses field.is_required() to check.

front/tests/test_py2pydantic_integration.py (new)

End-to-end coverage of the path that downstream packages actually
depend on:

  • generate input model from a Python function
  • instantiate (exercises validation/coercion)
  • dispatch through the wrapper
  • verify the original function ran and returned the right thing
  • verify ValidationError surfaces on bad input

This was previously only exercised by the doctest. The pre-existing
unit test only checked model-class structure, not dispatch behavior.

pyproject.toml

  • pydantic==1.10.12 → pydantic>=2

Migration guide for downstream callers

If your code uses front.py2pydantic.func_to_pyd_input_model_cls(...):

pydantic v1 pydantic v2
model.__fields__ model.model_fields
field.type_ field.annotation
field.default (None if required) field.is_required() + field.default
model.schema_json() json.dumps(model.model_json_schema())
instance.dict() instance.model_dump()
instance.json() instance.model_dump_json()

create_model, instantiating models, and basic validation are unchanged.

Verified

$ python -m pytest --doctest-modules front/ tests/ \
    --ignore=front/scrap --ignore=front/examples --ignore=examples \
    --ignore=scrap --ignore=docsrc
======================== 37 passed, 2 warnings in 0.69s ========================

Including the new integration tests, all 37 doctests + unit tests pass
under pydantic 2.12.2.

Follow-ups

After this lands and a new front version is on PyPI:

  1. streamlitfront — drop its v1-era streamlit_pydantic / pydantic
    usage, bump to a v2-compatible release. Separate PR.
  2. extrude — revert the temporary CI workarounds (Python 3.12 +
    Windows skip) added during the wider CI sweep. Should be one-line
    reverts once the ecosystem is consistent on v2.

Hard cut — no compatibility shim. See issue #30 for the migration guide.

Changes
-------
- front/py2pydantic.py
  - Doctest examples switched from schema_json() to model_json_schema().
  - Detect BaseModel-shadowing field names proactively (pydantic v2 only
    warns instead of raising NameError, which left conflicting fields in
    place silently). The auto-uppercase rename is now triggered upfront.
  - In func_to_pyd_model_specs, infer the field type from a default's
    type when no annotation is given. Pydantic v2 requires (type, default)
    tuples and no longer infers the type from a bare default value.
  - Module docstring expanded — concrete enough to drive doc generation.

- front/tests/test_py2pydantic.py
  - model.__fields__ -> model.model_fields
  - field.type_ -> field.annotation
  - default sentinel: handled via field.is_required() and the
    pydantic_core.PydanticUndefined sentinel rather than comparing to None.

- front/tests/test_py2pydantic_integration.py (new)
  - End-to-end coverage of the function -> pydantic input model ->
    wrapped-call -> result pipeline that streamlitfront and extrude
    depend on. Previously only the doctest exercised this path; the
    unit test only inspected model class structure.

- pyproject.toml
  - dependencies: pydantic==1.10.12 -> pydantic>=2
@thorwhalen
thorwhalen merged commit b0557ec into master May 19, 2026
16 of 18 checks passed
@thorwhalen
thorwhalen deleted the pydantic-v2-migration branch May 19, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate front from pydantic v1 to pydantic v2

1 participant