email.message.Message: Improve payload methods and Explain why __getitem__ isn't typed to return None#11095
Conversation
This comment has been minimized.
This comment has been minimized.
hauntsaninja
left a comment
There was a problem hiding this comment.
Should these be using unions with Any? E.g. we definitely want to handle the payload case, so get_payload() -> _PayloadType | Any seems better
Let's try it, I can always revert it. (also relates to #11094 ) |
This comment has been minimized.
This comment has been minimized.
|
Could be a true positive given that pip is passing around the base |
| def get_unixfrom(self) -> str | None: ... | ||
| def attach(self, payload: Message) -> None: ... | ||
| def get_payload(self, i: int | None = None, decode: bool = False) -> Any: ... # returns _PayloadType | None | ||
| def get_payload( |
There was a problem hiding this comment.
I think pip error might be unavoidable, but we can definitely do better using overloads. if i is not None, then return is message or none. if i is None, then I think it's either str | list[str] if decode is False and bytes if decode is True.
I don't actually see how we get list[Message], but this is all quite gross
There was a problem hiding this comment.
I think I got it all figured out :) This is more than I originally planned for this PR and doesn't address the linked issue, but sure, why not.
The pip error is still there. But should be more accurate. I AM curious about the cases/examples where payload can be a Message (or multipart list[Message]).
Just like for the header, the payload Union issue could be solved with a generic. But that would cascade a lot without defaults. Or overriding HttpMessage's different methods if it's true it should only use strings rather than Header and/or Message
This comment has been minimized.
This comment has been minimized.
Message.__getitem__ isn't typed to return Noneemail.message.Message: Improve payload methods and Explain why __getitem__ isn't typed to return None
hauntsaninja
left a comment
There was a problem hiding this comment.
Nice! (I think we could have gotten away with nominal typing for set_payload, but duck typing can't hurt!)
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
|
Diff from mypy_primer, showing the effect of this PR on open source code: pip (https://github.com/pypa/pip)
+ src/pip/_internal/metadata/_json.py:82: error: Incompatible types in assignment (expression has type "Message | Any | str | list[Message | str]", target has type "str | list[str]") [assignment]
|
|
@AlexWaygood @hauntsaninja Is this OK to merge? |
|
I haven't looked too deeply at this (and while I'm happy to if you want me to, I probably won't get to it in the next few days, unfortunately), but @hauntsaninja approved it earlier, and I trust both his and your judgement :) |
|
I feel confortable with this. It's #11114 I'm less certain about. If something comes up it can be updated there or in a new PR. |
|
fwiw the annotations to |
|
|
|
I think it should be safe to coerce to a string? Alternatively, the middle overload @overload # either
def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | Any: ...could be kept as only returning |
Addresses part of #2333
From #2333 (comment):
I don't think the false-positives a None union here would introduce is something we want. Especially when
.getexists for that purpose.I'm sure the explanation in my comment could be made clearer. Feel free to improve it.