Sentinel and Special-Purpose Schemas
Sentinel and special-purpose schemas in pydantic-core provide the foundation for handling non-standard types, nullability, and internal state management. Unlike standard type schemas (like IntSchema or StringSchema), these schemas define how the validator should treat None, missing values, or catch-all scenarios.
Handling Nullability
Pydantic distinguishes between a schema that only accepts None and a schema that allows a value to be either a specific type or None.
None Schema
The NoneSchema (created via core_schema.none_schema()) is a strict validator that only accepts the Python None value. It is defined in pydantic-core/python/pydantic_core/core_schema.py.
from pydantic_core import SchemaValidator, core_schema
# Only None is allowed
schema = core_schema.none_schema()
v = SchemaValidator(schema)
assert v.validate_python(None) is None
# v.validate_python(1) would raise a ValidationError
Nullable Schema
The NullableSchema is a wrapper used to implement Optional types. It takes an inner schema and allows the input to be either None or a value that satisfies the inner schema.
from pydantic_core import SchemaValidator, core_schema
# Allows a string OR None
schema = core_schema.nullable_schema(core_schema.str_schema())
v = SchemaValidator(schema)
assert v.validate_python("hello") == "hello"
assert v.validate_python(None) is None
The NullableSchema also supports a strict flag, which determines if the underlying schema should be validated in strict mode.
The Catch-All: Any Schema
The AnySchema is used when a field should accept any Python object without validation. It is frequently used as a fallback or for dynamic data where the structure is unknown.
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.any_schema()
v = SchemaValidator(schema)
assert v.validate_python(1) == 1
assert v.validate_python({"key": "value"}) == {"key": "value"}
assert v.validate_python(None) is None
In pydantic-core, AnySchema is often the default return_schema for serializers when no specific type information is available.
Internal Sentinels and Error States
Some schemas are designed for internal coordination or to mark specific states during schema construction.
Missing Sentinel Schema
The MissingSentinelSchema is used to represent the MISSING state. This is distinct from None, as it typically represents a value that was not provided at all (e.g., a missing key in a dictionary).
from pydantic_core import core_schema
# Represents the internal MISSING sentinel
schema = core_schema.missing_sentinel_schema()
Invalid Schema
The InvalidSchema acts as a placeholder. It is not intended to be used for actual validation. If a SchemaValidator is initialized with an InvalidSchema, it will raise a SchemaError. This is useful for marking incomplete definitions or states that should never be reached in a valid schema tree.
As seen in pydantic-core/tests/test_schema_functions.py:
import pytest
from pydantic_core import SchemaValidator, core_schema, SchemaError
def test_err_on_invalid():
# Attempting to use an invalid schema raises a SchemaError
with pytest.raises(SchemaError, match='Cannot construct schema with `InvalidSchema` member.'):
SchemaValidator(core_schema.invalid_schema())
Comparison Summary
| Schema | Type Literal | Purpose |
|---|---|---|
NoneSchema | 'none' | Matches only None. |
NullableSchema | 'nullable' | Wraps another schema to allow None. |
AnySchema | 'any' | Matches any value. |
MissingSentinelSchema | 'missing-sentinel' | Matches the internal MISSING sentinel. |
InvalidSchema | 'invalid' | Placeholder that triggers SchemaError on use. |