I upgraded a large private codebase to Pydantic 2, and in the process I made a bunch of improvements to bump-pydantic. See this branch, and the list of commits.
Here is a list of issues this branch addresses (possibly slightly incomplete, it's lightly edited from a personal log):
[x] parse_raw is deprecated and should be replaced with model_validate_json
[x] Pydantic 2 wants TypedDict to be imported from typing_extensions if using Python < 3.12, because it needs __orig_bases__ which 3.12 introduced
[x] The validator migration does not recognize pydantic.validator etc.
[x] Same for the Field migration
[x] pydantic.Extra.forbid should be replaced with "forbid"
[x] skip_on_failure=True should be removed from root_validator
[x] Batch Pyre requests for speed
[x] Should replace json.loads(x.model_dump_json()) with x.model_dump(mode="json").
[x] parse_raw_as has been removed entirely. Use TypeAdapter.
[x] parse_obj_as is not fully removed but should be replaced with TypeAdapter too.
[x] In root_validators (→ model_validators) with mode="before", the second argument may be an instance of the actual model instead of a dict, in which case you should probably just return it. See example in docs. In fact it could be anything since you can pass anything to model_validate ! Add a TODO comment when migrating
[x] model_validator(mode="after") needs to be an instance method, not a class method
[x] For debugging, need a way to run bump-pydantic on a single file while still importing the class hierarchy from a full repo
[x] Add a TODO/warning for BaseModel subclasses that override deprecated method like dict or json
[x] If you have an Optional, in model.dict() if model else {} Pyre infers the first model as Optional even though None should be excluded there. work around.
[x] If you have a field starting with model_, e.g. model_name, Pydantic 2 will complain. You need to add protected_namespaces=() to the ConfigDict
[x] model_config cannot be used as a field name at all
[x] we need to add a model_config if the original class had no Config
[x] Sometimes it generates ConfigDict with two extra args
[x] It does not handle __root__ with a type annotation
[x] Some migrations break if you have nested BaseModel classes due to incorrect tracking logic.
Most of these changes should be generally useful: they migrate things that were not migrated, or they fix bugs in existing migrations. A few changes are needed to enable running bump_pydantic on a large repository. A couple of things depend on improvements to LibCST which I am also upstreaming.
I sent three of these as separate PRs about two months ago, but there has been no activity. Let's find a way to coordinate on how to get these changes upstreamed. I won't need this code again, but I'd like others to be able to benefit from it.
I upgraded a large private codebase to Pydantic 2, and in the process I made a bunch of improvements to bump-pydantic. See this branch, and the list of commits.
Here is a list of issues this branch addresses (possibly slightly incomplete, it's lightly edited from a personal log):
allow_mutation
in Config should be converted tofrozen
https://github.com/pydantic/bump-pydantic/pull/161json_encoders
is allowed again, no need for comment https://github.com/pydantic/bump-pydantic/pull/162values
param in validatorsclassmethod
can be duplicated https://github.com/pydantic/bump-pydantic/pull/160validator(always=True)
should be converted tovalidate_default=True
on Fieldtest_class_def_visitor.py
is commented out.git
ClassDefVisitor
Config
class is a pydantic config. It should check that the parent is actually a model.root_validator
gets turned into amodel_validator
without arguments, which is invalid (needsmode
).root_validator
, now also need to handleroot_validator()
smart_union
is now the default. It should be automatically removed.underscore_attrs_are_private
is also now the default and should be removed.BaseModel.json
is deprecated, should usemodel_dump_json
.parse_obj
also deprecated. usemodel_validate
construct()
,copy()
,dict()
,json_schema()
,json()
,parse_obj()
,update_forward_refs()
__fields__
,__private_attributes__
,__validators__
parse_raw
is deprecated and should be replaced withmodel_validate_json
typing_extensions
if using Python < 3.12, because it needs__orig_bases__
which 3.12 introducedpydantic.validator
etc.pydantic.Extra.forbid
should be replaced with"forbid"
skip_on_failure=True
should be removed from root_validatorjson.loads(x.model_dump_json())
withx.model_dump(mode="json")
.parse_raw_as
has been removed entirely. Use TypeAdapter.parse_obj_as
is not fully removed but should be replaced with TypeAdapter too.root_validator
s (→model_validator
s) withmode="before"
, the second argument may be an instance of the actual model instead of a dict, in which case you should probably just return it. See example in docs. In fact it could be anything since you can pass anything tomodel_validate
! Add a TODO comment when migratingmodel_validator(mode="after")
needs to be an instance method, not a class methoddict
orjson
model.dict() if model else {}
Pyre infers the first model as Optional even though None should be excluded there. work around.model_
, e.g.model_name
, Pydantic 2 will complain. You need to addprotected_namespaces=()
to the ConfigDictmodel_config
cannot be used as a field name at allConfigDict
with twoextra
args__root__
with a type annotationMost of these changes should be generally useful: they migrate things that were not migrated, or they fix bugs in existing migrations. A few changes are needed to enable running bump_pydantic on a large repository. A couple of things depend on improvements to LibCST which I am also upstreaming.
I sent three of these as separate PRs about two months ago, but there has been no activity. Let's find a way to coordinate on how to get these changes upstreamed. I won't need this code again, but I'd like others to be able to benefit from it.