Skip to content

Migrate to pydantic 2 - #96

Merged
sequoiap merged 2 commits into
masterfrom
feat/pydantic-2
Oct 5, 2026
Merged

sequoiap merged 2 commits into
masterfrom
feat/pydantic-2

Conversation

@sequoiap

@sequoiap sequoiap commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Moves pyrolab from pydantic 1 to pydantic 2. This unblocks moving autogator to pydantic 2, which needs gdsfactory 7.4 or newer, or dropping gdsfactory for plain gdstk. That autogator work follows in a separate PR. Targets master; it doesn't depend on anything else.

What changed

  • Dependency: pydantic>=2.8,<3. 2.8 is the first version with coerce_numbers_to_str (see below).

  • Config models (configure.py):

    • model_config = ConfigDict(extra="forbid", validate_default=True, coerce_numbers_to_str=True) replaces class Config.
    • @field_validator replaces @validator, and the cross-reference check becomes an "after" @model_validator.
    • Nameservers get their names in model_post_init instead of a custom __init__, so this happens on every way a config is created: constructed, validated from YAML, or copied.
    • PrivateAttr is now imported from pydantic itself.
  • API renames throughout:

    pydantic 1 pydantic 2
    .dict() .model_dump()
    .json() .model_dump_json()
    parse_obj model_validate
    parse_file / parse_raw model_validate_json
    .copy(deep=True) .model_copy(deep=True)
    __fields_set__ model_fields_set
  • Error messages: describe_config_error shows our validators' own messages. pydantic 2 would otherwise prefix them with "Value error, ...". Its own messages have new wording, for example "Extra inputs are not permitted", and the tests now expect that.

Compatibility with existing files

  • Numbers in text fields: pydantic 1 accepted a number where text was expected, as in an unquoted version: 1.0 or a numeric description. Strict pydantic 2 rejects these, which would break existing configs, so the coerce_numbers_to_str setting keeps accepting them. A test covers it.
  • Files from the old version load: all three shipped configs, and a lockfile written by the previous release, load unchanged.
  • One behaviour change: model equality now includes private attributes. Two nameserver configs with the same settings but different names are therefore no longer equal. Nothing in pyrolab relies on this; the one test that compared them now compares model_dump().

Read the Docs

docs/requirements.txt pinned pydantic==1.10.21. With pyrolab requiring pydantic 2, Read the Docs would no longer be able to install the docs requirements together with pyrolab. Read the Docs runs pip install . after installing that file, which already brings pyrolab's own dependencies, so the file now lists only the docs tools: jupyter-book and numpydoc. The pydantic, Pyro5, pyyaml, numpy and scipy pins are gone, and so is the unused linkify-it-py. autodoc also no longer mocks deprecation, typer, colorama and tabulate, which are real dependencies.

A Read-the-Docs-style build (fresh Python 3.11 venv, then the requirements file, then pip install ., then sphinx) succeeds from a clean checkout. Its warnings match master's, apart from ones from pydantic 2's own docstrings, which use mkdocs syntax and appear on the pages for the inherited BaseModel methods.

Checks run

  • Suite: 434 passed, 9 skipped, including new tests for the two compatibility points above. pre-commit passes on every file.
  • No leftover v1 API: the suite also passes with pydantic's deprecation warnings (PydanticDeprecatedSince20) turned into errors.
  • End-to-end run with real processes: config update with an unquoted version: 1.0, then up, ps, status, info, and reload with re-registration at the nameserver, then down. No processes were left behind.

🤖 Generated with Claude Code

pydantic 1 is in maintenance mode, and newer packages in the lab's stack
(e.g. gdsfactory >= 7.4, needed to unblock autogator) require pydantic 2.

- Require pydantic>=2.8,<3 (2.8 for coerce_numbers_to_str).
- Config models: ConfigDict instead of class Config; field_validator and
  an "after" model_validator for the cross-reference check;
  model_post_init (rather than a custom __init__) names nameservers on
  every construction/validation path; PrivateAttr from pydantic.
- model_dump / model_validate / model_validate_json / model_copy replace
  dict / parse_obj / parse_file / parse_raw / copy throughout.
- describe_config_error reports our validators' own messages rather than
  pydantic 2's "Value error, ..." wrapping.
- Compatibility: coerce_numbers_to_str keeps configs that pydantic 1
  accepted (an unquoted `version: 1.0`) loading; lockfiles and configs
  written by the previous release still parse.
- docs/requirements.txt pinned pydantic==1.10.21, which would have made
  Read the Docs unable to install pyrolab at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read the Docs installs docs/requirements.txt and then PyroLab itself, which
brings its own dependencies. Repeating them here (pydantic, Pyro5, pyyaml,
numpy, scipy) only risked pins that disagree with pyproject.toml, as the old
pydantic==1.10.21 pin did. linkify-it-py was unused (MyST's linkify
extension isn't enabled). Also stop mocking deprecation, typer, colorama,
and tabulate for autodoc: they are real dependencies, always installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sequoiap
sequoiap merged commit 3f12d57 into master Oct 5, 2026
9 checks passed
@sequoiap
sequoiap deleted the feat/pydantic-2 branch October 5, 2026 16:42
@sequoiap sequoiap mentioned this pull request Oct 5, 2026
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.

1 participant