Skip to main content

Literals, Enums, and Callables

This guide explains how to work with fixed sets of values and functional validation using LiteralSchema, EnumSchema, and CallableSchema in the pydantic-core framework. These schemas allow you to define strict constraints on allowed values and ensure that inputs are executable objects.

Literal Validation

The LiteralSchema is used to validate that an input matches one of a specific set of allowed values. It is defined in pydantic_core/core_schema.py and is typically created using the literal_schema helper function.

Core Implementation

A LiteralSchema requires an expected list containing the allowed values. During validation, pydantic-core checks if the input is exactly equal to one of these values.

from pydantic_core import SchemaValidator, core_schema

# Define a schema for specific string values
schema = core_schema.literal_schema(['hello', 'world'])
v = SchemaValidator(schema)

assert v.validate_python('hello') == 'hello'
# v.validate_python('foo') # Raises ValidationError

Behavior with Enums

When using Python Enum members within a LiteralSchema, the behavior depends on whether the Enum is a "plain" Enum or a mixin like IntEnum or StrEnum:

  1. Plain Enums: Validation requires the actual Enum member. Passing the raw value (e.g., the string 'foo_value') will fail unless the input is the Enum member itself.
  2. Mixin Enums (StrEnum/IntEnum): Validation allows both the Enum member and the raw value, as these Enums compare equal to their underlying types.
from enum import Enum
from pydantic_core import SchemaValidator, core_schema

class Color(Enum):
RED = 'red'

# Plain Enum requires the member
schema = core_schema.literal_schema([Color.RED])
v = SchemaValidator(schema)

assert v.validate_python(Color.RED) == Color.RED
# v.validate_python('red') # Raises ValidationError: Input should be <Color.RED: 'red'>

Enum Validation

The EnumSchema provides a more robust way to validate Python Enum classes. Unlike LiteralSchema, which just checks for equality against a list, EnumSchema is designed to return instances of a specific Enum class.

Schema Structure

The EnumSchema requires the Enum class (cls) and its members. It also supports a sub_type to enable coercion from raw values.

from enum import Enum
from pydantic_core import SchemaValidator, core_schema

class Status(int, Enum):
ACTIVE = 1
INACTIVE = 2

# Create an Enum schema with integer coercion
schema = core_schema.enum_schema(
Status,
list(Status.__members__.values()),
sub_type='int'
)
v = SchemaValidator(schema)

# Coercion from various types
assert v.validate_python(1) is Status.ACTIVE
assert v.validate_python('2') is Status.INACTIVE
assert v.validate_json('1') is Status.ACTIVE

Strict vs. Lax Mode

The EnumSchema behaves differently based on the strict setting:

  • Lax Mode (default): Allows validation from raw values (like 1 or '1') and performs coercion if a sub_type is provided.
  • Strict Mode: In Python validation, it requires an actual instance of the Enum class. However, in JSON validation, it still allows raw values because JSON has no native Enum type.

Custom Lookup with missing

You can provide a missing callable to the EnumSchema. This function is called when a value is not found in the members list, allowing for custom fallback logic or dynamic member resolution (similar to the _missing_ method in Python Enums).

Callable Validation

The CallableSchema ensures that an input is a Python callable object. This includes functions, classes, and instances of classes that implement the __call__ method.

Usage

The callable_schema helper creates a simple validator that performs a check equivalent to Python's built-in callable() function.

from pydantic_core import SchemaValidator, core_schema

def my_function():
return 42

class MyClass:
def __call__(self):
return 42

schema = core_schema.callable_schema()
v = SchemaValidator(schema)

assert v.validate_python(my_function) == my_function
assert v.validate_python(list) == list # Classes are callable
assert v.validate_python(MyClass()) == MyClass() # Instance with __call__
# v.validate_python(42) # Raises ValidationError: Input should be callable

Implementation Details and Constraints

  • Minimum Elements: Both LiteralSchema and EnumSchema require at least one element in expected or members. Attempting to create a validator with an empty list will raise a SchemaError (e.g., SchemaError: expected should have length > 0).
  • Discriminated Unions: LiteralSchema is frequently used as a "tag" within UnionSchema to implement discriminated unions. This allows pydantic-core to quickly identify which branch of a union to validate against based on a specific field's literal value.
  • Metadata and Serialization: All three schemas support optional metadata (for storing extra information) and serialization (for customizing how these values are converted back to JSON or Python objects).