Skip to content

Binder should reason about complex expressions #2199

Open

Description

@gvanrossum

Here's a very simple example:

from typing import *
a = ['x', None]  # type: List[Optional[str]]
for i in range(len(a)):
    if a[i]:
        print(i, '<' + a[i] + '>')

We get an error on the last line:

__tmp__.py:5: error: Unsupported operand types for + ("str" and "Optional[str]")

Of course I can refactor this by using

for i in range(len(a)):
    ai = a[i]
    if ai:
        print(i, '<' + ai + '>')

but that's just busy-work and doesn't necessarily make the code more readable.

(I found this in some mypy test code where the actual expression was p[i].arg so this probably needs to become fairly powerful.)

Activity

  1. ddfisher commented on Sep 30, 2016

    @ddfisher
    Collaborator

    Yeah, this looks like a general binder feature to me.

  2. changed the title [-]None checking (binder?) should reason about complex expressions[/-] [+]Binder should reason about complex expressions[/+] on Sep 30, 2016
  3. ddfisher commented on Sep 30, 2016

    @ddfisher
    Collaborator

    Actually, these things are pretty difficult to do correctly/safely, because you have to worry about e.g.:

    from typing import *
    a = ['x', None]  # type: List[Optional[str]]
    for i in range(len(a)):
        if a[i]:
            a[0] = None  # how do we detect something like this?
            print(i, '<' + a[i] + '>')
  4. gvanrossum commented on Sep 30, 2016

    @gvanrossum
    MemberAuthor

    I agree it's complicated (and it would be even more complicated if we considered other threads). I'm just pointing out that if we have a lot of code like this (and I've seen a lot of it in Dropbox repos) is all needs to be refactored just to shut up --strict-optional, and that's tedious.

  5. JukkaL commented on Oct 4, 2016

    @JukkaL
    Collaborator

    The binder already isn't safe, so this wouldn't fundamentally change anything. The binder currently seems to handle a[0] but not a[i]. Adding support for the latter hopefully wouldn't be hard. [These only seem to work with isinstance tests. A test such as if a[0]: doesn't seem to be supported, but I assume that it wouldn't be hard to fix.]

    Example of unsafety:

    from typing import *
    a = ['x', 1]  # type: List[Union[int, str]]
    
    for i in range(len(a)):
        if isinstance(a[0], int):
            a[i] = 'x'
            reveal_type(a[0])  # int, even though it's clearly str
    
  6. gvanrossum commented on Oct 4, 2016

    @gvanrossum
    MemberAuthor
  7. JukkaL commented on Oct 4, 2016

    @JukkaL
    Collaborator

    I have no idea. We could perhaps generate a sample of such things from a large corpus and manually analyze them. I doubt we can reason this from first principles.

  8. added this to the milestone on Oct 27, 2016
  9. removed this from the milestone on Mar 29, 2017
  10. ilevkivskyi commented on May 2, 2017

    @ilevkivskyi
    Member

    I agree it's complicated (and it would be even more complicated if we considered other threads). I'm just pointing out that if we have a lot of code like this (and I've seen a lot of it in Dropbox repos) is all needs to be refactored just to shut up --strict-optional, and that's tedious.

    There is a lot of code like this in mypy itself: if x[i] is not None: ..., if x.attr is not None: ..., and None not in x (for collections). I think it makes sense to raise priority to normal here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions