-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Correcting type mismatches in the email module #10444
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
Comments
Thanks for all this work! Looking at the source for
And the annotation for
If I remember correctly, there have been previous checks of the standard library (by @ethanhs?), and we even had discussions about running checks on a regular basis, but I can't find them anymore. |
It's been a looong time since I ran my check against the standard library, I recall running it on the plane ride back from PyCon 2018 :) Here's the issue: #2149 At that point I was mostly just checking "does typeshed have stubs for these modules", which should be easy enough to check, the harder evaluation is coverage of the API surface... |
Stubtest actually does a very good job these days of checking whether things in modules at runtime exist in the stub. But, although it checks for missing modules in our third-party stubs, it currently doesn't do that for our stdlib stubs. I know we're missing several of the newer |
What if we consider the following overload? @overload
def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ...
@overload
def header_store_parse(self, name: str, value: Any) -> tuple[str, Any]: ... The implementation appears to divide into two parts, one for (name: str, value: Any) in Line-141~142, and the other for (name: str, value: str) in Line-148. This is where class EmailPolicy(Policy):
header_factory: Callable[[str, str], str]
def header_store_parse(self, name, value):
# Line 141~142
if hasattr(value, 'name') and value.name.lower() == name.lower():
return (name, value)
...
# Line 148
return (name, self.header_factory(name, value)) EDIT:
Oh I missed this, sorry. I'll think about it some more. |
One idea I have been thinking of, but haven't tried yet:
I think pytype comes with scripts that do step 2, but I might remember wrong. One challenge will be that private methods remain untyped, because they are missing from typeshed. To fix that, you could try running Python's test suite under monkeytype. If you can get this to work, you might discover lots of things to be improved :) |
Thank you, I appreciate all the advice you've given me. |
I hope these three instances of type mismatch can help in enhancing cpython type hints.
1. Type mismatch in
email.charset.Charset.header_encode_lines
Overview
Target for Modification:
Charset.header_encode_lines
inemail/charset.pyi
Existing Type Hint:
(self, string: str, maxlengths: Iterator[int]) -> list[str]
Suggested Type Hint:
(self, string: str, maxlengths: Iterator[int]) -> list[Optional[str]]
Explanation
The method
Charset.header_encode_lines
may potentially return a list containingNone
.Although the path through which control flow might reach
lines.append(None)
is not immediately clear, it's important to note that this condition (whereNone
is included in the returned list) was encountered in CPython's email unit test, specifically in TestEmailAsianCodecs.test_japanese_codecs intest/test_email/test_asian_codecs.py
. The minimal example provided below illustrates this type mismatch:In this example, the method
header_encode_lines
gives the output[None, '=?iso-8859-1?q?Gr=FC=DF_Gott!?=']
. This occurs through the follwoing control flow:2. Type mismatch in
email.charset.Charset.body_encode
Overview
Target for Modification:
Charset.body_encode
inemail/charset.pyi
Existing Type Hint: (overload)
(self, string: None) -> None
,(self, string: str) -> str
Suggested Type Hint: (overload)
(self, string: None) -> None
,(self, string: str | bytes) -> str
Explanation
The method
Charset.body_encode
may be called withbytes
as thestring
parameter, a case not currently covered by the existing type hint:typeshed/stdlib/email/charset.pyi
Lines 23 to 26 in cfc5425
As mentioned in #10429 ,
Message.set_charset
method can passself._payload
value toCharset.body_encode
as thestring
parameter. However, there are scenarios whereMessage.set_charset
modifies thestr
typeself._payload
tobytes
type, before passing it toCharset.body_encode
method. The following snippet from the library code demonstrates this:This mismatch (passing
bytes
in place ofstr
) was noticed in the CPython email unit test,TestEmailAsianCodecs.test_payload_encoding_utf8
found intest/test_email/test_asian_codecs.py
. The compact example provided below highlights this type mismatch:In this example, the
body_encode
method receivesbytes
, invoked via the following control flow:3. Type Mismatch in
email.policy.EmailPolicy.header_store_parse
Overview
Target for Modification:
EmailPolicy.header_store_parse
inemail/policy.pyi
Existing Type Hint:
(self, name: str, value: str) -> tuple[str, str]
Suggested Type Hint:
(self, name: str, value: Any) -> tuple[str, str]
Explanation
The
EmailPolicy.header_store_parse
method can be invoked with an object of any type as thevalue
parameter, but the current type hint only allows for astr
value.typeshed/stdlib/email/policy.pyi
Line 36 in cfc5425
Considering that
email.message.Message
's special method__setitem__
callsEmailPolicy.header_store_parse
and given the signature of__setitem__
, theEmailPolicy.header_store_parse
parameter type should be(self, name: str, value: Any)
.This inconsistency was detected in the CPython email unit test,
TestBytesGenerator.test_smtp_policy
found intest/test_email/test_generator.py
. The trimmed example provided below illustrates this type mismatch:In this scenario, the
msg["From"] = Address(...)
statement invokesmessage.EmailMessage.__setitem__
( =message.Message.__setitem__
) and passes anAddress
object to thevalue
parameter.P.S. All links to CPython reference commit hash
ae315991...
from the current CPython 3.12 branch.P.S. In my attempts to apply existing static type checking tools such as mypy, pytype, and pyright to the type hints in the cpython standard library, I've encountered various errors and imprecise results. I'm wondering if there are any established best practices for this process. Any guidance or tips would be greatly appreciated.
The text was updated successfully, but these errors were encountered: