Skip to content
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

Check deferred nodes after all first passes in an import cycle #2264

Merged
merged 21 commits into from
Oct 24, 2016

Conversation

gvanrossum
Copy link
Member

Might fix #481.

[file b.py]
import a
def g() -> int:
return a.y
Copy link
Collaborator

Choose a reason for hiding this comment

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

Reveal the type of a.y (to verify that it doesn't somehow fall back to Any).

[file a.py]
import b
def f() -> int:
return b.x
Copy link
Collaborator

Choose a reason for hiding this comment

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

Reveal the type of b.x.

in options.strict_optional_whitelist)

def check_first_pass(self) -> None:
"""Type check the entire file, but defer functions with forward references."""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Be more explicit about forward reference mean. In particular, they mean references to definitions whose types haven't been inferred yet.

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 19, 2016

Looks good to me! This hopefully fixes one of the biggest pain points mypy users with large code bases have had.

I'd like to see some more test cases before merging:

  1. Add test case where two passes aren't enough, to verify that we still
    generate cannot determine type errors when needed.
  2. Add test case for referring to a decorated function whose type might not be inferred
    (another common case where we had trouble before).
  3. Add test case where we generate an error only on the second pass (to verify
    that the import context is shown correctly). For example, something like this (partial example):
def f() -> None:
    a = b.x  # Type of b.x only available during second pass
    a + 1   # Error that we can't report during first pass

@gvanrossum
Copy link
Member Author

I'll add more tests. I also discovered that reveal_type() needs to check for current_node_deferred.

@gvanrossum
Copy link
Member Author

Rebased, generalized the multi-pass machinery, and added a test that would require 3 passes.

Still needs tests for (2) and (3). Also, there are still some print() calls left.

@gvanrossum
Copy link
Member Author

I could use a hint for (2).

@gvanrossum
Copy link
Member Author

I think I have all the required tests now. Jukka, can you double-check?

@codecov-io
Copy link

codecov-io commented Oct 23, 2016

Current coverage is 84.90% (diff: 100%)

Merging #2264 into master will increase coverage by 1.80%

@@             master      #2264   diff @@
==========================================
  Files            72         72           
  Lines         19037      20953   +1916   
  Methods           0          0           
  Messages          0          0           
  Branches       3919       4824    +905   
==========================================
+ Hits          15819      17790   +1971   
+ Misses         2618       2588     -30   
+ Partials        600        575     -25   

Powered by Codecov. Last update 15adf8b...f1b8872

return False
self.errors.set_file(self.path)
self.pass_num += 1
print('---', self.path, 'pass', self.pass_num + 1, '---')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove print.

for node, type_name in todo:
if node in done:
continue
print(type_name, '.', node.fullname() or node.name())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove another print.

tmp/a.py:4: error: Cannot determine type of 'x2'

[case testErrorInPassTwo]
# flags: --hide-error-context
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't hide error context to test the it works correctly for errors reported in the second pass.

import a
x = 1 + 1
[out]
tmp/a.py:4: error: Unsupported operand types for + ("int" and "str")
Copy link
Collaborator

@JukkaL JukkaL Oct 24, 2016

Choose a reason for hiding this comment

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

Maybe also test this the other way as well (error reported in b.py in addition a.py).

[file b.py]
from typing import Any
import a
def deco(f: Any) -> Any:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This decorator probably doesn't trigger a second pass, since simple decorator signatures are special cased in semantic analysis. A more complex decorator signature such as Callable[[T], int] -> Callable[[T]], str] is needed.

[file a.py]
import b
def g() -> None:
f()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Make sure that the decorator function returns an interesting type (not Any) and do reveal_type(f()) here to verify that decorator got applied correctly.

@gvanrossum
Copy link
Member Author

Ready for review/merge.

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 24, 2016

LGTM now -- thanks for the updates!

@JukkaL JukkaL merged commit 68c6e96 into master Oct 24, 2016
@gvanrossum gvanrossum deleted the checker-per-file branch October 24, 2016 17:05
@gvanrossum
Copy link
Member Author

gvanrossum commented Oct 25, 2016 via email

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.

Make it easier to work with circular module dependencies
3 participants