Skip to content

Commit

Permalink
Allow using '...' as a top-level type annotation.
Browse files Browse the repository at this point in the history
This is an experimental features that allows explicitly annotating a type as
"inferred." See python/typing#276.

PiperOrigin-RevId: 420417222
  • Loading branch information
rchen152 committed Jan 13, 2022
1 parent fa43edc commit 9f3f21e
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 6 deletions.
6 changes: 5 additions & 1 deletion pytype/annotation_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ def convert_annotations_list(self, node, annotations_list):
"""Convert a (name, raw_annot) list to a {name: annotation} dict."""
annotations = {}
for name, t in annotations_list:
if t is None:
if t is None or t == self.ctx.convert.ellipsis:
# '...' is an experimental "inferred type": see b/213607272.
continue
annot = self._process_one_annotation(node, t, name,
self.ctx.vm.simple_stack())
Expand Down Expand Up @@ -287,6 +288,9 @@ def apply_annotation(self, node, op, name, value):
if not op.annotation:
return None, value
annot = op.annotation
if annot == "...":
# Experimental "inferred type": see b/213607272.
return None, value
frame = self.ctx.vm.frame
with self.ctx.vm.generate_late_annotations(self.ctx.vm.simple_stack()):
var, errorlog = abstract_utils.eval_expr(self.ctx, node, frame.f_globals,
Expand Down
44 changes: 40 additions & 4 deletions pytype/tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,18 +940,17 @@ def g(x: int) -> int: ...
def test_ellipsis(self):
ty, errors = self.InferWithErrors("""
from typing import Dict, Tuple
def f(x: ...): pass # invalid-annotation[e1]
def f(x: ...): pass # experimental "inferred type": see b/213607272
def g(x: Tuple[str, ...]): pass
def h(x: Dict[..., int]): pass # invalid-annotation[e2]
def h(x: Dict[..., int]): pass # invalid-annotation[e]
""")
self.assertTypesMatchPytd(ty, """
from typing import Any, Dict, Tuple
def f(x) -> None: ...
def g(x: Tuple[str, ...]) -> None: ...
def h(x: Dict[Any, int]) -> None: ...
""")
self.assertErrorRegexes(
errors, {"e1": r"Ellipsis.*x", "e2": r"Ellipsis.*Dict"})
self.assertErrorRegexes(errors, {"e": r"Ellipsis.*Dict"})

def test_custom_container(self):
with file_utils.Tempdir() as d:
Expand Down Expand Up @@ -1298,5 +1297,42 @@ class B:
assert_type(A().c, Optional[B])
""")


class EllipsisTest(test_base.BaseTest):
"""Tests usage of '...' to mean "inferred type".
This is an experimental feature that makes it possible to explicitly annotate
a type as inferred. See b/213607272.
"""

def test_variable(self):
ty = self.Infer("x: ... = 0")
self.assertTypesMatchPytd(ty, "x: int")

def test_function(self):
ty = self.Infer("""
def f(x: ...) -> ...:
return x
""")
self.assertTypesMatchPytd(ty, """
from typing import TypeVar
_T0 = TypeVar('_T0')
def f(x: _T0) -> _T0: ...
""")

def test_class(self):
ty = self.Infer("""
class Foo:
x: ...
def f(self):
self.x = 5
""")
self.assertTypesMatchPytd(ty, """
class Foo:
x: int
def f(self) -> None: ...
""")


if __name__ == "__main__":
test_base.main()
5 changes: 4 additions & 1 deletion pytype/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1668,7 +1668,10 @@ def byte_STORE_SUBSCR(self, state, op):
state, (val, obj, subscr) = state.popn(3)
state = state.forward_cfg_node()
# Check whether obj is the __annotations__ dict.
if len(obj.data) == 1 and isinstance(obj.data[0], abstract.AnnotationsDict):
# '...' is an experimental "inferred type": see b/213607272.
if (len(obj.data) == 1 and
isinstance(obj.data[0], abstract.AnnotationsDict) and
val.data != [self.ctx.convert.ellipsis]):
try:
name = abstract_utils.get_atomic_python_constant(subscr, str)
except abstract_utils.ConversionError:
Expand Down

0 comments on commit 9f3f21e

Please sign in to comment.