-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
email.message.Message
: Improve payload methods and Explain why __getitem__
isn't typed to return None
#11095
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
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
stdlib/email/message.pyi
Outdated
@@ -24,14 +25,18 @@ class Message: | |||
def set_unixfrom(self, unixfrom: str) -> None: ... | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]>
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
.get
exists for that purpose.I'm sure the explanation in my comment could be made clearer. Feel free to improve it.