Skip to content

Commit ad4d845

Browse files
grievejiafacebook-github-bot
authored andcommitted
Implement pyre infer (7/n) -- raw infer output to module annotations
Reviewed By: dkgi Differential Revision: D30169045 fbshipit-source-id: 47fe64d8c6b036300241817e25455a2d1c633ceb
1 parent cb756bd commit ad4d845

File tree

2 files changed

+315
-0
lines changed

2 files changed

+315
-0
lines changed

client/commands/v2/infer.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,154 @@ class RawInferOutputParsingError(Exception):
211211
pass
212212

213213

214+
@dataclasses.dataclass(frozen=True)
215+
class StubGenerationOptions:
216+
annotate_attributes: bool
217+
use_future_annotations: bool
218+
dequalify: bool
219+
220+
221+
@dataclasses.dataclass(frozen=True)
222+
class TypeAnnotation:
223+
annotation: Optional[str]
224+
dequalify: bool
225+
226+
@staticmethod
227+
def from_raw(
228+
annotation: Optional[str], options: StubGenerationOptions
229+
) -> "TypeAnnotation":
230+
return TypeAnnotation(annotation=annotation, dequalify=options.dequalify)
231+
232+
@property
233+
def missing(self) -> bool:
234+
return self.annotation is None
235+
236+
237+
@dataclasses.dataclass(frozen=True)
238+
class Parameter:
239+
name: str
240+
annotation: TypeAnnotation
241+
value: Optional[str]
242+
243+
244+
@dataclasses.dataclass(frozen=True)
245+
class FunctionAnnotation:
246+
name: str
247+
return_annotation: TypeAnnotation
248+
parameters: Sequence[Parameter]
249+
decorators: Sequence[str]
250+
is_async: bool
251+
252+
253+
@dataclasses.dataclass(frozen=True)
254+
class MethodAnnotation(FunctionAnnotation):
255+
parent: str
256+
257+
258+
@dataclasses.dataclass(frozen=True)
259+
class FieldAnnotation:
260+
name: str
261+
annotation: TypeAnnotation
262+
263+
264+
@dataclasses.dataclass(frozen=True)
265+
class GlobalAnnotation(FieldAnnotation):
266+
pass
267+
268+
269+
@dataclasses.dataclass(frozen=True)
270+
class AttributeAnnotation(FieldAnnotation):
271+
parent: str
272+
273+
274+
@dataclasses.dataclass(frozen=True)
275+
class ModuleAnnotations:
276+
path: str
277+
options: StubGenerationOptions
278+
globals_: List[GlobalAnnotation] = dataclasses.field(default_factory=list)
279+
attributes: List[AttributeAnnotation] = dataclasses.field(default_factory=list)
280+
functions: List[FunctionAnnotation] = dataclasses.field(default_factory=list)
281+
methods: List[MethodAnnotation] = dataclasses.field(default_factory=list)
282+
283+
@staticmethod
284+
def from_infer_output(
285+
path: str,
286+
infer_output: RawInferOutput,
287+
options: StubGenerationOptions,
288+
) -> "ModuleAnnotations":
289+
def type_annotation(annotation: Optional[str]) -> TypeAnnotation:
290+
return TypeAnnotation.from_raw(
291+
annotation,
292+
options=options,
293+
)
294+
295+
return ModuleAnnotations(
296+
path=path,
297+
globals_=[
298+
GlobalAnnotation(
299+
name=global_.name, annotation=type_annotation(global_.annotation)
300+
)
301+
for global_ in infer_output.global_annotations
302+
],
303+
attributes=[
304+
AttributeAnnotation(
305+
parent=attribute.parent,
306+
name=attribute.name,
307+
annotation=type_annotation(attribute.annotation),
308+
)
309+
for attribute in infer_output.attribute_annotations
310+
]
311+
if options.annotate_attributes
312+
else [],
313+
functions=[
314+
FunctionAnnotation(
315+
name=define.name,
316+
return_annotation=type_annotation(define.return_),
317+
parameters=[
318+
Parameter(
319+
name=parameter.name,
320+
annotation=type_annotation(parameter.annotation),
321+
value=parameter.value,
322+
)
323+
for parameter in define.parameters
324+
],
325+
decorators=define.decorators,
326+
is_async=define.is_async,
327+
)
328+
for define in infer_output.define_annotations
329+
if define.parent is None
330+
],
331+
methods=[
332+
MethodAnnotation(
333+
parent=define.parent,
334+
name=define.name,
335+
return_annotation=type_annotation(define.return_),
336+
parameters=[
337+
Parameter(
338+
name=parameter.name,
339+
annotation=type_annotation(parameter.annotation),
340+
value=parameter.value,
341+
)
342+
for parameter in define.parameters
343+
],
344+
decorators=define.decorators,
345+
is_async=define.is_async,
346+
)
347+
for define in infer_output.define_annotations
348+
if define.parent is not None
349+
],
350+
options=options,
351+
)
352+
353+
def is_empty(self) -> bool:
354+
return (
355+
len(self.globals_)
356+
+ len(self.attributes)
357+
+ len(self.functions)
358+
+ len(self.methods)
359+
) == 0
360+
361+
214362
def create_infer_arguments(
215363
configuration: configuration_module.Configuration,
216364
infer_arguments: command_arguments.InferArguments,

client/commands/v2/tests/infer_test.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
RawDefineAnnotation,
2525
RawInferOutput,
2626
RawInferOutputParsingError,
27+
StubGenerationOptions,
28+
TypeAnnotation,
29+
FunctionAnnotation,
30+
MethodAnnotation,
31+
GlobalAnnotation,
32+
AttributeAnnotation,
33+
ModuleAnnotations,
2734
)
2835

2936

@@ -332,3 +339,163 @@ def assert_not_parsed(input: str) -> None:
332339
]
333340
),
334341
)
342+
343+
344+
class ModuleAnnotationTest(testslide.TestCase):
345+
def test_module_annotations_from_infer_output(self) -> None:
346+
def assert_result(
347+
path: str,
348+
infer_output: RawInferOutput,
349+
options: StubGenerationOptions,
350+
expected: ModuleAnnotations,
351+
) -> None:
352+
self.assertEqual(
353+
ModuleAnnotations.from_infer_output(path, infer_output, options),
354+
expected,
355+
)
356+
357+
default_path = "test.py"
358+
default_options = StubGenerationOptions(
359+
annotate_attributes=False, use_future_annotations=False, dequalify=False
360+
)
361+
362+
assert_result(
363+
path=default_path,
364+
infer_output=RawInferOutput(),
365+
options=default_options,
366+
expected=ModuleAnnotations(path=default_path, options=default_options),
367+
)
368+
369+
assert_result(
370+
path=default_path,
371+
infer_output=RawInferOutput(
372+
define_annotations=[
373+
RawDefineAnnotation(
374+
name="test.Foo.foo",
375+
parent="test.Foo",
376+
location=RawAnnotationLocation(
377+
qualifier="test", path="test.py", line=1
378+
),
379+
return_="int",
380+
decorators=["derp"],
381+
is_async=True,
382+
),
383+
RawDefineAnnotation(
384+
name="test.bar",
385+
location=RawAnnotationLocation(
386+
qualifier="test", path="test.py", line=2
387+
),
388+
),
389+
],
390+
),
391+
options=default_options,
392+
expected=ModuleAnnotations(
393+
path=default_path,
394+
options=default_options,
395+
functions=[
396+
FunctionAnnotation(
397+
name="test.bar",
398+
return_annotation=TypeAnnotation.from_raw(
399+
None, options=default_options
400+
),
401+
parameters=[],
402+
decorators=[],
403+
is_async=False,
404+
)
405+
],
406+
methods=[
407+
MethodAnnotation(
408+
parent="test.Foo",
409+
name="test.Foo.foo",
410+
return_annotation=TypeAnnotation.from_raw(
411+
"int", options=default_options
412+
),
413+
parameters=[],
414+
decorators=["derp"],
415+
is_async=True,
416+
)
417+
],
418+
),
419+
)
420+
421+
assert_result(
422+
path=default_path,
423+
infer_output=RawInferOutput(
424+
global_annotations=[
425+
RawGlobalAnnotation(
426+
name="x",
427+
location=RawAnnotationLocation(
428+
qualifier="test", path="test.py", line=3
429+
),
430+
annotation="int",
431+
)
432+
],
433+
),
434+
options=default_options,
435+
expected=ModuleAnnotations(
436+
path=default_path,
437+
options=default_options,
438+
globals_=[
439+
GlobalAnnotation(
440+
name="x",
441+
annotation=TypeAnnotation.from_raw(
442+
"int", options=default_options
443+
),
444+
)
445+
],
446+
),
447+
)
448+
449+
assert_result(
450+
path=default_path,
451+
infer_output=RawInferOutput(
452+
attribute_annotations=[
453+
RawAttributeAnnotation(
454+
parent="Foo",
455+
name="x",
456+
location=RawAnnotationLocation(
457+
qualifier="test", path="test.py", line=3
458+
),
459+
annotation="int",
460+
)
461+
],
462+
),
463+
options=default_options,
464+
expected=ModuleAnnotations(
465+
path=default_path,
466+
options=default_options,
467+
),
468+
)
469+
470+
annotate_attribute_options = StubGenerationOptions(
471+
annotate_attributes=True, use_future_annotations=False, dequalify=False
472+
)
473+
assert_result(
474+
path=default_path,
475+
infer_output=RawInferOutput(
476+
attribute_annotations=[
477+
RawAttributeAnnotation(
478+
parent="Foo",
479+
name="x",
480+
location=RawAnnotationLocation(
481+
qualifier="test", path="test.py", line=3
482+
),
483+
annotation="int",
484+
)
485+
],
486+
),
487+
options=annotate_attribute_options,
488+
expected=ModuleAnnotations(
489+
path=default_path,
490+
options=annotate_attribute_options,
491+
attributes=[
492+
AttributeAnnotation(
493+
parent="Foo",
494+
name="x",
495+
annotation=TypeAnnotation.from_raw(
496+
"int", options=annotate_attribute_options
497+
),
498+
)
499+
],
500+
),
501+
)

0 commit comments

Comments
 (0)