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

Errors when working with marshmallow_dataclass #1114

Closed
csghone opened this issue Mar 24, 2023 · 2 comments
Closed

Errors when working with marshmallow_dataclass #1114

csghone opened this issue Mar 24, 2023 · 2 comments

Comments

@csghone
Copy link

csghone commented Mar 24, 2023

I am able to use marshmallow_dataclass perfectly when using with dataclasses.dataclass

However, I want to use it with attrs module instead - use the converters/validators functionalities.
It works well most of the time. However, I am facing problems when I use marshmallow_dataclass + attrs with inheritance.

Sample code is given below.

  • Scenario1 : ENABLE_DC = True, ENABLE_ATTRS = False
    • Works as expected. Validator is inactive
  • Scenario2 : ENABLE_DC = True, ENABLE_ATTRS = True
    • Works as expected. Validator also works.
  • Scenario3 : ENABLE_DC = False, ENABLE_ATTRS = True
    • Crashes.
    • Fails in B = ClsB.Schema().load({'a': 123, 'b': 'bb', 'c': 4, 'd': "123"})
      • Error: marshmallow.exceptions.ValidationError: {'a': ['Unknown field.'], 'b': ['Unknown field.']}

Questions:

  • Is it possible to fix Scenario 3 ?
  • Scenario 2 seems to be ok for my requirements. But I am not sure what weird stuff might happen by enabling both dataclasses and attrs together. If you are aware of possible problems I might get into, please let me know.

I have raised this query with marshmallow_dataclass as well.
lovasoa/marshmallow_dataclass#236

I hope the first question is answered by marshmallow_dataclass team and second by the attrs team.

import dataclasses
import marshmallow
from marshmallow import Schema
import marshmallow_dataclass
from marshmallow_dataclass import class_schema
import attr
from typing import ClassVar, Type

ENABLE_DC    = True
ENABLE_ATTRS = True

# Conditional decorator
def conditionally(dec, cond):
    def resdec(f):
        if not cond:
            return f
        return dec(f)
    return resdec


@marshmallow_dataclass.add_schema
@conditionally(dataclasses.dataclass, ENABLE_DC)
@conditionally(attr.s(auto_attribs=True), ENABLE_ATTRS)
class ClsA:
    a: int
    b: str
    Schema : ClassVar[Type[Schema]] = Schema


@marshmallow_dataclass.add_schema
@conditionally(dataclasses.dataclass, ENABLE_DC)
@conditionally(attr.s(auto_attribs=True), ENABLE_ATTRS)
class ClsB(ClsA):
    if ENABLE_ATTRS:
        c: float = attr.ib(validator=attr.validators.lt(5))
    else:
        c: float
    d: str
    Schema : ClassVar[Type[Schema]] = Schema


B = ClsB.Schema().load({'a': 123, 'b': 'bb', 'c': 4, 'd': "123"})
print(B)
# ClsB(a=123, b='bb', c=4.0, d='123')

B = ClsB.Schema().load({'a': 123, 'b': 'bb', 'c': "3", 'd': 'dd'})
print(B)
# ClsB(a=123, b='bb', c=3.0, d='dd')

try:
    B = ClsB.Schema().load({'a': "asdad", 'b': 'bb', 'c': 2, 'd': 'dd'})
    print(B)
except Exception as e:
    print(e)
# {'a': ['Not a valid integer.']}

try:
    B = ClsB.Schema().load({'a': 123, 'b': 'bb', 'c': '123123asd', 'd': 'dd'})
    print(B)
except Exception as e:
    print(e)
# {'c': ['Not a valid number.']}

try:
    B = ClsB.Schema().load({'a': 123, 'b': 'bb', 'c': '123123', 'd': 'dd'})
    print(B)
except Exception as e:
    print(e)
# "ClsB(a=123, b='bb', c=123123.0, d='dd')" if not ENABLE_ATTRS else "'c' must be < 5: 123123.0"
@hynek
Copy link
Member

hynek commented Apr 4, 2023

I've simplified your breaking example to:

import attr, marshmallow_dataclass
from marshmallow import Schema
from typing import ClassVar, Type


@marshmallow_dataclass.add_schema
@attr.s(auto_attribs=True)
class ClsA:
    a: int
    b: str
    Schema: ClassVar[Type[Schema]] = Schema


@marshmallow_dataclass.add_schema
@attr.s(auto_attribs=True)
class ClsB(ClsA):
    c: float = attr.ib(validator=attr.validators.lt(5))
    d: str
    Schema: ClassVar[Type[Schema]] = Schema

b = ClsB(1, "2", 3.0, "4")

print(ClsB.Schema().dumps(b))

B = ClsB.Schema().load({"a": 123, "b": "bb", "c": 4, "d": "123"})

Sadly, given the output of the print (only c and d), my guess is that marshmallow-dataclass simply doesn't support attrs. All attributes are accessible at __attrs_attrs__ (or preferably via attrs.fields()) but it seems to only know about the fields that are defined within the body using type annotations.

I suspect it would be rather trivial for marshmallow-dataclass to support attrs if they cared for it, but it's nothing we can do anything about here at attrs.

@hynek hynek closed this as not planned Won't fix, can't repro, duplicate, stale Apr 4, 2023
@csghone
Copy link
Author

csghone commented Apr 5, 2023

@hynek - Thank you for the response.
I'll try out some experiments to find some better workaround based on your explanations.

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

2 participants