Skip to content

Commit cb756bd

Browse files
grievejiafacebook-github-bot
authored andcommitted
Implement pyre infer (6/n) -- JSON deserialization of raw infer output
Reviewed By: dkgi Differential Revision: D30169046 fbshipit-source-id: ba93ed2aa70ff1423858f0740b1ddb7e6efad7d2
1 parent 445223c commit cb756bd

File tree

2 files changed

+251
-2
lines changed

2 files changed

+251
-2
lines changed

client/commands/v2/infer.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from pathlib import Path
1414
from typing import Any, Dict, Iterator, List, Optional, Sequence
1515

16+
import dataclasses_json
17+
1618
from ... import commands, command_arguments, configuration as configuration_module
1719
from . import remote_logging, backend_arguments, start
1820

@@ -110,6 +112,105 @@ def serialize(self) -> Dict[str, Any]:
110112
}
111113

112114

115+
@dataclasses_json.dataclass_json(
116+
letter_case=dataclasses_json.LetterCase.CAMEL,
117+
undefined=dataclasses_json.Undefined.EXCLUDE,
118+
)
119+
@dataclasses.dataclass(frozen=True)
120+
class RawAnnotationLocation:
121+
qualifier: str
122+
path: str
123+
line: int
124+
125+
126+
@dataclasses_json.dataclass_json(
127+
letter_case=dataclasses_json.LetterCase.CAMEL,
128+
undefined=dataclasses_json.Undefined.EXCLUDE,
129+
)
130+
@dataclasses.dataclass(frozen=True)
131+
class RawGlobalAnnotation:
132+
name: str
133+
location: RawAnnotationLocation
134+
annotation: str
135+
136+
137+
@dataclasses_json.dataclass_json(
138+
letter_case=dataclasses_json.LetterCase.CAMEL,
139+
undefined=dataclasses_json.Undefined.EXCLUDE,
140+
)
141+
@dataclasses.dataclass(frozen=True)
142+
class RawAttributeAnnotation:
143+
parent: str
144+
name: str
145+
location: RawAnnotationLocation
146+
annotation: str
147+
148+
149+
@dataclasses_json.dataclass_json(
150+
letter_case=dataclasses_json.LetterCase.CAMEL,
151+
undefined=dataclasses_json.Undefined.EXCLUDE,
152+
)
153+
@dataclasses.dataclass(frozen=True)
154+
class RawParameter:
155+
name: str
156+
index: int
157+
annotation: Optional[str] = None
158+
value: Optional[str] = None
159+
160+
161+
@dataclasses_json.dataclass_json(
162+
letter_case=dataclasses_json.LetterCase.CAMEL,
163+
undefined=dataclasses_json.Undefined.EXCLUDE,
164+
)
165+
@dataclasses.dataclass(frozen=True)
166+
class RawDefineAnnotation:
167+
name: str
168+
location: RawAnnotationLocation
169+
parent: Optional[str] = None
170+
return_: Optional[str] = dataclasses.field(
171+
metadata=dataclasses_json.config(field_name="return"), default=None
172+
)
173+
parameters: List[RawParameter] = dataclasses.field(default_factory=list)
174+
decorators: List[str] = dataclasses.field(default_factory=list)
175+
is_async: bool = dataclasses.field(
176+
metadata=dataclasses_json.config(field_name="async"), default=False
177+
)
178+
179+
180+
@dataclasses_json.dataclass_json(
181+
letter_case=dataclasses_json.LetterCase.CAMEL,
182+
undefined=dataclasses_json.Undefined.EXCLUDE,
183+
)
184+
@dataclasses.dataclass(frozen=True)
185+
class RawInferOutput:
186+
global_annotations: List[RawGlobalAnnotation] = dataclasses.field(
187+
metadata=dataclasses_json.config(field_name="globals"), default_factory=list
188+
)
189+
attribute_annotations: List[RawAttributeAnnotation] = dataclasses.field(
190+
metadata=dataclasses_json.config(field_name="attributes"), default_factory=list
191+
)
192+
define_annotations: List[RawDefineAnnotation] = dataclasses.field(
193+
metadata=dataclasses_json.config(field_name="defines"), default_factory=list
194+
)
195+
196+
@staticmethod
197+
def create(input: str) -> "RawInferOutput":
198+
try:
199+
# pyre-fixme[16]: Pyre doesn't understand `dataclasses_json`
200+
return RawInferOutput.schema().loads(input)
201+
except (
202+
TypeError,
203+
KeyError,
204+
ValueError,
205+
dataclasses_json.mm.ValidationError,
206+
) as error:
207+
raise RawInferOutputParsingError(str(error)) from error
208+
209+
210+
class RawInferOutputParsingError(Exception):
211+
pass
212+
213+
113214
def create_infer_arguments(
114215
configuration: configuration_module.Configuration,
115216
infer_arguments: command_arguments.InferArguments,

client/commands/v2/tests/infer_test.py

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,28 @@
33
# This source code is licensed under the MIT license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
import json
67
import tempfile
78
from pathlib import Path
8-
from typing import Iterable, Tuple
9+
from typing import Iterable, Tuple, Dict
910

1011
import testslide
1112

1213
from .... import configuration, command_arguments
1314
from ....tests import setup
1415
from .. import backend_arguments
15-
from ..infer import Arguments, InferMode, create_infer_arguments
16+
from ..infer import (
17+
Arguments,
18+
InferMode,
19+
create_infer_arguments,
20+
RawAnnotationLocation,
21+
RawGlobalAnnotation,
22+
RawAttributeAnnotation,
23+
RawParameter,
24+
RawDefineAnnotation,
25+
RawInferOutput,
26+
RawInferOutputParsingError,
27+
)
1628

1729

1830
class ArgumentTest(testslide.TestCase):
@@ -184,3 +196,139 @@ def test_create_infer_arguments(self) -> None:
184196
),
185197
),
186198
)
199+
200+
def test_parse_raw_infer_output(self) -> None:
201+
def assert_parsed(input: Dict[str, object], expected: RawInferOutput) -> None:
202+
self.assertEqual(RawInferOutput.create(json.dumps(input)), expected)
203+
204+
def assert_not_parsed(input: str) -> None:
205+
with self.assertRaises(RawInferOutputParsingError):
206+
RawInferOutput.create(input)
207+
208+
assert_not_parsed("")
209+
assert_not_parsed("[]")
210+
assert_not_parsed("42")
211+
assert_not_parsed('"abc"')
212+
213+
assert_parsed({}, RawInferOutput())
214+
assert_parsed({"irrelevant": 42}, RawInferOutput())
215+
assert_parsed(
216+
{
217+
"globals": [
218+
{
219+
"name": "x",
220+
"location": {"qualifier": "test", "path": "test.py", "line": 4},
221+
"annotation": "int",
222+
}
223+
]
224+
},
225+
RawInferOutput(
226+
global_annotations=[
227+
RawGlobalAnnotation(
228+
name="x",
229+
location=RawAnnotationLocation(
230+
qualifier="test", path="test.py", line=4
231+
),
232+
annotation="int",
233+
)
234+
]
235+
),
236+
)
237+
assert_parsed(
238+
{
239+
"attributes": [
240+
{
241+
"parent": "Foo",
242+
"name": "x",
243+
"location": {"qualifier": "test", "path": "test.py", "line": 3},
244+
"annotation": "int",
245+
}
246+
]
247+
},
248+
RawInferOutput(
249+
attribute_annotations=[
250+
RawAttributeAnnotation(
251+
parent="Foo",
252+
name="x",
253+
location=RawAnnotationLocation(
254+
qualifier="test", path="test.py", line=3
255+
),
256+
annotation="int",
257+
)
258+
]
259+
),
260+
)
261+
assert_parsed(
262+
{
263+
"defines": [
264+
{
265+
"name": "test.foo",
266+
"parent": None,
267+
"return": None,
268+
"parameters": [],
269+
"decorators": [],
270+
"location": {"qualifier": "test", "path": "test.py", "line": 1},
271+
"async": False,
272+
}
273+
]
274+
},
275+
RawInferOutput(
276+
define_annotations=[
277+
RawDefineAnnotation(
278+
name="test.foo",
279+
location=RawAnnotationLocation(
280+
qualifier="test", path="test.py", line=1
281+
),
282+
is_async=False,
283+
)
284+
]
285+
),
286+
)
287+
assert_parsed(
288+
{
289+
"defines": [
290+
{
291+
"name": "test.Foo.foo",
292+
"parent": "test.Foo",
293+
"return": "int",
294+
"parameters": [
295+
{
296+
"name": "self",
297+
"annotation": None,
298+
"value": None,
299+
"index": 0,
300+
},
301+
{
302+
"name": "x",
303+
"annotation": "int",
304+
"value": "42",
305+
"index": 1,
306+
},
307+
],
308+
"decorators": ["derp"],
309+
"location": {"qualifier": "test", "path": "test.py", "line": 1},
310+
"async": True,
311+
}
312+
]
313+
},
314+
RawInferOutput(
315+
define_annotations=[
316+
RawDefineAnnotation(
317+
name="test.Foo.foo",
318+
parent="test.Foo",
319+
location=RawAnnotationLocation(
320+
qualifier="test", path="test.py", line=1
321+
),
322+
return_="int",
323+
parameters=[
324+
RawParameter(name="self", index=0),
325+
RawParameter(
326+
name="x", index=1, annotation="int", value="42"
327+
),
328+
],
329+
decorators=["derp"],
330+
is_async=True,
331+
)
332+
]
333+
),
334+
)

0 commit comments

Comments
 (0)