Skip to content

Commit

Permalink
Add tests for Literal types with incremental and fine-grained mode (#…
Browse files Browse the repository at this point in the history
…6075)

This pull request adds a variety of tests for Literal types and
incremental and fine-grained mode.

Most of the tests are relatively simple: the most complex ones
focus on testing chained references to literal types and various
shenanigans with str/unicode/bytes.

It also removes one assert from the `visit_raw_literal` function in
astmerge's TypeReplaceVisitor: it turned out it'll sometimes visit
unanalyzed types.
  • Loading branch information
Michael0x2a authored Jan 4, 2019
1 parent 25b83ea commit 2b8691d
Show file tree
Hide file tree
Showing 7 changed files with 611 additions and 2 deletions.
9 changes: 7 additions & 2 deletions mypy/server/astmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,12 @@ def replace_statements(self, nodes: List[Statement]) -> List[Statement]:


class TypeReplaceVisitor(SyntheticTypeVisitor[None]):
"""Similar to NodeReplaceVisitor, but for type objects."""
"""Similar to NodeReplaceVisitor, but for type objects.
Note: this visitor may sometimes visit unanalyzed types
such as 'UnboundType' and 'RawLiteralType' For example, see
NodeReplaceVisitor.process_base_func.
"""

def __init__(self, replacements: Dict[SymbolNode, SymbolNode]) -> None:
self.replacements = replacements
Expand Down Expand Up @@ -393,7 +398,7 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None:
typ.fallback.accept(self)

def visit_raw_literal_type(self, t: RawLiteralType) -> None:
assert False, "Unexpected RawLiteralType after semantic analysis phase"
pass

def visit_literal_type(self, typ: LiteralType) -> None:
typ.fallback.accept(self)
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
'contextlib',
'sys',
'mypy_extensions',
'typing_extensions',
'enum',
)

Expand Down
14 changes: 14 additions & 0 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -4890,3 +4890,17 @@ python_version=3.6
[out]
[out2]
tmp/a.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")

[case testLiteralIncrementalTurningIntoLiteral]
import mod
reveal_type(mod.a)
[file mod.py]
from typing_extensions import Literal
a = 1
[file mod.py.2]
from typing_extensions import Literal
a: Literal[2] = 2
[out]
main:2: error: Revealed type is 'builtins.int'
[out2]
main:2: error: Revealed type is 'Literal[2]'
16 changes: 16 additions & 0 deletions test-data/unit/deps-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,19 @@ def g() -> None:
[out]
<m.f1> -> m.g
<m.f2> -> m.g

[case testLiteralDepsExpr]
from typing_extensions import Literal

Alias = Literal[1]

a: Alias
b = a
def f(x: Alias) -> None: pass
def g() -> Literal[1]:
return b
[out]
<m.Alias> -> m, m.f
<m.a> -> m
<m.b> -> m, m.g
<typing_extensions.Literal> -> m
299 changes: 299 additions & 0 deletions test-data/unit/diff.test
Original file line number Diff line number Diff line change
Expand Up @@ -1109,3 +1109,302 @@ plugins=<ROOT>/test-data/unit/plugins/dyn_class.py
[out]
__main__.Diff
__main__.Diff.x

[case testLiteralTriggersVar]
from typing_extensions import Literal

x: Literal[1] = 1
y = 1
z: Literal[1] = 1
same: Literal[1] = 1
class C:
x_class: Literal[1] = 1
y_class = 1
z_class: Literal[1] = 1
same_class: Literal[1] = 1
def __init__(self) -> None:
self.x_instance: Literal[1] = 1
self.y_instance = 1
self.z_instance: Literal[1] = 1
self.same_instance: Literal[1] = 1

[file next.py]
from typing_extensions import Literal

x = 1
y: Literal[1] = 1
z: Literal[2] = 2
same: Literal[1] = 1
class C:
x_class = 1
y_class: Literal[1] = 1
z_class: Literal[2] = 2
same_class: Literal[1] = 1
def __init__(self) -> None:
self.x_instance = 1
self.y_instance: Literal[1] = 1
self.z_instance: Literal[2] = 2
self.same_instance: Literal[1] = 1
[out]
__main__.C.x_class
__main__.C.x_instance
__main__.C.y_class
__main__.C.y_instance
__main__.C.z_class
__main__.C.z_instance
__main__.x
__main__.y
__main__.z

[case testLiteralTriggersFunctions]
from typing_extensions import Literal

def function_1() -> int: pass
def function_2() -> Literal[1]: pass
def function_3() -> Literal[1]: pass

def function_4(x: int) -> None: pass
def function_5(x: Literal[1]) -> None: pass
def function_6(x: Literal[1]) -> None: pass

def function_same_1() -> Literal[1]: pass
def function_same_2(x: Literal[1]) -> None: pass

class C:
def method_1(self) -> int: pass
def method_2(self) -> Literal[1]: pass
def method_3(self) -> Literal[1]: pass

def method_4(self, x: int) -> None: pass
def method_5(self, x: Literal[1]) -> None: pass
def method_6(self, x: Literal[1]) -> None: pass

def method_same_1(self) -> Literal[1]: pass
def method_same_2(self, x: Literal[1]) -> None: pass

@classmethod
def classmethod_1(cls) -> int: pass
@classmethod
def classmethod_2(cls) -> Literal[1]: pass
@classmethod
def classmethod_3(cls) -> Literal[1]: pass

@classmethod
def classmethod_4(cls, x: int) -> None: pass
@classmethod
def classmethod_5(cls, x: Literal[1]) -> None: pass
@classmethod
def classmethod_6(cls, x: Literal[1]) -> None: pass

@classmethod
def classmethod_same_1(cls) -> Literal[1]: pass
@classmethod
def classmethod_same_2(cls, x: Literal[1]) -> None: pass

@staticmethod
def staticmethod_1() -> int: pass
@staticmethod
def staticmethod_2() -> Literal[1]: pass
@staticmethod
def staticmethod_3() -> Literal[1]: pass

@staticmethod
def staticmethod_4(x: int) -> None: pass
@staticmethod
def staticmethod_5(x: Literal[1]) -> None: pass
@staticmethod
def staticmethod_6(x: Literal[1]) -> None: pass

@staticmethod
def staticmethod_same_1() -> Literal[1]: pass
@staticmethod
def staticmethod_same_2(x: Literal[1]) -> None: pass

[file next.py]
from typing_extensions import Literal

def function_1() -> Literal[1]: pass
def function_2() -> int: pass
def function_3() -> Literal[2]: pass

def function_4(x: Literal[1]) -> None: pass
def function_5(x: int) -> None: pass
def function_6(x: Literal[2]) -> None: pass

def function_same_1() -> Literal[1]: pass
def function_same_2(x: Literal[1]) -> None: pass

class C:
def method_1(self) -> Literal[1]: pass
def method_2(self) -> int: pass
def method_3(self) -> Literal[2]: pass

def method_4(self, x: Literal[1]) -> None: pass
def method_5(self, x: int) -> None: pass
def method_6(self, x: Literal[2]) -> None: pass

def method_same_1(self) -> Literal[1]: pass
def method_same_2(self, x: Literal[1]) -> None: pass

@classmethod
def classmethod_1(cls) -> Literal[1]: pass
@classmethod
def classmethod_2(cls) -> int: pass
@classmethod
def classmethod_3(cls) -> Literal[2]: pass

@classmethod
def classmethod_4(cls, x: Literal[1]) -> None: pass
@classmethod
def classmethod_5(cls, x: int) -> None: pass
@classmethod
def classmethod_6(cls, x: Literal[2]) -> None: pass

@classmethod
def classmethod_same_1(cls) -> Literal[1]: pass
@classmethod
def classmethod_same_2(cls, x: Literal[1]) -> None: pass

@staticmethod
def staticmethod_1() -> Literal[1]: pass
@staticmethod
def staticmethod_2() -> int: pass
@staticmethod
def staticmethod_3() -> Literal[2]: pass

@staticmethod
def staticmethod_4(x: Literal[1]) -> None: pass
@staticmethod
def staticmethod_5(x: int) -> None: pass
@staticmethod
def staticmethod_6(x: Literal[2]) -> None: pass

@staticmethod
def staticmethod_same_1() -> Literal[1]: pass
@staticmethod
def staticmethod_same_2(x: Literal[1]) -> None: pass
[builtins fixtures/classmethod.pyi]
[out]
__main__.C.classmethod_1
__main__.C.classmethod_2
__main__.C.classmethod_3
__main__.C.classmethod_4
__main__.C.classmethod_5
__main__.C.classmethod_6
__main__.C.method_1
__main__.C.method_2
__main__.C.method_3
__main__.C.method_4
__main__.C.method_5
__main__.C.method_6
__main__.C.staticmethod_1
__main__.C.staticmethod_2
__main__.C.staticmethod_3
__main__.C.staticmethod_4
__main__.C.staticmethod_5
__main__.C.staticmethod_6
__main__.function_1
__main__.function_2
__main__.function_3
__main__.function_4
__main__.function_5
__main__.function_6

[case testLiteralTriggersProperty]
from typing_extensions import Literal

class C:
@property
def p1(self) -> Literal[1]: pass

@property
def p2(self) -> int: pass

@property
def same(self) -> Literal[1]: pass

[file next.py]
from typing_extensions import Literal

class C:
@property
def p1(self) -> int: pass

@property
def p2(self) -> Literal[1]: pass

@property
def same(self) -> Literal[1]: pass
[builtins fixtures/property.pyi]
[out]
__main__.C.p1
__main__.C.p2

[case testLiteralsTriggersOverload]
from typing import overload
from typing_extensions import Literal

@overload
def func(x: str) -> str: ...
@overload
def func(x: Literal[1]) -> int: ...
def func(x):
pass

@overload
def func_same(x: str) -> str: ...
@overload
def func_same(x: Literal[1]) -> int: ...
def func_same(x):
pass

class C:
@overload
def method(self, x: str) -> str: ...
@overload
def method(self, x: Literal[1]) -> int: ...
def method(self, x):
pass

@overload
def method_same(self, x: str) -> str: ...
@overload
def method_same(self, x: Literal[1]) -> int: ...
def method_same(self, x):
pass

[file next.py]
from typing import overload
from typing_extensions import Literal

@overload
def func(x: str) -> str: ...
@overload
def func(x: Literal[2]) -> int: ...
def func(x):
pass

@overload
def func_same(x: str) -> str: ...
@overload
def func_same(x: Literal[1]) -> int: ...
def func_same(x):
pass

class C:
@overload
def method(self, x: str) -> str: ...
@overload
def method(self, x: Literal[2]) -> int: ...
def method(self, x):
pass

@overload
def method_same(self, x: str) -> str: ...
@overload
def method_same(self, x: Literal[1]) -> int: ...
def method_same(self, x):
pass
[out]
__main__.C.method
__main__.func
Loading

0 comments on commit 2b8691d

Please sign in to comment.