marshmallow icon indicating copy to clipboard operation
marshmallow copied to clipboard

Generate a TypedDict or dataclass from Schema

Open OJFord opened this issue 4 years ago • 7 comments

It would be nice to be able to use Schemata with mypy or similar, e.g.

class User(Schema):
    name = fields.String()
    age = fields.Integer()


def have_a_birthday(user: User.type) -> User.type:
    user['age'] += '1'  # will fail type check
    return user  # typechecks OK

I started an inline implementation with Schema._declared_fields, but since there's nothing like a Field.deser_type it was going to be quite a hacky map by cases - isinstance(cls, fields.String) etc. - and then I realised I had nested fields to contend with and jumped out of the rabbit hole before I got too deep.

I do think this would be very nice to have though, or perhaps even go further and have a @deser_to_dataclass decorator (that's the route I was going) that re-writes the Schema so that the type is User (or whatever) itself, and User.name is annotated automatically as : str.

OJFord avatar Sep 17 '19 18:09 OJFord

I've been considering something like this. It's a neat idea.

A few half-baked thoughts:

  • You would need to generate types from schema instances rather than classes. Conceptually, a schema instance represents a set of known inputs/outputs, whereas a schema class is more like a blueprint or factory.
  • There need to be different TypedDicts generated for load vs dump. So you could have an API like
def user_detail(user: LoadType(UserSchema())) -> DumpType(UserSchema()):
    # ...
  • This is pretty tricky given that post_load and post_dump methods can return any type.

I probably won't pursue this myself for a while, and I don't think we should put this into marshmallow core at least until PEP 589 lands. If you end up experimenting with this in a 3rd-party repo, though, please let us know.

I do think this would be very nice to have though, or perhaps even go further and have a @deser_to_dataclass

You might want to look into https://github.com/lovasoa/marshmallow_dataclass . It generates a schema from dataclass that also deserializes to the same dataclass.

sloria avatar Sep 17 '19 19:09 sloria

+1 for marshmallow_dataclass. It would be cool to eventually see this kind of functionality included in Marshmallow by default now that it's Python 3-only!

mivade avatar Sep 18 '19 17:09 mivade

Well, we'll be supporting Python 3.5 and 3.6 for a while, so probably won't be added to marshmallow core any time soon. That said, it seems to be actively maintained, and I might even help out with it.

sloria avatar Sep 18 '19 17:09 sloria

Maybe marshmallow 4 could go PY3.7+ and include marshmallow-dataclass?

lafrech avatar Nov 12 '19 08:11 lafrech

Maybe marshmallow 4 could go PY3.7+ and include marshmallow-dataclass?

@lafrech I'm open to that. We could even do it in marshmallow 3 if we keep it in an isolated module.

sloria avatar Feb 10 '20 00:02 sloria

How about building a schema based on an existing TypedDict class? There's a plenty of information that can be inferred from the type definition, similar to marshmallow-dataclass.

killthekitten avatar Sep 21 '20 13:09 killthekitten

Maybe adding a field that allows the dev to state the type to cast the dump into?

Schema().dump(schema_obj, cast=my_typed_dict)

Currently @post_load and @post_dump do not pass along their type hints to the load and dump functions. If they could then that would be even better :)

jeremyfx avatar Sep 09 '21 09:09 jeremyfx