Pydantic Compat
pydantic-compat is a compatibility layer designed to help developers write code that works seamlessly with both Pydantic v1 and v2. It provides common interfaces, aliases, and wrappers for key Pydantic components, allowing libraries and applications to support different Pydantic major versions without extensive conditional logic. The current version is 0.1.2, and it follows an as-needed release cadence to address compatibility needs.
Common errors
-
AttributeError: module 'pydantic' has no attribute 'model_validator'
cause Attempting to use Pydantic v2's `@model_validator` decorator with Pydantic v1 installed, without using `pydantic-compat`.fixImport `@model_validator` from `pydantic_compat` (i.e., `from pydantic_compat import model_validator`) and ensure your base model is `pydantic_compat.CompatModel`. -
TypeError: 'BaseModel' object is not subscriptable
cause This often occurs when trying to access v2-specific attributes (e.g., `model_fields`, `model_json_schema`) on a Pydantic v1 `BaseModel`, or when misusing version-specific methods.fixEnsure you are using `pydantic_compat.CompatModel` and accessing attributes/methods that are provided or wrapped by `pydantic-compat` for cross-version compatibility. For direct Pydantic version-specific features, use conditional logic based on `pydantic.VERSION`. -
TypeError: BaseModel.__init__() got an unexpected keyword argument 'extra'
cause Pydantic v1 and v2 handle model configuration (like `extra='ignore'`) differently, especially in how `extra` fields are configured. This error often arises from a mismatch between the Pydantic version installed and the configuration syntax used.fixUse `pydantic_compat.ConfigDict` for model configuration. For example, `model_config = ConfigDict(extra='ignore')`. This will adapt the configuration method to the installed Pydantic version.
Warnings
- gotcha pydantic-compat provides *syntactic* compatibility for common patterns, not full feature translation. It does not automatically enable Pydantic v2-only features when Pydantic v1 is installed, or vice-versa. Its primary goal is to allow a single codebase to define models and validators using a common API.
- gotcha Mixing `pydantic.BaseModel` and `pydantic_compat.CompatModel` in the same codebase (especially for inheritance) can lead to unexpected behavior or `AttributeError` if the underlying Pydantic version doesn't support the syntax used with `pydantic.BaseModel`.
- gotcha Pydantic v1 uses `class Config:` nested classes, while v2 uses `model_config = {...}` or `ConfigDict`. While `pydantic-compat` provides `ConfigDict`, developers need to be mindful of which configuration options are truly available and behave identically across versions.
Install
-
pip install pydantic-compat
Imports
- CompatModel
from pydantic import BaseModel
from pydantic_compat import CompatModel
- model_validator
from pydantic import model_validator
from pydantic_compat import model_validator
- FieldValidationInfo
from pydantic_compat import FieldValidationInfo
- ConfigDict
from pydantic_compat import ConfigDict
Quickstart
from pydantic_compat import CompatModel, model_validator, FieldValidationInfo
from typing import ClassVar
import pydantic # To check version directly if needed
class MyConfig(CompatModel):
my_field: str
version_info: ClassVar[str] = ""
@model_validator(mode='after')
def check_version(self, info: FieldValidationInfo):
# Access original pydantic version for conditional logic
if pydantic.VERSION.startswith('1.'):
self.version_info = "Pydantic v1 compatible"
else:
self.version_info = "Pydantic v2 compatible"
return self
# This will adapt based on the installed Pydantic version
config_instance = MyConfig(my_field="hello world")
print(f"MyField: {config_instance.my_field}")
print(f"Compatibility Info: {config_instance.version_info}")
# Example with ConfigDict (for Pydantic v2 style config)
from pydantic_compat import ConfigDict
class MyModelWithConfig(CompatModel):
model_config = ConfigDict(extra='ignore', frozen=True)
value: int
model_instance = MyModelWithConfig(value=123, unknown_field="ignored")
print(f"Model Configured Value: {model_instance.value}")