Skip to content

Conversation

@lovelydinosaur
Copy link
Contributor

Refs #993, #985

Makes more careful use of typing.Union[str, bytes] vs. typing.AnyStr.

Use AnyStr where we'd expect the types to be the same across everything in the signature. Eg. typing.Mapping[AnyStr, AnyStr] means "choose either str or bytes here, but be consistent.".

Resolves an issues where mypy errors with a really simple bit of httpx usage...

example.py

import httpx

headers = {"content-type": "example"}
httpx.Headers(headers)

MyPy

$ venv/bin/mypy example.py 
example.py:5: error: Argument 1 to "Headers" has incompatible type "Dict[str, str]"; expected "Union[Headers, Dict[Union[str, bytes], Union[str, bytes]], Sequence[Tuple[Union[str, bytes], Union[str, bytes]]], None]"
Found 1 error in 1 file (checked 1 source file)

@lovelydinosaur
Copy link
Contributor Author

After this change...

$ venv/bin/mypy example.py 
Success: no issues found in 1 source file

@yeraydiazdiaz
Copy link
Contributor

Eg. typing.Mapping[AnyStr, AnyStr] means "choose either str or bytes here, but be consistent.".

I don't think that's the case, either using Mapping or Dict, mypy is fine with the code below:

import typing

AnyStrDict = typing.Dict[typing.AnyStr, typing.AnyStr]
AnyStrMapping = typing.Mapping[typing.AnyStr, typing.AnyStr]
d: AnyStrDict = {b"foo": "bar"}
m: AnyStrMapping = {b"foo": "bar"}

@lovelydinosaur
Copy link
Contributor Author

Gotcha. It’s at least “consistent within this part of the type” tho right? Eg. Keys as str or Keys as bytes, not keys as str or bytes.

@yeraydiazdiaz
Copy link
Contributor

Apparently not 😕 , again mypy is completely fine with the code below:

import typing

AnyStrDict = typing.Dict[typing.AnyStr, typing.AnyStr]
AnyStrMapping = typing.Mapping[typing.AnyStr, typing.AnyStr]
d: AnyStrDict = {"foo": "bar", b"bar": "baz"}
m: AnyStrMapping = {b"foo": "bar", "foo": "baz"}

Looks like the guarantees only hold in the context of a function. From the docs:

It is meant to be used for functions that may accept any kind of string without allowing different kinds of strings to mix.

@lovelydinosaur
Copy link
Contributor Author

Interestingly MyPy's behaviour varies depending on if the annotation is used directly or indirectly, eg...

from typing import Union, Dict, Sequence, Tuple, AnyStr

HeaderTypes = Union[
    Dict[AnyStr, AnyStr], Sequence[Tuple[AnyStr, AnyStr]],
]

def foo(h: HeaderTypes) -> None:
    print(h)


def bar(h: Union[Dict[AnyStr, AnyStr], Sequence[Tuple[AnyStr, AnyStr]]]) -> None:
    print(h)


headers = {b"content-type": b"example", "accept": "*/*"}
foo(headers)  # Ok
bar(headers)  # Error 

Copy link
Contributor

@florimondmanca florimondmanca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@lovelydinosaur lovelydinosaur merged commit 3721a78 into master May 27, 2020
@lovelydinosaur lovelydinosaur deleted the use-anystr-where-appropriate branch May 27, 2020 14:17
@lovelydinosaur lovelydinosaur mentioned this pull request May 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants