marshmallow icon indicating copy to clipboard operation
marshmallow copied to clipboard

Type hinting for `load` is too strict

Open jrefice opened this issue 6 years ago • 12 comments

The type hinting for load's data argument is set to typing.Mapping which makes sense for the many=False case, but for many=True case the type of data is list, which isn't a Mapping type.

jrefice avatar Nov 12 '19 17:11 jrefice

That sounds like an opportunity for @overload and Literal[True]. https://mypy.readthedocs.io/en/latest/literal_types.html

jtrakk avatar Nov 13 '19 04:11 jtrakk

Ah, right, the typing isn't completely correct. PRs welcome!

sloria avatar Nov 21 '19 15:11 sloria

https://github.com/marshmallow-code/marshmallow/pull/1488 fixes this.

sloria avatar Feb 02 '20 19:02 sloria

The fix isn't quite right. The merged code returns a Union[T, List[T]], but that means my calling code has to handle both cases, even though I'm either calling it with many=True or many=False, not both. Instead, the return type should be dependent on the value of the many parameter.

I believe the way to make the signatures depend on the values of the parameters is

@overload
def f(data: T1, many: Literal[False]) -> T2: ...

@overload
def f(data: Iterable[T1], many: Literal[True]) -> List[T2]: ...


jtrakk avatar Feb 02 '20 20:02 jtrakk

Is there a way to make it work when many=None, i.e. when self.many is used?

sloria avatar Feb 02 '20 20:02 sloria

Depending on pre_load decorators, the input with many=True could even be a non-iterable. E.g. list nested in a {'data': my_list} envelope.

lafrech avatar Feb 02 '20 20:02 lafrech

Is there a way to make it work when many=None, i.e. when self.many is used?

That might require a mypy plugin, I'm not sure. Some of the experts answer questions in https://gitter.im/python/typing, I've gotten help there before.

jtrakk avatar Feb 02 '20 20:02 jtrakk

It makes me wonder whether schemas with self.many=True should instead be regular schemas wrapped up like Many(MySchema()).

jtrakk avatar Feb 02 '20 20:02 jtrakk

@jtrakk Would you like to investigate this further and send a PR to improve the typing? I'm afraid I won't be able to look into this within the next few weeks (lots of work and non-work commitments this month).

It makes me wonder whether schemas with self.many=True should instead be regular schemas wrapped up like Many(MySchema()).

Or even List(MySchema()). But then it'd be difficult to encapsulate all the pre-/post- processing stuff in a single class, e.g. I'm not sure how you'd express this example.

Anyway, that's a larger change we could consider in the future. For now, let's improve the typing with the current API.

sloria avatar Feb 02 '20 22:02 sloria

I'm afraid I won't be able to look into this within the next few weeks

I'm in the same boat there; Sorry to dump these suggestions on you and run away :-)

jtrakk avatar Feb 03 '20 00:02 jtrakk

The same issue is with dump method with many=True

PavelShishmarev avatar Dec 24 '20 12:12 PavelShishmarev

It seems that, assuming pure Python typing, this could almost be expressed using generic types with default values (akin to Rust associated types).

The problem is self.many, since it silently changes the input and return types without actually changing call signatures 😔 Because of that, I think a plugin is the only way to do it without changing the API.

skairunner avatar Mar 20 '25 14:03 skairunner