Skip to content
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

"Message | str | Any" has no attribute "get_param" #17034

Open
yellowhat opened this issue Mar 15, 2024 · 4 comments
Open

"Message | str | Any" has no attribute "get_param" #17034

yellowhat opened this issue Mar 15, 2024 · 4 comments
Labels
bug mypy got something wrong

Comments

@yellowhat
Copy link

Bug Report

Hi,
consider the following example code.
It was ok up to mypy 1.8.0, it does not work anymore with 1.9.0.

Thanks

To Reproduce

from email import message_from_bytes

msg = message_from_bytes(b"bla")
if not msg.is_multipart():
    raise

for part in msg.get_payload():
    print(part.get_param("name"))

Expected Behavior

$ pip install mypy==1.8.0
...
$ mypy --install-types --non-interactive --strict .
Success: no issues found in 1 source file

Actual Behavior

$ pip install mypy==1.9.0
...
$ mypy --install-types --non-interactive --strict .
a.py:8: error: Item "str" of "Message | str | Any" has no attribute "get_param"  [union-attr]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.9.0
  • Mypy command-line flags: --install-types --non-interactive --strict
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.12.2
@yellowhat yellowhat added the bug mypy got something wrong label Mar 15, 2024
@richin13
Copy link

I think 1.9.0 is the expected behavior 🤔 😅

For the case when get_payload returns a str your call to get_param will fail

If you adjust your code to print the types:

from email import message_from_bytes

msg = message_from_bytes(b"bla")
reveal_type(msg)
if not msg.is_multipart():
    raise
reveal_type(msg)
for part in msg.get_payload():
    reveal_type(part)
    print(part.get_param("name"))

Then run it on 1.8.0:

repro.py:4: note: Revealed type is "email.message.Message"
repro.py:8: note: Revealed type is "email.message.Message"
repro.py:10: note: Revealed type is "Any"
Success: no issues found in 1 source file

1.9.0:

repro.py:4: note: Revealed type is "email.message.Message"
repro.py:7: note: Revealed type is "email.message.Message"
repro.py:9: note: Revealed type is "Union[email.message.Message, builtins.str, Any]"
repro.py:10: error: Item "str" of "Message | str | Any" has no attribute "get_param"  [union-attr]
Found 1 error in 1 file (checked 1 source file)

I think 1.8.0 saying part is of type Any is not quite correct. The if not msg.is_multipart block does not alter anything from mypy's pov

@yellowhat
Copy link
Author

Thanks for the reply.
Therefore, based on your analysis, the only way is to whitelist the get_param line?

@wyattscarpenter
Copy link

@yellowhat I'm not really familiar with this library, but it seems to me that you're confident that part will be a Message, so try this:

from email import message_from_bytes
from email.message import Message

msg = message_from_bytes(b"bla")
if not msg.is_multipart():
    raise

for part in msg.get_payload():
    assert isinstance(part, Message)
    print(part.get_param("name"))

This will ensure the type is correct by exploding at runtime if it is wrong (as in our example, come to think of it). You could also write similar code to check the type and do something appropriate in each case. (if isinstance(part, Message): ... elif ...: ... else: ...)

For more information, see https://mypy.readthedocs.io/en/stable/type_narrowing.html , https://docs.python.org/3/library/functions.html#isinstance , https://docs.python.org/3/reference/simple_stmts.html#assert

@yellowhat
Copy link
Author

Thanks for the suggestion.

But if I have to add code to workaround this, I would just ignore it:

part.get_param("name")  # type: ignore[union-attr]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants