-
-
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
Support generic partial types for attributes #8044
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1458,9 +1458,9 @@ class A: | |
class A: | ||
def f(self) -> None: | ||
# Attributes aren't supported right now. | ||
self.a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
self.a = [] | ||
self.a.append(1) | ||
self.a.append('') | ||
self.a.append('') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferListInitializedToEmptyInClassBodyAndOverriden] | ||
|
@@ -1585,6 +1585,121 @@ oo.update(d) | |
reveal_type(oo) # N: Revealed type is 'collections.OrderedDict[builtins.int*, builtins.str*]' | ||
[builtins fixtures/dict.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssigned] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] | ||
if bool(): | ||
self.a = [1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test using a non-self attribute -- it shouldn't affect type inferece. For example: ...
self.a = []
if bool():
a = self
a.a = [1] # Should not infer type
a.append(1) # Should not infer type |
||
reveal_type(C().a) # N: Revealed type is 'builtins.list[builtins.int*]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAppended] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] | ||
if bool(): | ||
self.a.append(1) | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[builtins.int]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssignedItem] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = {} | ||
if bool(): | ||
self.a[0] = 'yes' | ||
reveal_type(C().a) # N: Revealed type is 'builtins.dict[builtins.int, builtins.str]' | ||
[builtins fixtures/dict.pyi] | ||
|
||
[case testInferAttributeInitializedToNoneAndAssigned] | ||
# flags: --strict-optional | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = None | ||
if bool(): | ||
self.a = 1 | ||
reveal_type(C().a) # N: Revealed type is 'Union[builtins.int, None]' | ||
|
||
[case testInferAttributeInitializedToEmptyNonSelf] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
if bool(): | ||
a = self | ||
a.a = [1] | ||
a.a.append(1) | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssignedOtherMethod] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
def meth(self) -> None: | ||
self.a = [1] | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAppendedOtherMethod] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
def meth(self) -> None: | ||
self.a.append(1) | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssignedItemOtherMethod] | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...") | ||
def meth(self) -> None: | ||
self.a[0] = 'yes' | ||
reveal_type(C().a) # N: Revealed type is 'builtins.dict[Any, Any]' | ||
[builtins fixtures/dict.pyi] | ||
|
||
[case testInferAttributeInitializedToNoneAndAssignedOtherMethod] | ||
# flags: --strict-optional | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = None | ||
def meth(self) -> None: | ||
self.a = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "None") | ||
reveal_type(C().a) # N: Revealed type is 'None' | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssignedClassBody] | ||
class C: | ||
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
def __init__(self) -> None: | ||
self.a = [1] | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAppendedClassBody] | ||
class C: | ||
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...") | ||
def __init__(self) -> None: | ||
self.a.append(1) | ||
reveal_type(C().a) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testInferAttributeInitializedToEmptyAndAssignedItemClassBody] | ||
class C: | ||
a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...") | ||
def __init__(self) -> None: | ||
self.a[0] = 'yes' | ||
reveal_type(C().a) # N: Revealed type is 'builtins.dict[Any, Any]' | ||
[builtins fixtures/dict.pyi] | ||
|
||
[case testInferAttributeInitializedToNoneAndAssignedClassBody] | ||
# flags: --strict-optional | ||
class C: | ||
a = None | ||
def __init__(self) -> None: | ||
self.a = 1 | ||
reveal_type(C().a) # N: Revealed type is 'Union[builtins.int, None]' | ||
|
||
|
||
-- Inferring types of variables first initialized to None (partial types) | ||
-- ---------------------------------------------------------------------- | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2714,6 +2714,7 @@ class C: | |
class D: | ||
def __init__(self) -> None: | ||
self.x = {} | ||
def meth(self) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about also testing that the type inference works correctly in fine-grained mode (within one method)? |
||
self.x['a'] = 'b' | ||
[file a.py] | ||
def g() -> None: pass | ||
|
@@ -2731,6 +2732,7 @@ class D: | |
def __init__(self) -> None: | ||
a.g() | ||
self.x = {} | ||
def meth(self) -> None: | ||
self.x['a'] = 'b' | ||
[file a.py] | ||
def g() -> None: pass | ||
|
@@ -2742,6 +2744,69 @@ main:5: error: Need type annotation for 'x' (hint: "x: Dict[<type>, <type>] = .. | |
== | ||
main:5: error: Need type annotation for 'x' (hint: "x: Dict[<type>, <type>] = ...") | ||
|
||
[case testRefreshPartialTypeInferredAttributeIndex] | ||
from c import C | ||
reveal_type(C().a) | ||
[file c.py] | ||
from b import f | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = {} | ||
if bool(): | ||
self.a[0] = f() | ||
[file b.py] | ||
def f() -> int: ... | ||
[file b.py.2] | ||
from typing import List | ||
def f() -> str: ... | ||
[builtins fixtures/dict.pyi] | ||
[out] | ||
main:2: note: Revealed type is 'builtins.dict[builtins.int, builtins.int]' | ||
== | ||
main:2: note: Revealed type is 'builtins.dict[builtins.int, builtins.str]' | ||
|
||
[case testRefreshPartialTypeInferredAttributeAssign] | ||
from c import C | ||
reveal_type(C().a) | ||
[file c.py] | ||
from b import f | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] | ||
if bool(): | ||
self.a = f() | ||
[file b.py] | ||
from typing import List | ||
def f() -> List[int]: ... | ||
[file b.py.2] | ||
from typing import List | ||
def f() -> List[str]: ... | ||
[builtins fixtures/list.pyi] | ||
[out] | ||
main:2: note: Revealed type is 'builtins.list[builtins.int]' | ||
== | ||
main:2: note: Revealed type is 'builtins.list[builtins.str]' | ||
|
||
[case testRefreshPartialTypeInferredAttributeAppend] | ||
from c import C | ||
reveal_type(C().a) | ||
[file c.py] | ||
from b import f | ||
class C: | ||
def __init__(self) -> None: | ||
self.a = [] | ||
if bool(): | ||
self.a.append(f()) | ||
[file b.py] | ||
def f() -> int: ... | ||
[file b.py.2] | ||
def f() -> str: ... | ||
[builtins fixtures/list.pyi] | ||
[out] | ||
main:2: note: Revealed type is 'builtins.list[builtins.int]' | ||
== | ||
main:2: note: Revealed type is 'builtins.list[builtins.str]' | ||
|
||
[case testRefreshTryExcept] | ||
import a | ||
def f() -> 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.
Does this duplicate any logic used for
None
partial type handling? Could they be merged?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.
Short answers are no and no. Some things may be however simplified after (or while) fixing #8043 (at least the first bullet).