override http.HTTPMessage methods to only use str for the header type#11114
Conversation
This comment has been minimized.
This comment has been minimized.
srittau
left a comment
There was a problem hiding this comment.
I think the PR looks good overall. Limiting HTTPMessage to just str seems ok, especially given the primer output.
| def getallmatchingheaders(self, name: str) -> list[str]: ... # undocumented | ||
| # override below all of Message's methods that use `_HeaderType` / `_HeaderTypeParam` with `str` | ||
| # avoiding having to make Message generic | ||
| def __getitem__(self, name: str) -> str: ... # AnyOf[str, None] |
There was a problem hiding this comment.
str | None should be correct here. A header is not guaranteed to exist, so not being prepared for a None value is a strong indication of a bug.
There was a problem hiding this comment.
Since it's a copy, the reasoning is the same as in https://github.com/python/typeshed/pull/11095/files#diff-ced02b01b42b244e9b7bc2e12ef7a215bde89a29072c99ade903f58fb83a3bc7R66-R67
Same as
getwithfailobj=None, but with the expectation that it won't return None in most scenarios
This is important for protocols using__getitem__, likeSupportsKeysAndGetItem
And #11095 (comment)
[...] I don't think the false-positives a None union here would introduce is something we want. Especially when
.getexists for that purpose.
There was a problem hiding this comment.
That being said, the Any trick could be usable here (and in the referenced PR). I should try a primer run with it.
There was a problem hiding this comment.
I'm not sure I agree with that reasoning. While get() exists, __getattr__ returns it as well. This is arguably a misfeature in the implementation (as it should just raise), but still that's something that a user should check against, because it absolutely can return None, even in unexpected scenarios.
There was a problem hiding this comment.
#11095 (comment) At the very least using the Any trick should avoid the bad false-positive with __getitem__ based protocols like SupportsKeysAndGetItem.
Ref #11094
| def __getitem__(self, name: str) -> str: ... # AnyOf[str, None] | |
| def __getitem__(self, name: str) -> str | Any: ... # See comment on `Message.__getitem__` |
There was a problem hiding this comment.
I'm still not happy about this. This should be an error in my opinion:
def parse_headers(headers: SupportsGetItem[str]):
header_value = headers["Content-Type"]
full_header = f"Content-Type: {header_value}"
parse_headers(HTTPMessage(...))Users of HTTPMessage (and email.message.Message) must be equipped to get None values from __getitem__.
| @overload | ||
| def get_all(self, name: str, failobj: _T) -> list[str] | _T: ... |
There was a problem hiding this comment.
Ideally, we'd be able to have an overload like this, but I don't think it's possible:
@overload
def get_all(self, name: str, failobj: list[<empty>]) -> list[str]: ...
There was a problem hiding this comment.
I'm not sure what such an overload would be trying to do. Consumer code is the one passing the failobj and immediately receiving the return type. get_all does nothing with failobj.
If it only supports a list[_HeaderType] it would be its responsibility to pass a value of the appropriate type to failobj, which a typechecker checks for.
def foo(message: HttpMessage) -> list[str] | SentinelType:
return message.get_all("foo", sentinel_value) # Ok
def foo(message: HttpMessage) -> list[str]:
return message.get_all("foo", []) # Fails pyright `reportUnknownArgumentType` but by no fault of the definition
def foo(message: HttpMessage) -> list[str]:
return message.get_all("foo", list[str]()) # ok
def foo(message: HttpMessage) -> list[str]:
return message.get_all("foo", sentinel_value) # Fails type-checkingIf we still really wanna restrict it, we can bind _T to list[_HeaderType]
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Diff from mypy_primer, showing the effect of this PR on open source code: spack (https://github.com/spack/spack)
+ lib/spack/spack/oci/oci.py:142: error: Argument 1 to "endpoint" of "ImageReference" has incompatible type "Optional[str]"; expected "str" [arg-type]
|
Assuming
HTTPMessageshould only work instrheaders, which I'm not certain about. But either way would close #2333 .Signature of the overridden methods based on #11095 .
An alternative to overriding is to make
email.message.Messagegeneric. Which would cause close to 100 changes in stdlib. Unless we defer until PEP 696 support.