diff --git a/bump_pydantic/codemods/field.py b/bump_pydantic/codemods/field.py index 54c5439..e6d647f 100644 --- a/bump_pydantic/codemods/field.py +++ b/bump_pydantic/codemods/field.py @@ -100,7 +100,15 @@ def leave_field_call(self, original_node: cst.Call, updated_node: cst.Call) -> c for arg in updated_node.args: if m.matches(arg, m.Arg(keyword=m.Name())): keyword = RENAMED_KEYWORDS.get(arg.keyword.value, arg.keyword.value) # type: ignore - new_args.append(arg.with_changes(keyword=arg.keyword.with_changes(value=keyword))) # type: ignore + value = arg.value + # The `allow_mutation` keyword argument is a special case. It's the negative of `frozen`. + if arg.keyword and arg.keyword.value == "allow_mutation": + if m.matches(arg.value, m.Name(value="False")): + value = cst.Name("True") + elif m.matches(arg.value, m.Name(value="True")): + value = cst.Name("False") + new_arg = arg.with_changes(keyword=arg.keyword.with_changes(value=keyword), value=value) # type: ignore + new_args.append(new_arg) # type: ignore else: new_args.append(arg) diff --git a/tests/unit/test_field.py b/tests/unit/test_field.py index be7a8ea..62e02b5 100644 --- a/tests/unit/test_field.py +++ b/tests/unit/test_field.py @@ -102,3 +102,20 @@ class Potato(BaseModel): potato: Literal[MyEnum.POTATO] = MyEnum.POTATO """ self.assertCodemod(before, after) + + def test_flip_frozen_boolean(self) -> None: + before = """ + from pydantic import BaseSettings, Field + + class Settings(BaseSettings): + potato: int = Field(..., allow_mutation=False) + strawberry: int = Field(..., allow_mutation=True) + """ + after = """ + from pydantic import BaseSettings, Field + + class Settings(BaseSettings): + potato: int = Field(..., frozen=True) + strawberry: int = Field(..., frozen=False) + """ + self.assertCodemod(before, after)