-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Fine-grained: Don't infer partial types from multiple targets #4553
Conversation
Definitions of local partial types can't span multiple fine-grained incremental targets.
This is ready for review but don't merge yet since I want to fix various issues in internal Dropbox codebases first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, looks good, I have few suggestions here.
def is_defined_in_base_class(self, var: Var) -> bool: | ||
if var.info is not None: | ||
for base in var.info.mro[1:]: | ||
if base.get(var.name()) is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we also check that it is not None
in base class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm not sure what you mean by this. Can you give an example where this would make a difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean add ... and not isinstance(base.get(var.name()), NoneTyp)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base.get()
returns Optional[SymbolTableNode]
so that exact check doesn't make sense. If you meant guarding agains a definition like x = None # type: None
in a base class, I don't agree -- it should be treated as a valid definition. If you still don't agree, can you give me a test case where this would make a difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry, you are right, this is perfectly valid.
mypy/checker.py
Outdated
@@ -107,6 +107,10 @@ | |||
('is_upper_bound', bool), # False => precise type | |||
]) | |||
|
|||
# Keeps track of partial types in a single scope | |||
PartialTypeScope = NamedTuple('PartialTypeMap', [('map', Dict[Var, Context]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't NAME
in NAME = NamedTuple('NAME', ...)
always be the same in l.h.s. and r.h.s.?
mypy/checker.py
Outdated
@@ -107,6 +107,10 @@ | |||
('is_upper_bound', bool), # False => precise type | |||
]) | |||
|
|||
# Keeps track of partial types in a single scope | |||
PartialTypeScope = NamedTuple('PartialTypeMap', [('map', Dict[Var, Context]), | |||
('is_function', bool)]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a short comment about why we need to keep track about is_function
will help here. (My understanding this is because fine-grained targets are always top level functions.)
# flags: --local-partial-types --strict-optional | ||
from typing import Any | ||
|
||
A: Any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add the same test where A
is not Any
, but a normal class. (And None
in two subclasses like here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test case.
y = '' | ||
[out] | ||
== | ||
a.py:1: error: Need type annotation for 'y' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for completes I would add a tests where the error appears in the first run, but not in the second one (like if a user listened and actually added an annotation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test case.
The travis failure seems like a known flake. |
* master: (27 commits) Don't call --strict-optional and --incremental experimental (python#4642) Sync typeshed (python#4641) Fix callable types with inconsistent argument counts (python#4611) Fix example (add 'class A:') Make psutil an optional dependency (python#4634) mypy and mypy_extensions aren't posix only (python#3765) Documentation for attr support (python#4632) Use read_with_python_encoding in stubgen to handle file encoding (python#3790) Sync typeshed (python#4631) Add remaining core team emails to CREDITS (python#4629) Fix issues with attr code. (python#4628) Better support for converter in attrs plugin. (python#4607) Clean up credits (python#4626) Support type aliases in fine-grained incremental mode (python#4525) Fine-grained: Fix crash caused by unreachable class (python#4613) Treat divmod like a binary operator (python#4585) Sync typeshed (python#4605) Fine-grained: Don't infer partial types from multiple targets (python#4553) Fine-grained: Compare symbol table snapshots when following dependencies (python#4598) Fix type of forward reference to a decorated class method (python#4486) ...
…#4553) Previously if partial types were inferred from assignments in multiple scopes, like in the example below, fine-grained incremental mode could display bogus messages, since only one of the scopes could be reprocessed in one incremental update: ``` x = None def f() -> None: global x x = '' ``` This PR fixes the issue by always inferring partial types within a single fine-grained incremental target. This is always enabled in fine-grained incremental mode and can also be turned on in other modes through a new (but hidden) `--local-partial-types` option. This was complicated by various edge cases that are covered by the new test cases and/or documented in comments. If the new option is not enabled, the old behavior should (mostly) be preserved, module some fixes that may affect also the default mode. Work towards python#4492.
Previously if partial types were inferred from assignments in multiple
scopes, like in the example below, fine-grained incremental mode could
display bogus messages, since only one of the scopes could be
reprocessed in one incremental update:
This PR fixes the issue by always inferring partial types within a single
fine-grained incremental target. This is always enabled in fine-grained
incremental mode and can also be turned on in other modes through
a new (but hidden)
--local-partial-types
option.This was complicated by various edge cases that covered by the new
test cases and/or documented in comments.
If the new option is not enabled, the old behavior should (mostly) be
preserved, module some fixes that may affect also the default mode.
Work towards #4492.