Skip to content

Commit 5b2dc7a

Browse files
martindemellometa-codesync[bot]
authored andcommitted
Use typed dicts when working with pyrefly json info
Summary: Restores typed dict code after merge issues Reviewed By: DinoV Differential Revision: D96209580 fbshipit-source-id: fb6df0661da31ed102b222df99d28ea43ce38fd2
1 parent 4a0ac41 commit 5b2dc7a

3 files changed

Lines changed: 70 additions & 127 deletions

File tree

cinderx/PythonLib/cinderx/compiler/static/pyrefly_info.py

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,67 @@
77
import json
88
import os
99
from ast import AST
10+
from dataclasses import dataclass
11+
from typing import TypedDict
12+
13+
14+
class Location(TypedDict):
15+
start_line: int
16+
start_col: int
17+
end_line: int
18+
end_col: int
19+
20+
21+
TypeKind = int
22+
23+
24+
class LocationEntry(TypedDict):
25+
loc: LocationInfo
26+
type: TypeKind
27+
28+
29+
class TypeInfo(TypedDict):
30+
type_table: list[TypeTableEntry]
31+
locations: list[LocationEntry]
32+
33+
34+
class TypeTableEntry(TypedDict):
35+
kind: str
36+
qname: object
37+
38+
39+
@dataclass(slots=True, eq=True)
40+
class LocationInfo:
41+
start_line: int
42+
start_col: int
43+
end_line: int
44+
end_col: int
45+
46+
def __hash__(self) -> int:
47+
return (
48+
self.start_col
49+
^ self.start_line << 14
50+
^ self.end_col << 7
51+
^ self.end_line << 32
52+
)
53+
54+
@classmethod
55+
def from_location(cls, loc: Location):
56+
return cls(
57+
start_line=loc["start_line"],
58+
start_col=loc["start_col"],
59+
end_line=loc["end_line"],
60+
end_col=loc["end_col"],
61+
)
62+
63+
@classmethod
64+
def from_node(cls, node: AST):
65+
return cls(
66+
start_line=node.lineno, # pyre-ignore[16]
67+
start_col=node.col_offset, # pyre-ignore[16]
68+
end_line=node.end_lineno, # pyre-ignore[16]
69+
end_col=node.end_col_offset, # pyre-ignore[16]
70+
)
1071

1172

1273
class PyreflyTypeInfo:
@@ -21,17 +82,11 @@ class PyreflyTypeInfo:
2182
Python AST conventions.
2283
"""
2384

24-
def __init__(self, data: dict[str, object]) -> None:
25-
self._type_table: list[dict[str, object]] = data["type_table"]
26-
self._locations: dict[tuple[int, int, int, int], int] = {}
85+
def __init__(self, data: TypeInfo) -> None:
86+
self._type_table: list[TypeTableEntry] = data["type_table"]
87+
self._locations: dict[LocationInfo, int] = {}
2788
for entry in data["locations"]:
28-
loc = entry["loc"]
29-
key = (
30-
loc["start_line"],
31-
loc["start_col"],
32-
loc["end_line"],
33-
loc["end_col"],
34-
)
89+
key = LocationInfo.from_location(entry["loc"])
3590
self._locations[key] = entry["type"]
3691

3792
def _type_to_str(self, type_index: int) -> str:
@@ -63,12 +118,7 @@ def _type_to_str(self, type_index: int) -> str:
63118

64119
def _lookup(self, node: AST) -> int | None:
65120
"""Look up the type_table index for an AST node by its source position."""
66-
key = (
67-
node.lineno, # pyre-ignore[16]
68-
node.col_offset, # pyre-ignore[16]
69-
node.end_lineno, # pyre-ignore[16]
70-
node.end_col_offset, # pyre-ignore[16]
71-
)
121+
key = LocationInfo.from_node(node)
72122
return self._locations.get(key)
73123

74124
def lookup(self, node: AST) -> str:

cinderx/PythonLib/cinderx/compiler/static/pyrefly_type_binder.py

Lines changed: 2 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -6,61 +6,20 @@
66

77
import ast
88
from ast import AST, Name
9-
from dataclasses import dataclass
10-
from typing import TYPE_CHECKING, TypedDict
9+
from typing import TYPE_CHECKING
1110

1211
from ..errors import TypedSyntaxError
1312
from ..symbols import SymbolVisitor
1413
from .effects import NarrowingEffect
1514
from .module_table import ModuleTable
1615
from .pyrefly_info import PyreflyTypeInfo
1716
from .type_binder import TypeBinder
18-
from .types import Class, Value
17+
from .types import Class
1918

2019
if TYPE_CHECKING:
2120
from .compiler import Compiler
2221

2322

24-
class Location(TypedDict):
25-
start_line: int
26-
start_col: int
27-
end_line: int
28-
end_col: int
29-
30-
31-
TypeKind = int
32-
33-
34-
class LocationEntry(TypedDict):
35-
loc: LocationInfo
36-
type: TypeKind
37-
38-
39-
class TypeInfo(TypedDict):
40-
type_table: list[TypeTable]
41-
locations: list[LocationEntry]
42-
43-
44-
class TypeTable(TypedDict):
45-
kind: str
46-
qname: object
47-
48-
49-
@dataclass(slots=True, eq=True)
50-
class LocationInfo:
51-
start_line: int
52-
start_col: int
53-
end_line: int
54-
end_col: int
55-
56-
def __hash__(self) -> int:
57-
return (
58-
self.start_col
59-
^ self.start_line << 14
60-
^ self.end_col << 7
61-
^ self.end_line << 32
62-
)
63-
6423
def _resolve_classname(qname: str, modules: dict[str, ModuleTable]) -> Class | None:
6524
"""Resolve a dotted qname like 'builtins.int' to a Class.
6625
@@ -100,72 +59,6 @@ def _resolve_classname(qname: str, modules: dict[str, ModuleTable]) -> Class | N
10059
return None
10160

10261

103-
class PyreflyTypeInfo:
104-
"""Loads and indexes a pyrefly type trace JSON file.
105-
106-
The JSON file contains:
107-
- type_table: array of type entries (class, literal, callable)
108-
- locations: array of {loc: {start_line, start_col, end_line, end_col}, type: index}
109-
110-
Locations map source positions to type_table indices. Positions use
111-
1-based lines and 0-based columns (end_col is exclusive), matching
112-
Python AST conventions.
113-
"""
114-
115-
def __init__(self, type_info: TypeInfo) -> None:
116-
self._type_table: list[TypeTable] = type_info["type_table"]
117-
self._locations: dict[LocationInfo, TypeKind] = {}
118-
for entry in type_info["locations"]:
119-
loc = entry["loc"]
120-
key = LocationInfo(
121-
loc["start_line"],
122-
loc["start_col"],
123-
loc["end_line"],
124-
loc["end_col"],
125-
)
126-
self._locations[key] = entry["type"]
127-
128-
def _type_to_str(self, type_index: int) -> str:
129-
"""Convert a type_table entry to a Python annotation string."""
130-
entry = self._type_table[type_index]
131-
kind = entry["kind"]
132-
if kind == "literal":
133-
return ""
134-
elif kind == "class":
135-
qname = str(entry["qname"])
136-
args = entry.get("args", [])
137-
assert isinstance(args, list)
138-
if not args:
139-
return qname
140-
arg_strs = [self._type_to_str(a) for a in args]
141-
if any(not s for s in arg_strs):
142-
return qname
143-
return f"{qname}[{', '.join(arg_strs)}]"
144-
elif kind == "callable":
145-
params = entry.get("params", [])
146-
assert isinstance(params, list)
147-
ret = entry.get("return_type")
148-
param_strs = [self._type_to_str(p) for p in params]
149-
ret_str = self._type_to_str(ret) if isinstance(ret, int) else ""
150-
if any(not s for s in param_strs) or not ret_str:
151-
return ""
152-
return f"Callable[[{', '.join(param_strs)}], {ret_str}]"
153-
return ""
154-
155-
def lookup(self, node: AST) -> str:
156-
"""Look up the type string for an AST node by its source position."""
157-
key = LocationInfo(
158-
node.lineno, # pyre-ignore[16]
159-
node.col_offset, # pyre-ignore[16]
160-
node.end_lineno, # pyre-ignore[16]
161-
node.end_col_offset, # pyre-ignore[16]
162-
)
163-
type_index = self._locations.get(key)
164-
if type_index is None:
165-
return ""
166-
return self._type_to_str(type_index)
167-
168-
16962
class PyreflyTypeBinder(TypeBinder):
17063
"""TypeBinder that uses pyrefly type inference to set types on expression nodes.
17164

cinderx/PythonLib/test_cinderx/test_compiler/test_static/pyreflybinder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def test_simple(self):
1616
code = self.compile_one("x = 1 + 2")
1717

1818
def test_force_static(self):
19-
compiler = PyreflyCompiler(non_static_modules=set())
19+
compiler = PyreflyCompiler(static_opt_out=set())
2020
code, strict, static = compiler.load_compiled_module_from_source(
2121
"def f(x: int): return x.is_integer()", "foo.py", "foo", 0
2222
)
@@ -25,7 +25,7 @@ def test_force_static(self):
2525
self.assertInBytecode(self.find_code(code, "f"), "INVOKE_METHOD")
2626

2727
def test_non_force_static(self):
28-
compiler = PyreflyCompiler(non_static_modules={"foo"})
28+
compiler = PyreflyCompiler(static_opt_out={"foo"})
2929
code, strict, static = compiler.load_compiled_module_from_source(
3030
"def f(x: int): return x.is_integer()", "foo.py", "foo", 0
3131
)

0 commit comments

Comments
 (0)