python / typeshed

Collection of library stubs for Python, with static types
Other
4.3k stars 1.73k forks source link

Fallback overloads for literal argument #5493

Open srittau opened 3 years ago

srittau commented 3 years ago

Currently, we use an extra str overload when using literals. For example:

@overload
def open(mode: Literal["r"] = ...) -> int: ...
@overload
def open(mode: Literal["w"]) -> str: ...
@overload
def open(mode: str) -> str | int: ...

What's the use case for this? The only thing I can think of is something like this:

mode: str = ...
open(mode)

But this actually looks like a typing error to me where mode should have been annotated with Literal["r", "w"] anyway. I would suggest starting to remove these defaults, possibly after the next mypy version is released. primer should warn us if this causes lots of new errors (as I would expect for e.g builtins.open()) and we can then decide on a case-by-case basis to leave the default in for now.

Side note: We can't do this for bool yet due to python/mypy#6113.

Akuli commented 3 years ago

Seems like mypy doesn't use our open definition, since this isn't merged yet: https://github.com/python/mypy/pull/9275

erictraut commented 3 years ago

I presume that the str variant is present to handle cases where the literal mode isn't available — e.g. the code is too dynamic for the type checker to infer the literal type or the annotations in the code annotations specify only str without limiting it to specific literal values. It provides a reasonable fallback in these cases.

In most overloads that provide this fallback, the return type for the non-literal variant is not a union but an Any (or a generic type parameterized by Any such as IO[Any]).

This fallback eliminates a class of false positive errors, so I see some utility in it.

I guess it depends on whether you want to force typing strictness on all typeshed users or you want to be "looser" and avoid false positive errors. I can see arguments in favor of both approaches, but my guess is that the majority of Python developers would vote for reducing false positives over being ultra strict.

Akuli commented 3 years ago

In mypy, basically anything that comes from a variable is unlikely to have Literal type:

s = "lol"
reveal_type(s)   # This is str, not Literal["lol"]
srittau commented 3 years ago

Sure, but

In mypy, basically anything that comes from a variable is unlikely to have Literal type:

Sure, but I wonder how common that is and whether it's too much overhead to require users to use a variable annotation in those cases. I think we should experiment with not providing str overloads. Primer could give us a good indication how disruptive these changes are.