lovasoa / marshmallow_dataclass

Automatic generation of marshmallow schemas from dataclasses.
https://lovasoa.github.io/marshmallow_dataclass/html/marshmallow_dataclass.html
MIT License
458 stars 78 forks source link

why is type checking not supported for marshmallow_dataclass in PyCharm? #178

Open sarimmehdi opened 2 years ago

sarimmehdi commented 2 years ago

I was using the normal dataclass before and type checking worked fine inside PyCharm for that but that doesn't seem to be the case when I use the @marshmallow_dataclass.dataclass annotation over my class instead of the usual @dataclasses.dataclass one. Is there a special reason for it being this way? Seeing my dataclass declaration become entirely yellow in PyCharm (an incorrect warning) is very annoying

sarimmehdi commented 2 years ago

So, as a workaround, I found this. It is one extra annotation that gets rid of the annoying incorrect warnings but if someone here can explain to me why just using the @marshmallow_dataclass.dataclass doesn't give the same behavior, then that'd be great. Thanks!

sarimmehdi commented 2 years ago

It seems the @add_schema annotation gets rid of all types of PyCharm warnings entirely. If anyone has a workaround to this issue then that'd be great.

mdesjardins commented 2 years ago

FYI - it's not just a PyCharm thing. My code is currently not passing mypy at all.

Here's a super basic example that hopefully (it's a scaled down version of production code) reproduces it:

class BaseSchema(Schema):
    """All schema derive from this to automatically handle camelcasing fields and other goodies."""
    def on_bind_field(self, field_name: str, field_obj: Any) -> None:
        field_obj.data_key = camelcase(field_obj.data_key or field_name)

@dataclass(base_schema=BaseSchema)
class RecipientForm:
    label: str
    money_type: MoneyType = field(metadata=dict(by_value=True))
    payout_method: PayoutMethod = field(metadata=dict(by_value=True))
    enabled: bool = True

DEFAULT_MOBILE_RECIPIENT_FORM = RecipientForm(
    label="default-new-mobile-recipient",
    money_type=MoneyType.MOBILE,
    payout_method=PayoutMethod.MOBILE,
)

the above code is currently giving me a mypy error I decorate RecipientForm with @marshmallow_dataclass.dataclass:

/Users/mdesjardins/_work/recipient_forms.py:97: error: Unexpected keyword argument "label" for "RecipientForm"
/Users/mdesjardins/_work/recipient_forms.py:97: error: Unexpected keyword argument "money_type" for "RecipientForm"
/Users/mdesjardins/_work/recipient_forms.py:97: error: Unexpected keyword argument "payout_method" for "RecipientForm"

I don't get this mypy error when I decorate with @dataclasses.dataclass, but then I run into problems because my classes no longer use BaseSchema as their parent schema.

Aside: My real-world scenario is a bit gnarlier than the contrived example above - i.e., I realize that for this example, I could just create my Schema class with

RecipientFormResponseSchema = marshmallow_dataclass.class_schema(RecipientForm, BaseSchema)

_and forgo the mashmallow_dataclass version of the dataclass decorator and be fine. The problem that I'm actually having is that, for my real world code, my outer RecipientForm dataclass has an inner list of FormField objects that are also dataclasses that I'm serializing... once I get into dataclasses containing lists of dataclasses, the alternative above doesn't really work, because those inner dataclasses also need to use BaseSchema as their base._

mdesjardins commented 2 years ago

Here's a better example showing the problem: https://gist.github.com/mdesjardins/2d2eb37bad865b8037800d0bbb07a18a

lucatpa commented 2 years ago

Related to this, if you try to construct a class with a marshmallow.dataclass decorator instead of dataclasses.dataclass, the type checking for the constructor's arguments fail saying the constructor doesn't take any arguments, but using the constructor as if it were a normal dataclass works perfectly, so the type hinting is failing. Should I create a separate issue for this?