pydantic / bump-pydantic

Convert Pydantic from V1 to V2 ♻
MIT License
303 stars 24 forks source link
codemod pydantic python

Bump Pydantic ♻️

PyPI - Version PyPI - Python Version

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.

Table of contents


Installation

The installation is as simple as:

pip install bump-pydantic

Usage

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

Check diff before applying changes

To check the diff before applying the changes, you can run:

bump-pydantic --diff <path>

Apply changes

To apply the changes, you can run:

bump-pydantic <path>

Rules

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.

BP001: Add default None to Optional[T], Union[T, None] and Any fields

The following code will be transformed:

class User(BaseModel):
    name: Optional[str]

Into:

class User(BaseModel):
    name: Optional[str] = None

BP002: Replace Config class by model_config attribute

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")

BP003: Replace Field old parameters to new ones

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)

BP004: Replace imports

BP005: Replace GenericModel by BaseModel

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

BP006: Replace __root__ by RootModel

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

BP007: Replace decorators

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

BP008: Replace con* functions by Annotated versions

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)]

BP009: Mark Pydantic "protocol" functions in custom types with proper TODOs

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"

BP010: Add type annotations or TODO comments to fields without them

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")

License

This project is licensed under the terms of the MIT license.