Skip to content

Add polymorphic_serialization option#12518

Merged
Viicos merged 19 commits intomainfrom
dh/polymorphic-serialization
Feb 21, 2026
Merged

Add polymorphic_serialization option#12518
Viicos merged 19 commits intomainfrom
dh/polymorphic-serialization

Conversation

@davidhewitt
Copy link
Copy Markdown
Collaborator

@davidhewitt davidhewitt commented Nov 11, 2025

Change Summary

(Imported from pydantic/pydantic-core#1881)

Introduced as a possible solution to #12382; the theory is that most uses of serialize_as_any are because users want to respect subtyping properly.

This PR introduces a configuration option and runtime flag polymorphic_serialization which is used to enable serializing subclasses of models and dataclasses as the subclass, not as the base class.

To maintain backwards compatibility, the default is False - models and dataclasses will be serialized as the exact type in the schema.

When the config is set to True, then models and dataclasess will be serialized as their runtime type. This also respects any model serializer the subclass may be using.

Users can also pass polymorphic_serialization as a runtime option to the serialization functions; doing so will override the config value (i.e. can be globally enabled or disabled).


Points of Discussion for this PR

Annotated metadata

Not explored yet in this PR, an additional / alternative possibility could be to expose this as some kind of wrapper so that for foreign types one can use Annotated[ForeignModel, PolymorphicSerialization] to enable this even when the type didn't opt in.

(An Annotated form may remove the need for the global runtime override.)

Non-Pydantic types

This method cannot really ever work for types which Pydantic doesn't know how to serialize, e.g. subclass of int will not be serialized polymorphically. If the subclass had a __pydantic_serializer__ attribute it could in theory work, but I think that's the unlikely case.

More complicated is stdlib dataclasses and TypedDict. It might be reasonable to expect these to work, but I think that would require inspection of the subclass types at runtime and somehow caching a serializer for them. (i.e. a lot of runtime callback into Pydantic.)

Backwards compatibility

For the concern listed above on non-pydantic types, one way to introduce support for them later would be to have the runtime flag accept a set of types to serialize, e.g. polymorphic_serialization = {BaseModel, BaseDataClass, int}

... but another way to avoid the problem entirely would be to not have the global runtime flag and use the Annotated form to apply this behavior only to supported types.

Related issue number

#12382

Checklist

  • The pull request title is a good summary of the changes - it will be used in the changelog
  • Unit tests for the changes exist
  • Tests pass on CI
  • Documentation reflects the changes where applicable
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

@github-actions github-actions Bot added the relnotes-fix Used for bugfixes. label Nov 11, 2025
@davidhewitt davidhewitt added relnotes-feature backport-2.13 Needs backport to 2.13 and removed relnotes-fix Used for bugfixes. labels Nov 11, 2025
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Nov 11, 2025

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Nov 11, 2025

Merging this PR will degrade performance by 8.43%

❌ 1 regressed benchmark
✅ 211 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_simple_recursive_model_schema_generation 616.4 µs 673.1 µs -8.43%

Comparing dh/polymorphic-serialization (6a04e9f) with main (12288b1)

Open in CodSpeed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Nov 21, 2025

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pydantic
  config.py
  main.py
  pydantic/_internal
  _config.py
  pydantic-core/python/pydantic_core
  core_schema.py
Project Total  

This report was generated by python-coverage-comment-action

@Viicos Viicos force-pushed the dh/polymorphic-serialization branch from f062486 to 3f56f5d Compare December 1, 2025 14:52
@Viicos Viicos changed the title add polymorphic_serialization flag Add polymorphic_serialization option Dec 1, 2025
@davidhewitt davidhewitt marked this pull request as ready for review January 12, 2026 15:09
@davidhewitt davidhewitt requested a review from Viicos January 12, 2026 15:09
Comment thread docs/concepts/serialization.md
Comment on lines +633 to +636
!!! warning Polymorphic serialization of standard library dataclasses
Polymorphic serialization is only supported for Pydantic models and [Pydantic dataclasses](./dataclasses.md).
When using [standard library dataclasses][dataclasses], polymorphic serialization is *not* supported,
even if the dataclass is a subclass of a Pydantic dataclass. This may be fixed in a future Pydantic release.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How hard would it be to fallback to inference (as serialize_as_any does) in this case? I'm worried we recommend switching from serialize_as_any to polymorphic_serialization but then users discover that it's "less powerful" in this case.

I guess it's a trade-off between not having it supported for now (and being able to implement it properly later), and having the limited inference behavior now (and users could as such rely on it) but such behavior changing when we implement it properly (and so is a behavior change for users already relying on it).

Comment thread pydantic-core/python/pydantic_core/core_schema.py
@Viicos Viicos force-pushed the dh/polymorphic-serialization branch from d116784 to eb7a7ec Compare February 20, 2026 20:12
@Viicos Viicos force-pushed the dh/polymorphic-serialization branch 2 times, most recently from 6d6e402 to ca84f7d Compare February 20, 2026 22:26
@Viicos Viicos force-pushed the dh/polymorphic-serialization branch from ca84f7d to 3917617 Compare February 20, 2026 22:31
@Viicos Viicos merged commit c41a258 into main Feb 21, 2026
81 of 82 checks passed
@Viicos Viicos deleted the dh/polymorphic-serialization branch February 21, 2026 08:48
@Viicos Viicos added the needs-blogpost-entry This PR needs to be documented in the release notes blog post label Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-blogpost-entry This PR needs to be documented in the release notes blog post relnotes-feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants