Skip to content

Conversation

Viicos
Copy link
Member

@Viicos Viicos commented May 16, 2025

Change Summary

Requires pydantic/pydantic-core#1711.

Fixes #5326.
Fixes #6928.
Fixes #7712.
Fixes #8394.
Fixes #9057.
Fixes #11705.
Fixes #11843.

Example:

from pydantic import BaseModel
from pydantic.experimental.missing_sentinel import MISSING


class A(BaseModel):
    a: int | MISSING = MISSING

a1 = A()
a1.model_dump()
#> {}

a2 = A(a=1)
a2.model_dump()
#> {'a': 1}

A.model_json_schema()
#> {'properties': {'a': {'title': 'A', 'type': 'integer'}}, 'title': 'A', 'type': 'object'}

class B(BaseModel):
    a: MISSING = MISSING


B.model_json_schema()
#> {'properties': {}, 'title': 'B', 'type': 'object'}

Things to consider:

  • Sentinel name: UNSET seems to be the best fit (and matches msgspec), but will be confusing as we already have a concept of unset fields (tracked in __pydantic_fields_set__). These unset fields can already be excluded by specifying exclude_unset=True during serialization. If users explicitly set exclude_unset=False but the UNSET sentinel is still excluded, this will be confusing. One alternative could be MISSING: it also makes it a little bit clearer that the expected behavior is to have fields set to MISSING excluded from the output. Went with MISSING.
  • Sentinel location: currently defined in pydantic-core, but we need to expose it from the experimental module as we rely on the draft PEP 661.
  • Should we create a new core schema for the UNSET sentinel? I'm currently using a 'literal' schema, but not ideal for a couple reasons:
    • The core validator uses equality to validate literal values, but sentinels should be compared using identity.
    • If we happen to implement an optimization where we merge 'literal' schemas together (e.g. Literal[1] | UNSET -> Literal[1, UNSET]), we would need to account for this in the JSON Schema generation logic). Went with a new core schema type.

@github-actions github-actions bot added the relnotes-fix Used for bugfixes. label May 16, 2025
Copy link
Contributor

github-actions bot commented May 16, 2025

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pydantic
  fields.py
  json_schema.py
  version.py
  pydantic/_internal
  _generate_schema.py
  pydantic/experimental
  missing_sentinel.py
Project Total  

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

@davidhewitt
Copy link
Contributor

  • Should we create a new core schema for the UNSET sentinel?

I could imagine users might want custom sentinels too, should there be a generic "sentinel" schema?

@Viicos Viicos added relnotes-feature needs-blogpost-entry This PR needs to be documented in the release notes blog post and removed relnotes-fix Used for bugfixes. labels May 18, 2025
Copy link

cloudflare-workers-and-pages bot commented Jul 1, 2025

Deploying pydantic-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9ed9218
Status: ✅  Deploy successful!
Preview URL: https://b51576eb.pydantic-docs.pages.dev
Branch Preview URL: https://unset-sentinel.pydantic-docs.pages.dev

View logs

@Viicos Viicos changed the title Add UNSET sentinel Add MISSING sentinel Jul 25, 2025
@Viicos Viicos marked this pull request as ready for review July 25, 2025 21:47
Copy link

codspeed-hq bot commented Jul 25, 2025

CodSpeed Performance Report

Merging #11883 will not alter performance

Comparing unset-sentinel (9ed9218) with main (de6528b)

Summary

✅ 46 untouched benchmarks

@Viicos Viicos changed the title Add MISSING sentinel Add experimental MISSING sentinel Jul 26, 2025
@Viicos Viicos merged commit 7c40924 into main Jul 26, 2025
64 checks passed
@Viicos Viicos deleted the unset-sentinel branch July 26, 2025 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment