Merge branch 'pyre_test_suite'

This commit is contained in:
Jimmy Lai 2019-12-26 11:08:14 -08:00
commit bf0bfd40db
4 changed files with 701 additions and 1 deletions

View file

@ -0,0 +1,494 @@
{
"types": [
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 7,
"column": 19
},
"stop": {
"line": 7,
"column": 27
}
},
"annotation": "typing.Type[typing.Sequence]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 10,
"column": 6
},
"stop": {
"line": 10,
"column": 10
}
},
"annotation": "typing.Type[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 11,
"column": 8
},
"stop": {
"line": 11,
"column": 16
}
},
"annotation": "typing.Callable(libcst.tests.pyre.simple_class.Item.__init__)[[Named(self, unknown), Named(n, int)], None]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 11,
"column": 17
},
"stop": {
"line": 11,
"column": 21
}
},
"annotation": "libcst.tests.pyre.simple_class.Item"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 11,
"column": 23
},
"stop": {
"line": 11,
"column": 24
}
},
"annotation": "int"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 11,
"column": 26
},
"stop": {
"line": 11,
"column": 29
}
},
"annotation": "typing.Type[int]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 11,
"column": 34
},
"stop": {
"line": 11,
"column": 38
}
},
"annotation": "None"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 12,
"column": 8
},
"stop": {
"line": 12,
"column": 12
}
},
"annotation": "libcst.tests.pyre.simple_class.Item"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 12,
"column": 22
},
"stop": {
"line": 12,
"column": 23
}
},
"annotation": "int"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 15,
"column": 6
},
"stop": {
"line": 15,
"column": 19
}
},
"annotation": "typing.Type[libcst.tests.pyre.simple_class.ItemCollector]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 8
},
"stop": {
"line": 16,
"column": 17
}
},
"annotation": "typing.Callable(libcst.tests.pyre.simple_class.ItemCollector.get_items)[[Named(self, unknown), Named(n, int)], typing.Sequence[libcst.tests.pyre.simple_class.Item]]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 18
},
"stop": {
"line": 16,
"column": 22
}
},
"annotation": "libcst.tests.pyre.simple_class.ItemCollector"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 24
},
"stop": {
"line": 16,
"column": 25
}
},
"annotation": "int"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 27
},
"stop": {
"line": 16,
"column": 30
}
},
"annotation": "typing.Type[int]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 35
},
"stop": {
"line": 16,
"column": 43
}
},
"annotation": "typing.Callable(typing.GenericMeta.__getitem__)[[typing.Type[Variable[typing._T_co](covariant)]], typing.Type[typing.Sequence[Variable[typing._T_co](covariant)]]]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 35
},
"stop": {
"line": 16,
"column": 49
}
},
"annotation": "typing.Type[typing.Sequence[libcst.tests.pyre.simple_class.Item]]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 16,
"column": 44
},
"stop": {
"line": 16,
"column": 48
}
},
"annotation": "typing.Type[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 15
},
"stop": {
"line": 17,
"column": 42
}
},
"annotation": "typing.List[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 16
},
"stop": {
"line": 17,
"column": 20
}
},
"annotation": "typing.Type[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 16
},
"stop": {
"line": 17,
"column": 23
}
},
"annotation": "libcst.tests.pyre.simple_class.Item"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 28
},
"stop": {
"line": 17,
"column": 29
}
},
"annotation": "int"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 33
},
"stop": {
"line": 17,
"column": 38
}
},
"annotation": "typing.Type[range]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 33
},
"stop": {
"line": 17,
"column": 41
}
},
"annotation": "range"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 17,
"column": 39
},
"stop": {
"line": 17,
"column": 40
}
},
"annotation": "int"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 20,
"column": 0
},
"stop": {
"line": 20,
"column": 9
}
},
"annotation": "libcst.tests.pyre.simple_class.ItemCollector"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 20,
"column": 12
},
"stop": {
"line": 20,
"column": 25
}
},
"annotation": "typing.Type[libcst.tests.pyre.simple_class.ItemCollector]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 20,
"column": 12
},
"stop": {
"line": 20,
"column": 27
}
},
"annotation": "libcst.tests.pyre.simple_class.ItemCollector"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 21,
"column": 0
},
"stop": {
"line": 21,
"column": 5
}
},
"annotation": "typing.Sequence[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 21,
"column": 8
},
"stop": {
"line": 21,
"column": 17
}
},
"annotation": "libcst.tests.pyre.simple_class.ItemCollector"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 21,
"column": 8
},
"stop": {
"line": 21,
"column": 27
}
},
"annotation": "typing.Callable(libcst.tests.pyre.simple_class.ItemCollector.get_items)[[Named(n, int)], typing.Sequence[libcst.tests.pyre.simple_class.Item]]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 21,
"column": 8
},
"stop": {
"line": 21,
"column": 30
}
},
"annotation": "typing.Sequence[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 21,
"column": 28
},
"stop": {
"line": 21,
"column": 29
}
},
"annotation": "typing_extensions.Literal[3]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 22,
"column": 4
},
"stop": {
"line": 22,
"column": 8
}
},
"annotation": "libcst.tests.pyre.simple_class.Item"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 22,
"column": 12
},
"stop": {
"line": 22,
"column": 17
}
},
"annotation": "typing.Sequence[libcst.tests.pyre.simple_class.Item]"
},
{
"location": {
"path": "libcst/tests/pyre/simple_class.py",
"start": {
"line": 23,
"column": 4
},
"stop": {
"line": 23,
"column": 8
}
},
"annotation": "libcst.tests.pyre.simple_class.Item"
}
]
}

View file

@ -0,0 +1,23 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# fmt: off
from typing import Sequence
class Item:
def __init__(self, n: int) -> None:
self.number = n
class ItemCollector:
def get_items(self, n: int) -> Sequence[Item]:
return [Item(i) for i in range(n)]
collector = ItemCollector()
items = collector.get_items(3)
for item in items:
item.number

View file

@ -0,0 +1,183 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
import json
import subprocess
from pathlib import Path
from typing import Dict, List, Mapping, Optional, Sequence, Tuple, Union
from mypy_extensions import TypedDict
import libcst as cst
from libcst.metadata import MetadataWrapper, PositionProvider
from libcst.testing.utils import UnitTest, data_provider
TEST_SUITE_PATH: Path = Path(__file__).parent / "pyre"
class TypeVerificationVisitor(cst.CSTVisitor):
METADATA_DEPENDENCIES = (PositionProvider,)
def __init__(
self, lookup: Mapping[Tuple[int, int, int, int], str], test: UnitTest
) -> None:
self.lookup = lookup
self.test = test
self.attributes: List[cst.Attribute] = [] # stack of Attribute
self.imports: List[Union[cst.Import, cst.ImportFrom]] = [] # stack of imports
self.annotations: List[cst.Annotation] = [] # stack of Annotation
super().__init__()
def visit_Attribute(self, node: cst.Attribute) -> Optional[bool]:
pos = self.get_metadata(PositionProvider, node)
start = pos.start
end = pos.end
self.attributes.append(node)
tup = (start.line, start.column, end.line, end.column)
# remove this if condition when the type issues are fixed.
if not any(
node.deep_equals(name)
for name in {
cst.Attribute(cst.Name("self"), attr=cst.Name("number")),
cst.Attribute(cst.Name("item"), attr=cst.Name("number")),
}
):
self.test.assertIn(
tup,
self.lookup,
f"Attribute node {node} at {tup} found without inferred type.",
)
def leave_Attribute(self, original_node: cst.Attribute) -> None:
self.attributes.pop()
def visit_Name(self, node: cst.Name) -> Optional[bool]:
if (
len(self.imports) > 0
or len(self.attributes) > 0
or len(self.annotations) > 0
):
return
pos = self.get_metadata(PositionProvider, node)
start = pos.start
end = pos.end
tup = (start.line, start.column, end.line, end.column)
# remove this if condition when the type issues are fixed.
if not any(
node.deep_equals(name) and tup == _tup
for (name, _tup) in {(cst.Name("i"), (17, 21, 17, 22)),}
):
self.test.assertIn(
tup,
self.lookup,
f"Name node {node.value} at {tup} found without inferred type.",
)
def visit_Import(self, node: cst.Import) -> Optional[bool]:
self.imports.append(node)
def leave_Import(self, original_node: cst.Import) -> None:
self.imports.pop()
def visit_ImportFrom(self, node: cst.ImportFrom) -> Optional[bool]:
self.imports.append(node)
def leave_ImportFrom(self, original_node: cst.ImportFrom) -> None:
self.imports.pop()
def visit_Annotation(self, node: cst.Annotation) -> Optional[bool]:
self.annotations.append(node)
def leave_Annotation(self, original_node: cst.Annotation) -> None:
self.annotations.pop()
class PyreIntegrationTest(UnitTest):
@data_provider(
(
(source_path, data_path)
for source_path, data_path in zip(
TEST_SUITE_PATH.glob("*.py"), TEST_SUITE_PATH.glob("*.json")
)
)
)
def test_type_availability(self, source_path: Path, data_path: Path) -> None:
module = cst.parse_module(source_path.read_text())
data: PyreData = json.loads(data_path.read_text())
lookup: Dict[Tuple[int, int, int, int], str] = {}
for t in data["types"]:
loc = t["location"]
start = loc["start"]
stop = loc["stop"]
lookup[(start["line"], start["column"], stop["line"], stop["column"])] = t[
"annotation"
]
MetadataWrapper(module).visit(TypeVerificationVisitor(lookup, self))
def _run_command(command: str) -> Tuple[str, str, int]:
process = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
)
stdout, stderr = process.communicate()
return stdout.decode(), stderr.decode(), process.returncode
class Position(TypedDict):
line: int
column: int
class Location(TypedDict):
path: str
start: Position
stop: Position
class InferredType(TypedDict):
location: Location
annotation: str
class PyreData(TypedDict):
types: Sequence[InferredType]
def _sort_by_position(data: InferredType) -> Tuple[int, int, int, int]:
start = data["location"]["start"]
stop = data["location"]["stop"]
return (start["line"], start["column"], stop["line"], stop["column"])
if __name__ == "__main__":
"""Run this script directly to generate pyre data for test suite (tests/pyre/*.py)
"""
print("start pyre server")
stdout: str
stderr: str
return_code: int
stdout, stderr, return_code = _run_command("pyre")
if return_code != 0:
print(stdout)
print(stderr)
for path in (TEST_SUITE_PATH).glob("*.py"):
cmd = f'''pyre query "types(path='{path}')"'''
print(cmd)
stdout, stderr, return_code = _run_command(cmd)
if return_code != 0:
print(stdout)
print(stderr)
data = json.loads(stdout)
data = data["response"][0]
del data["path"]
data["types"].sort(key=_sort_by_position)
output_path = path.with_suffix(".json")
print(f"write output to {output_path}")
output_path.write_text(json.dumps(data, indent=2))

View file

@ -8,6 +8,6 @@ git+https://github.com/jimmylai/sphinx.git@slots_type_annotation
isort==4.3.20
jupyter==1.0.0
nbsphinx==0.4.2
pyre-check==0.0.36
pyre-check==0.0.38
sphinx-rtd-theme==0.4.3
prompt-toolkit==2.0.9