Core functionality for Pydantic validation and serialization
—
Sentinel values, utility classes, and helper functions for advanced validation scenarios and edge case handling. These utilities provide fine-grained control over validation behavior and enable sophisticated data processing patterns.
Special values that represent different states during validation and serialization.
PydanticUndefined: PydanticUndefinedType
"""
Sentinel value representing undefined/missing values in Pydantic Core.
This is the primary sentinel value used internally by pydantic-core
to indicate when a value has not been provided or is undefined.
"""
class PydanticUndefinedType:
"""
Type class for the PydanticUndefined sentinel.
This type ensures PydanticUndefined has a unique type that can
be used in type annotations and isinstance checks.
"""
def __repr__(self) -> str:
return 'PydanticUndefined'
def __bool__(self) -> bool:
return False
MISSING: Sentinel
"""
Sentinel indicating a field value was not provided during validation.
Can be used as a default value alternative to None when None has
explicit meaning. During serialization, fields with MISSING are excluded.
"""
UNSET: Sentinel # Alias for MISSING
"""
Alternative name for MISSING sentinel value.
Provides a more explicit name when indicating that a field
has not been set or provided.
"""Helper classes for advanced validation and data handling scenarios.
class Some:
"""
Wrapper for optional values that distinguishes between None and undefined.
Useful when you need to differentiate between an explicit None value
and a missing/undefined value in validation contexts.
"""
def __init__(self, value):
"""
Create a Some wrapper around a value.
Args:
value: The value to wrap (can be None)
"""
self.value = value
def __repr__(self) -> str:
return f'Some({self.value!r})'
class ArgsKwargs:
"""
Container for function arguments and keyword arguments.
Used in advanced validation scenarios where you need to capture
and validate both positional and keyword arguments together.
"""
def __init__(self, args: tuple, kwargs: dict):
"""
Create an ArgsKwargs container.
Args:
args: Tuple of positional arguments
kwargs: Dictionary of keyword arguments
"""
self.args = args
self.kwargs = kwargs
def __repr__(self) -> str:
return f'ArgsKwargs({self.args!r}, {self.kwargs!r})'Special marker classes used to control validation and serialization behavior.
class PydanticOmit:
"""
Marker to omit fields during serialization.
When a field value is set to PydanticOmit (or a validator returns it),
the field will be excluded from serialization output.
"""
def __repr__(self) -> str:
return 'PydanticOmit'
class PydanticUseDefault:
"""
Marker to use default value during validation.
When a validator returns PydanticUseDefault, the validation process
will use the field's default value instead of the returned marker.
"""
def __repr__(self) -> str:
return 'PydanticUseDefault'class TzInfo:
"""
Timezone information handling for datetime validation.
Provides timezone-aware datetime processing and validation
with support for various timezone representations.
"""
def __repr__(self) -> str:
return 'TzInfo(...)'from pydantic_core import SchemaValidator, MISSING, UNSET, PydanticUndefined
from pydantic_core.core_schema import dict_schema, str_schema, default_schema
# Schema with optional fields using sentinels
schema = dict_schema({
'name': str_schema(),
'nickname': default_schema(str_schema(), default=MISSING),
'email': default_schema(str_schema(), default=UNSET),
'phone': default_schema(str_schema(), default=PydanticUndefined)
})
validator = SchemaValidator(schema)
# Test data without optional fields
data = {'name': 'Alice'}
result = validator.validate_python(data)
print(f"Result: {result}")
print(f"Nickname: {result.get('nickname', 'NOT_FOUND')}")
print(f"Email: {result.get('email', 'NOT_FOUND')}")
print(f"Phone: {result.get('phone', 'NOT_FOUND')}")
# Check sentinel values
print(f"MISSING is UNSET: {MISSING is UNSET}") # True - they're aliases
print(f"MISSING == PydanticUndefined: {MISSING == PydanticUndefined}") # False
# Type checking
print(f"Type of PydanticUndefined: {type(PydanticUndefined)}")
print(f"isinstance(PydanticUndefined, PydanticUndefinedType): {isinstance(PydanticUndefined, type(PydanticUndefined))}")from pydantic_core import SchemaValidator, Some
from pydantic_core.core_schema import (
with_info_plain_validator_function,
union_schema,
none_schema,
str_schema
)
def validate_optional_with_some(value):
"""Validator that distinguishes None from missing."""
if isinstance(value, Some):
# Extract the wrapped value, even if it's None
return value.value
elif value is None:
# Explicit None
return None
else:
# Regular value
if not isinstance(value, str):
raise ValueError("Expected string or Some(value)")
return value
# Schema using Some
optional_schema = with_info_plain_validator_function(validate_optional_with_some)
validator = SchemaValidator(optional_schema)
# Test different values
test_values = [
"regular_string",
None,
Some("wrapped_string"),
Some(None), # Explicit None wrapped in Some
]
for value in test_values:
try:
result = validator.validate_python(value)
print(f"Input: {value} -> Output: {result}")
except Exception as e:
print(f"Input: {value} -> Error: {e}")from pydantic_core import SchemaValidator, ArgsKwargs
from pydantic_core.core_schema import (
with_info_plain_validator_function,
arguments_schema,
str_schema,
int_schema
)
def validate_function_call(value):
"""Validate function arguments structure."""
if isinstance(value, ArgsKwargs):
args, kwargs = value.args, value.kwargs
elif isinstance(value, dict) and 'args' in value and 'kwargs' in value:
args, kwargs = value['args'], value['kwargs']
else:
raise ValueError("Expected ArgsKwargs or dict with 'args' and 'kwargs'")
# Validate arguments
if len(args) < 1:
raise ValueError("At least one positional argument required")
if not isinstance(args[0], str):
raise ValueError("First argument must be a string")
# Return validated ArgsKwargs
return ArgsKwargs(args, kwargs)
# Create validator
func_validator = SchemaValidator(
with_info_plain_validator_function(validate_function_call)
)
# Test function call validation
test_calls = [
ArgsKwargs(("hello",), {"count": 3}),
{"args": ("world",), "kwargs": {"repeat": True}},
ArgsKwargs((), {"name": "test"}), # Invalid: no args
]
for call in test_calls:
try:
result = func_validator.validate_python(call)
print(f"Valid call: {result}")
print(f" Args: {result.args}")
print(f" Kwargs: {result.kwargs}")
except Exception as e:
print(f"Invalid call: {e}")from pydantic_core import (
SchemaValidator,
SchemaSerializer,
PydanticOmit,
PydanticUseDefault
)
from pydantic_core.core_schema import (
dict_schema,
str_schema,
int_schema,
with_info_plain_validator_function,
default_schema
)
def process_field_value(value):
"""Validator that may omit fields or use defaults."""
if isinstance(value, str):
if value == "OMIT":
return PydanticOmit # This field will be excluded
elif value == "DEFAULT":
return PydanticUseDefault # Use the field's default value
elif value.startswith("PROCESS:"):
return value[8:] # Remove "PROCESS:" prefix
return value
# Schema with conditional processing
schema = dict_schema({
'id': int_schema(),
'name': with_info_plain_validator_function(process_field_value),
'description': default_schema(
with_info_plain_validator_function(process_field_value),
default="Default description"
),
'status': with_info_plain_validator_function(process_field_value)
})
validator = SchemaValidator(schema)
serializer = SchemaSerializer(schema)
# Test data with markers
test_data = {
'id': 123,
'name': 'PROCESS:Alice', # Will become 'Alice'
'description': 'DEFAULT', # Will use default value
'status': 'OMIT' # Will be omitted from output
}
# Validate
validated = validator.validate_python(test_data)
print(f"Validated: {validated}")
# Serialize - status should be omitted
serialized = serializer.to_python(validated)
print(f"Serialized: {serialized}")
# Check what fields are present
print(f"Fields in result: {list(serialized.keys())}")
print(f"Status omitted: {'status' not in serialized}")from pydantic_core import SchemaValidator, SchemaSerializer, MISSING
from pydantic_core.core_schema import (
model_schema,
str_schema,
int_schema,
default_schema,
nullable_schema
)
# User model with various optional field types
user_schema = model_schema(
'User',
{
'id': int_schema(),
'name': str_schema(),
'email': nullable_schema(str_schema()), # Can be None
'phone': default_schema(str_schema(), default=MISSING), # Missing by default
'address': default_schema(nullable_schema(str_schema()), default=None), # None by default
}
)
validator = SchemaValidator(user_schema)
serializer = SchemaSerializer(user_schema)
# Test different scenarios
test_users = [
# Minimal user
{'id': 1, 'name': 'Alice', 'email': None},
# User with phone
{'id': 2, 'name': 'Bob', 'email': 'bob@example.com', 'phone': '123-456-7890'},
# User with all fields
{'id': 3, 'name': 'Charlie', 'email': 'charlie@example.com', 'phone': '098-765-4321', 'address': '123 Main St'},
]
for user_data in test_users:
print(f"\nInput: {user_data}")
# Validate
validated = validator.validate_python(user_data)
print(f"Validated: {validated}")
# Serialize excluding unset fields
serialized_exclude_unset = serializer.to_python(validated, exclude_unset=True)
print(f"Exclude unset: {serialized_exclude_unset}")
# Serialize excluding None values
serialized_exclude_none = serializer.to_python(validated, exclude_none=True)
print(f"Exclude None: {serialized_exclude_none}")from typing_extensions import Sentinel
# Create custom sentinel values for specific use cases
REDACTED = Sentinel('REDACTED')
COMPUTED = Sentinel('COMPUTED')
INHERITED = Sentinel('INHERITED')
def process_config_value(value):
"""Process configuration values with custom sentinels."""
if value is REDACTED:
return "[REDACTED]"
elif value is COMPUTED:
return f"computed_value_{hash('config')}"
elif value is INHERITED:
return "inherited_from_parent"
else:
return value
# Example usage in configuration processing
config_data = {
'api_key': REDACTED,
'cache_size': COMPUTED,
'log_level': INHERITED,
'service_name': 'my-service'
}
processed_config = {
key: process_config_value(value)
for key, value in config_data.items()
}
print("Original config:")
for key, value in config_data.items():
print(f" {key}: {value}")
print("\nProcessed config:")
for key, value in processed_config.items():
print(f" {key}: {value}")
# Sentinel comparison
print(f"\nSentinel comparisons:")
print(f"REDACTED is REDACTED: {REDACTED is REDACTED}")
print(f"REDACTED == REDACTED: {REDACTED == REDACTED}")
print(f"REDACTED is COMPUTED: {REDACTED is COMPUTED}")Install with Tessl CLI
npx tessl i tessl/pypi-pydantic-core