Bump Pydantic is a tool to help you migrate your code from Pydantic V1 to V2.
Note
If you find bugs, please report them on the issue tracker.
- Bump Pydantic ♻️
-
- BP001: Add default
None
toOptional[T]
,Union[T, None]
andAny
fields - BP002: Replace
Config
class bymodel_config
attribute - BP003: Replace
Field
old parameters to new ones - BP004: Replace imports
- BP005: Replace
GenericModel
byBaseModel
- BP006: Replace
__root__
byRootModel
- BP007: Replace decorators
- BP008: Replace
con*
functions byAnnotated
versions - BP009: Mark pydantic "protocol" functions in custom types with proper TODOs
- BP001: Add default
The installation is as simple as:
pip install bump-pydantic
bump-pydantic
is a CLI tool, hence you can use it from your terminal.
It's easy to use. If your project structure is:
repository/
└── my_package/
└── <python source files>
Then you'll want to do:
cd /path/to/repository
bump-pydantic my_package
To check the diff before applying the changes, you can run:
bump-pydantic --diff <path>
To apply the changes, you can run:
bump-pydantic <path>
You can find below the list of rules that are applied by bump-pydantic
.
It's also possible to disable rules by using the --disable
option.
- ✅ Add default
None
toOptional[T]
fields.
The following code will be transformed:
class User(BaseModel):
name: Optional[str]
Into:
class User(BaseModel):
name: Optional[str] = None
- ✅ Replace
Config
class bymodel_config = ConfigDict()
. - ✅ Rename old
Config
attributes to newmodel_config
attributes. - ✅ Add a TODO comment in case the transformation can't be done automatically.
- ✅ Replace
Extra
enum by string values.
The following code will be transformed:
from pydantic import BaseModel, Extra
class User(BaseModel):
name: str
class Config:
extra = Extra.forbid
Into:
from pydantic import ConfigDict, BaseModel
class User(BaseModel):
name: str
model_config = ConfigDict(extra="forbid")
- ✅ Replace
Field
old parameters to new ones. - ✅ Replace
field: Enum = Field(Enum.VALUE, const=True)
byfield: Literal[Enum.VALUE] = Enum.VALUE
.
The following code will be transformed:
from typing import List
from pydantic import BaseModel, Field
class User(BaseModel):
name: List[str] = Field(..., min_items=1)
Into:
from typing import List
from pydantic import BaseModel, Field
class User(BaseModel):
name: List[str] = Field(..., min_length=1)
- ✅ Replace
BaseSettings
frompydantic
topydantic_settings
. - ✅ Replace
Color
andPaymentCardNumber
frompydantic
topydantic_extra_types
.
- ✅ Replace
GenericModel
byBaseModel
.
The following code will be transformed:
from typing import Generic, TypeVar
from pydantic.generics import GenericModel
T = TypeVar('T')
class User(GenericModel, Generic[T]):
name: str
Into:
from typing import Generic, TypeVar
from pydantic import BaseModel
T = TypeVar('T')
class User(BaseModel, Generic[T]):
name: str
- ✅ Replace
__root__
byRootModel
.
The following code will be transformed:
from typing import List
from pydantic import BaseModel
class User(BaseModel):
age: int
name: str
class Users(BaseModel):
__root__ = List[User]
Into:
from typing import List
from pydantic import RootModel, BaseModel
class User(BaseModel):
age: int
name: str
class Users(RootModel[List[User]]):
pass
- ✅ Replace
@validator
by@field_validator
. - ✅ Replace
@root_validator
by@model_validator
.
The following code will be transformed:
from pydantic import BaseModel, validator, root_validator
class User(BaseModel):
name: str
@validator('name', pre=True)
def validate_name(cls, v):
return v
@root_validator(pre=True)
def validate_root(cls, values):
return values
Into:
from pydantic import BaseModel, field_validator, model_validator
class User(BaseModel):
name: str
@field_validator('name', mode='before')
def validate_name(cls, v):
return v
@model_validator(mode='before')
def validate_root(cls, values):
return values
- ✅ Replace
constr(*args)
byAnnotated[str, StringConstraints(*args)]
. - ✅ Replace
conint(*args)
byAnnotated[int, Field(*args)]
. - ✅ Replace
confloat(*args)
byAnnotated[float, Field(*args)]
. - ✅ Replace
conbytes(*args)
byAnnotated[bytes, Field(*args)]
. - ✅ Replace
condecimal(*args)
byAnnotated[Decimal, Field(*args)]
. - ✅ Replace
conset(T, *args)
byAnnotated[Set[T], Field(*args)]
. - ✅ Replace
confrozenset(T, *args)
byAnnotated[Set[T], Field(*args)]
. - ✅ Replace
conlist(T, *args)
byAnnotated[List[T], Field(*args)]
.
The following code will be transformed:
from pydantic import BaseModel, constr
class User(BaseModel):
name: constr(min_length=1)
Into:
from pydantic import BaseModel, StringConstraints
from typing_extensions import Annotated
class User(BaseModel):
name: Annotated[str, StringConstraints(min_length=1)]
- ✅ Mark
__get_validators__
as to be replaced by__get_pydantic_core_schema__
. - ✅ Mark
__modify_schema__
as to be replaced by__get_pydantic_json_schema__
.
The following code will be transformed:
class SomeThing:
@classmethod
def __get_validators__(cls):
yield from []
@classmethod
def __modify_schema__(cls, field_schema, field):
if field:
field_schema['example'] = "Weird example"
Into:
class SomeThing:
@classmethod
# TODO[pydantic]: We couldn't refactor `__get_validators__`, please create the `__get_pydantic_core_schema__` manually.
# Check https://docs.pydantic.dev/latest/migration/#defining-custom-types for more information.
def __get_validators__(cls):
yield from []
@classmethod
# TODO[pydantic]: We couldn't refactor `__modify_schema__`, please create the `__get_pydantic_json_schema__` manually.
# Check https://docs.pydantic.dev/latest/migration/#defining-custom-types for more information.
def __modify_schema__(cls, field_schema, field):
if field:
field_schema['example'] = "Weird example"
- ✅ Add type annotations based on the default value for a few types that can be inferred, like
bool
,str
,int
,float
. - ✅ Add
# TODO[pydantic]: add type annotation
comments to fields that can't be inferred.
The following code will be transformed:
from pydantic import BaseModel, Field
class Potato(BaseModel):
name: str
is_sale = True
tags = ["tag1", "tag2"]
price = 10.5
description = "Some item"
active = Field(default=True)
ready = Field(True)
age = Field(10, title="Age")
Into:
from pydantic import BaseModel, Field
class Potato(BaseModel):
name: str
is_sale: bool = True
# TODO[pydantic]: add type annotation
tags = ["tag1", "tag2"]
price: float = 10.5
description: str = "Some item"
active: bool = Field(default=True)
ready: bool = Field(True)
age: int = Field(10, title="Age")
This project is licensed under the terms of the MIT license.