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

TypeError: object is not a dataclass and cannot be turned into one. #208

Closed jacquilevitan closed 2 years ago

jacquilevitan commented 2 years ago

Hi,

I'm trying to use a DEFAULT_NONE sentinel value that's set to an object so I can differentiate between values that aren't passed in and that are passed in as None. However, I get this TypeError: File "/Users/jacquilevitan/Library/Caches/pypoetry/virtualenvs/transactions-svc-EUl0Pv-b-py3.8/lib/python3.8/site-packages/marshmallow_dataclass/__init__.py", line 387, in _internal_class_schema raise TypeError( TypeError: object is not a dataclass and cannot be turned into one.

My Schemas:

@dataclass
class ItemDataClass:
    name: str
    price: int
    quantity: int
    sku: Optional[Union[str, object]] = field(default=DEFAULT_NONE, metadata={"allow_none": True})
    image_url: Optional[Union[str, object]] = field(
        default=DEFAULT_NONE, metadata={"allow_none": True}
    )
    currency: Optional[Union[str, object]] = field(
        default=DEFAULT_NONE, metadata={"allow_none": True}
    )

    def as_dict(self) -> Dict:
        item_dict = {}
        for k, v in self.__dict__.items():
            if v is DEFAULT_NONE:
                continue
            item_dict[k] = v
        return item_dict

@dataclass
class CreateCheckoutRequestBody:
    merchant_public_key: str
    external_checkout_id: str
    order_total: int
    subtotal: int
    tax_amount: int
    shipping_amount: int
    items: List[ItemDataClass]

and then i parse the schema here: @dataclass class CreateCheckoutRequestBody: merchant_public_key: str external_checkout_id: str order_total: int subtotal: int tax_amount: int shipping_amount: int items: List[ItemDataClass]

Any idea how to fix this?

dairiki commented 2 years ago

I think the issue is with your sku, image_url, and currency fields. Those have of type Union[None, str, object]. Marshmallow_dataclass has no idea how to create a marshmallow field (which is what defines how to de/serialize the value) for objects of type object.

A couple ways around...

You could provide an explicit marshmallow field for those fields, e.g.

from dataclasses import dataclass, field

import marshmallow.fields

DEFAULT_NONE = object()

@dataclass
class ItemDataClass:
    sku: Optional[Union[str, object]] = field(
        default=DEFAULT_NONE,
        metadata={"marshmallow_field": marshmallow.fields.String(allow_none=True)},
    )

or you could use a sentinel of a type that marshmallow_dataclass knows how to deal with:

from dataclasses import dataclass, field
from enum import Enum

import marshmallow.fields

class Sentinel(Enum):
    DEFAULT_NONE = None  # or object()...

@dataclass
class ItemDataClass:
    sku: Optional[Union[str, Sentinel]] = Sentinel.DEFAULT_NONE
dairiki commented 2 years ago

Not a bug, I think. Closing.