Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need rules for each_item and always #70

Open
cclauss opened this issue Jul 10, 2023 · 8 comments
Open

Need rules for each_item and always #70

cclauss opened this issue Jul 10, 2023 · 8 comments

Comments

@cclauss
Copy link
Contributor

cclauss commented Jul 10, 2023

elif m.matches(arg.keyword, m.Name(value=m.MatchIfTrue(lambda v: v in ("each_item", "always")))):
self._should_add_comment = True

https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators

@Kludex
Copy link
Member

Kludex commented Jul 10, 2023

What are you proposing here?

@cclauss
Copy link
Contributor Author

cclauss commented Jul 10, 2023

Rules

BP0xy: Replace each_item

  • ✅ Replace each_item by something better.

The following code will be transformed:

from pydantic import BaseModel, validator


class User(BaseModel):
    first_name: str
    last_name: str

    @validator("first_name", "last_name", each_item=True)
    def names_must_not_be_empty_strings(cls, v):
        if v:
            return v
        raise ValueError("Empty strings are not permitted")

Into:

from pydantic import BaseModel, field_validator


class User(BaseModel):
    first_name: str
    last_name: str

    @field_validator("first_name", "last_name",  # I am not sure what comes next...

@cclauss cclauss changed the title Need fixers for each_item and always Need rules for each_item and always Jul 10, 2023
@Viicos
Copy link
Contributor

Viicos commented Jul 10, 2023

I'm using a BeforeValidator to implement each_item in v2:

def validator_for_each_list_item(item: str):
   ...

class A(BaseModel):
    my_list: List[Annotated[str, BeforeValidator(validator_for_each_list_item)]]

Not sure if this is doable with a codemod

@Kludex
Copy link
Member

Kludex commented Jul 11, 2023

Well... It's feasible.

I guess what would be faster is to teach how to do it via comment.

@cclauss
Copy link
Contributor Author

cclauss commented Jul 12, 2023

teach how to do it via comment.

Given the source above #70 (comment), what should the expected be?

@Kludex
Copy link
Member

Kludex commented Jul 12, 2023

teach how to do it via comment.

Given the source above #70 (comment), what should the expected be?

Were last_name and first_name supposed to be str, and not List[str]?

@cclauss
Copy link
Contributor Author

cclauss commented Jul 12, 2023

Let's do List[str] so last_names and first_names (plural).

@Kludex
Copy link
Member

Kludex commented Jul 12, 2023

Then...

If validator method has 2 params (parameter names doesn't matter, but numbers do):

from pydantic import BaseModel, validator


class User(BaseModel):
    first_names: List[str]
    last_names: List[str]

    @validator("first_name", "last_name", each_item=True)
    def names_must_not_be_empty_strings(cls, v):
        if v:
            return v
        raise ValueError("Empty strings are not permitted")

Then we'd get:

from pydantic import BaseModel, validator, BeforeValidator
from typing_extensions import Annotated
from typing import List


# This function goes to the outer scope.
# I think we can get the `ClassDef` parent node with `LibCST.ParentProvider`. But I'm not sure...
def names_must_not_be_empty_strings(v):
    if v:
        return v
    raise ValueError("Empty strings are not permitted")


class User(BaseModel):
    # Check if the `AnnAssign.annotation` (List in this case) is a `Subscriptable` - and if it's, apply `BeforeValidator`.  
    first_names: List[Annotated[str, BeforeValidator(names_must_not_be_empty_strings)]]
    last_names: List[Annotated[str, BeforeValidator(names_must_not_be_empty_strings)]]

Remember to import typing_extensions.Annotated, and BeforeValidator, and remove validator.

Also, you might want to check function name collision on the module level - but I'm not sure if this is needed - but an xfail would be nice.

Not finished yet... If the number of params is more than 2, then add a note on top of the method:

# TODO: We couldn't replace the validator automatically.
# Please check https://docs.pydantic.dev/latest/migration/#validator-and-root_validator-are-deprecated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants