mirror of
https://github.com/Instagram/LibCST.git
synced 2025-12-23 10:35:53 +00:00
Merge branch 'pyre_test_suite'
This commit is contained in:
commit
bf0bfd40db
4 changed files with 701 additions and 1 deletions
494
libcst/tests/pyre/simple_class.json
Normal file
494
libcst/tests/pyre/simple_class.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
libcst/tests/pyre/simple_class.py
Normal file
23
libcst/tests/pyre/simple_class.py
Normal 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
|
||||
183
libcst/tests/test_pyre_integration.py
Normal file
183
libcst/tests/test_pyre_integration.py
Normal 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))
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue