Pydantic Tagged Union Pattern

Demonstrates discriminated unions for type-safe configuration using Pydantic’s discriminator field

When you need to handle multiple connection types or configuration variants, use Pydantic tagged unions with a discriminator field. This pattern enables type-safe deserialization where the type field determines which specific subclass to instantiate, providing compile-time type checking and runtime validation for polymorphic configurations.

pydantic_tagged_union_pattern.py
 1# -*- coding: utf-8 -*-
 2
 3"""
 4In Python, we can use Pydantic to define a tagged union
 5(also known as a discriminated union) using the `discriminator` field.
 6This allows us to create a union type that can be used to represent
 7different subtypes of a base type, where each subtype has its own specific fields.
 8"""
 9
10import typing as T
11from pydantic import BaseModel, Field
12
13
14class BaseConnection(BaseModel):
15    type: str = Field()
16
17
18class SqlalchemyConnection(BaseConnection):
19    type: T.Literal["sqlalchemy"] = Field(default="sqlalchemy")
20    create_engine_kwargs: dict[str, T.Any] = Field(default_factory=dict)
21
22
23class AwsConnection(BaseConnection):
24    type: T.Literal["aws"] = Field(default="aws")
25    boto_session_kwargs: dict[str, T.Any] = Field(default_factory=dict)
26
27
28class Config(BaseModel):
29    connection: T.Union[SqlalchemyConnection, AwsConnection] = Field(
30        discriminator="type"
31    )
32
33
34if __name__ == "__main__":
35    dct_sql = {"connection": {"type": "sqlalchemy", "create_engine_kwargs": {}}}
36    dct_aws = {"connection": {"type": "aws", "boto_session_kwargs": {}}}
37
38    config = Config(**dct_sql)
39    # config = Config(**dct_aws)
40
41    print(config)