Skip to content

os.open should not have AnyStr in its type #439

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

Closed
gnprice opened this issue Aug 4, 2016 · 4 comments
Closed

os.open should not have AnyStr in its type #439

gnprice opened this issue Aug 4, 2016 · 4 comments

Comments

@gnprice
Copy link
Contributor

gnprice commented Aug 4, 2016

See python/mypy#1943 -- AnyStr shouldn't be used when it only appears once in the type, because it complicates the semantics without adding anything.

@matthiaskramm
Copy link
Contributor

Should type checkers just detect a single usage and optimize for it? AnyStr is a pretty convenient shortcut.

@gnprice
Copy link
Contributor Author

gnprice commented Aug 5, 2016

I'm not convinced that would actually be correct, under the semantics specified in PEP 484.

The semantics of AnyStr is that it's a type variable which has to specifically take on (in Python 3) either the value str or the value bytes. So in code like this:

AnyStr = TypeVar('AnyStr', constraints=[str, bytes])

def f(x: AnyStr) -> None: ...

x = 'a'  # type: Union[str, bytes]

f(x)  # error?

We're trying to use a value of type Union[str, bytes] in a generic function which requires an AnyStr. What value should the type variable AnyStr take there to make that well-typed?

The type Union[str, bytes] isn't equal to either str or bytes, and in fact it isn't usable as either a str or a bytes. So neither possible way to instantiate the type variable makes this code well-typed.

Does that match your interpretation of the semantics? How would you interpret PEP 484 in the context of this code that makes it well-typed?

@matthiaskramm
Copy link
Contributor

I had assumed that TypeVar("T", X, Y) is equivalent to TypeVar("T", Union[X, Y]).

With that, your code sample passes checking, based on Union[str, bytes] <: Union[str, bytes]?

@gvanrossum
Copy link
Member

No, TypeVar('T', X, Y) cannot be reduced to TypeVar('T', Union[X, Y]). The latter is actually an error according to PEP 484 (and mypy) -- it requires at least two constraint values if there are any. A single constraint would be indistinguishable from the bare constraint type.

(Constrained type variables are tricky that way. It took me a long time to grok them. And I still haven't found an example besides AnyStr. But that's indispensible -- just not for os.open(). :-)

I'm still not sure how to express what I'd like to see, and maybe it's not worth it. We can't exactly say that Union[str, bytes] <: AnyStr. It's only the case that if there's exactly one parameter of type exactly AnyStr, and no other use of AnyStr in the signature, then Union[str, bytes] should be acceptable. But that seems excessively specific. In the meantime we should just change typeshed.

euresti pushed a commit to euresti/typeshed that referenced this issue Mar 21, 2017
In order to unify these two versions I'm making all paths be _PathType = Union[bytes, Text]
Fixes python#439
n8henrie added a commit to n8henrie/typeshed that referenced this issue Jan 9, 2018
It seems that AnyStr has the main advantage of ensuring that multiple arguments (or argument(s) and return values) have the same type (i.e. `str` and `bytes` are both okay but shouldn't be mixed). However it makes it significantly more difficult to cast to a single type.

With a Union, something like:

```python
def foo(a: Union[bytes, Text]) -> Text:
    if isinstance(a, bytes):
        a = a.decode()
    return "Hello, " + a
```

works fine. With AnyStr, you have to define a new intermediate variable, something like:

```python
def foo(a: AnyStr) -> str:
    if isinstance(a, bytes):
        b = a.decode()
    else:
        b = a
    return "Hello, " + b
```

If there's no advantage to using AnyStr (since there's no other AnyStr argument or return value), I think the Union would be simpler, have no significant disadvantage, and there is [precedent for similar changes](python#1054).

> It's only the case that if there's exactly one parameter of type exactly AnyStr, and no other use of AnyStr in the signature, then Union[str, bytes] should be acceptable.

-  [gvanrossum](python#439 (comment))

Discussion: 

- python#1054
- python/mypy#1141
- python#439
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

No branches or pull requests

3 participants