Skip to content

Generic type as schema field type? Is it possible? #203

@belo4ya

Description

@belo4ya

I want to implement a Generic marshmallow dataclass, which I can then embed into other class definitions. Is there any way I can do this?

I want to get a class like DateValue with this behavior

import typing as t

from marshmallow import Schema, fields
from marshmallow_dataclass import NewType, dataclass


class SchemaMixin:
    Schema: t.ClassVar[t.Type[Schema]] = Schema


DateTime = NewType('DateTime', str, field=fields.DateTime, format='iso')
T = t.TypeVar('T', str, int, float)


@dataclass
class DateValue(t.Generic[T], SchemaMixin):
    date: DateTime
    value: T  # here is an error: we need a Type object created using NewType

Example of client code that wants to use DateValue:

@dataclass
class Container(SchemaMixin):
    timeseries_str: list[DateValue[str]]
    timeseries_float: list[DateValue[float]]


print(Container.Schema().load({
    'timeseries_str': [
        {'date': '2022-01-01T00:00:00', 'value': 'str1'},
        {'date': '2022-01-02T00:00:00', 'value': 'str2'},
    ],
    'timeseries_float': [
        {'date': '2022-01-01T00:00:00', 'value': 2},
        {'date': '2022-01-02T00:00:00', 'value': 3.14},
    ],
}))

I get the following error. And this is expected, because the type T was not created using the NewType function.

…\src\.venv\lib\site-packages\marshmallow_dataclass\__init__.py:373: UserWarning: ****** WARNING ****** marshmallow_dataclass was called on the class ~T, which is not a dataclass. It is going to try and convert the class into a dataclass, which may have undesirable side effects. To avoid this message, make sure all your classes and all the classes of their fields are either explicitly supported by marshmallow_dataclass, or define the schema explicitly using field(metadata=dict(marshmallow_field=...)). For more information, see https://github.com/lovasoa/marshmallow_dataclass/issues/51 ****** WARNING ******
  warnings.warn(
Traceback (most recent call last):
  File "…\.pyenv\pyenv-win\versions\3.9.13\lib\dataclasses.py", line 1033, in fields
    fields = getattr(class_or_instance, _FIELDS)
AttributeError: 'TypeVar' object has no attribute '__dataclass_fields__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "…\src\.venv\lib\site-packages\marshmallow_dataclass\__init__.py", line 370, in _internal_class_schema
    fields: Tuple[dataclasses.Field, ...] = dataclasses.fields(clazz)
  File "…\.pyenv\pyenv-win\versions\3.9.13\lib\dataclasses.py", line 1035, in fields
    raise TypeError('must be called with a dataclass type or instance')
TypeError: must be called with a dataclass type or instance

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
…
  File "…\.pyenv\pyenv-win\versions\3.9.13\lib\dataclasses.py", line 835, in _process_class
    for b in cls.__mro__[-1:0:-1]:
AttributeError: 'TypeVar' object has no attribute '__mro__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "…\src\_utils\__init__.py", line 33, in <module>
    print(Container.Schema().load({
…
  File "…\src\.venv\lib\site-packages\marshmallow_dataclass\__init__.py", line 387, in _internal_class_schema
    raise TypeError(
TypeError: T is not a dataclass and cannot be turned into one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions