Skip to content

Ensure __pydantic_private__ is set in model_construct() with user-defined model_post_init()#12816

Merged
Viicos merged 4 commits intopydantic:mainfrom
nightcityblade:fix/issue-12813
Mar 2, 2026
Merged

Ensure __pydantic_private__ is set in model_construct() with user-defined model_post_init()#12816
Viicos merged 4 commits intopydantic:mainfrom
nightcityblade:fix/issue-12813

Conversation

@nightcityblade
Copy link
Copy Markdown
Contributor

Fixes #12813

Problem

When a model defines a custom model_post_init but has no private attributes, model_construct() fails to set __pydantic_private__, causing pickle.dumps() to crash with AttributeError.

This happens because:

  1. When there are no private attributes, the metaclass does not wrap model_post_init with init_private_attributes
  2. model_construct only sets __pydantic_private__ in the elif branch (when __pydantic_post_init__ is falsy)
  3. With a user-defined model_post_init, __pydantic_post_init__ is truthy, so the elif branch is skipped
  4. The user's model_post_init does not set __pydantic_private__ → attribute never gets set

Fix

After calling model_post_init in model_construct, check if __pydantic_private__ was set. If not, set it to None.

Test

Added test_model_construct_pickle_with_model_post_init that reproduces the exact scenario from the issue.

@github-actions github-actions Bot added the relnotes-fix Used for bugfixes. label Feb 18, 2026
@Viicos Viicos changed the title fix: ensure __pydantic_private__ is set in model_construct with user-defined model_post_init Ensure __pydantic_private__ is set in model_construct() with user-defined model_post_init() Feb 19, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Feb 19, 2026

Merging this PR will not alter performance

✅ 212 untouched benchmarks


Comparing nightcityblade:fix/issue-12813 (0a56b6b) with main (ae41acd)

Open in CodSpeed

…r-defined model_post_init()

When a model defines model_post_init() and uses PrivateAttr fields,
calling model_construct() would fail because __pydantic_private__ was
not initialized before model_post_init() was called.

This ensures __pydantic_private__ is always set with default values
before model_post_init() runs in model_construct().

Fixes pydantic#12813
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 22, 2026

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pydantic
  main.py
Project Total  

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

@Viicos Viicos enabled auto-merge (squash) March 2, 2026 14:10
@Viicos Viicos merged commit 0967dd9 into pydantic:main Mar 2, 2026
141 of 143 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

relnotes-fix Used for bugfixes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

model_construct() can leave __pydantic_private__ unset when model_post_init is user-defined, causing pickle.dumps() to crash in BaseModel.__getstate__

2 participants