tests.test_typing.test_get_from_imports()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nop 1
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
1
from typing import Iterable
2
from unittest.mock import patch
3
4
import pytest
5
6
from menderbot import python_cst
7
from menderbot.__main__ import try_function_type_hints
8
from menderbot.code import LanguageStrategy, PythonLanguageStrategy, node_str
9
from menderbot.source_file import Insertion, SourceFile, insert_in_lines
10
from menderbot.typing import add_type_hints, parse_type_hint_answer, what_needs_typing
11
12
13
@pytest.fixture
14
def py_strat():
15
    return PythonLanguageStrategy()
16
17
18
def parse_string_to_tree(str, lang_strat: LanguageStrategy):
19
    source_bytes = bytes(str, "utf-8")
20
    return lang_strat.parse_source_to_tree(source_bytes)
21
22
23
def test_add_type_hints(py_strat):
24
    code = """
25
def foo(a):
26
    pass
27
"""
28
    code_lines = code.splitlines(True)
29
    expected_lines = ["\n", "def foo(a: int) -> None:\n", "    pass\n"]
30
    expected = [
31
        Insertion(text=": int", line_number=2, col=10, inline=True, label="foo"),
32
        Insertion(text=" -> None", line_number=2, col=11, inline=True, label="foo"),
33
    ]
34
    fn_asts = python_cst.collect_function_asts(code)
35
    hints = [("a", "int"), ("return", "None")]
36
    insertions = add_type_hints(fn_asts[0], hints, [])
37
38
    sig_ast = fn_asts[0].children_filtered(kind=python_cst.KIND_SIGNATURE)[0]
39
    # print(code)
40
    # print(sig_ast.text)
41
    # print(sig_ast.src_range)
42
    # print(python_cst.to_json(fn_asts))
43
    assert sig_ast.src_range.end.col == 11
44
45
    assert insertions == expected
46
    assert list(insert_in_lines(code_lines, insertions)) == expected_lines
47
48
49
def test_add_type_hints_on_first_line(py_strat):
50
    code = """def foo(a):
51
    pass
52
"""
53
    expected = [
54
        Insertion(text=": int", line_number=1, col=10, inline=True, label="foo"),
55
        Insertion(text=" -> None", line_number=1, col=11, inline=True, label="foo"),
56
    ]
57
    fn_asts = python_cst.collect_function_asts(code)
58
    hints = [("a", "int"), ("return", "None")]
59
    insertions = add_type_hints(fn_asts[0], hints, [])
60
61
    assert insertions == expected
62
63
64
def test_parse_type_hint_answer():
65
    assert parse_type_hint_answer("a : int\nreturn : Any\n") == [("a", "int")]
66
67
68
def test_process_untyped_functions_one_result(py_strat):
69
    code = """def foo(a):
70
    pass"""
71
    fn_asts = python_cst.collect_function_asts(code)
72
    needs_typing = what_needs_typing(fn_asts[0])
73
    assert needs_typing == ["a", "return"]
74
75
76
def test_process_untyped_functions_excludes_init(py_strat):
77
    code = """def __init__(a):
78
    pass"""
79
    fn_asts = python_cst.collect_function_asts(code)
80
    needs_typing = what_needs_typing(fn_asts[0])
81
    assert needs_typing == ["a"]
82
83
84
class MockSourceFile(SourceFile):
85
    def __init__(self):
86
        self.path = ""
87
88
    def load_source_as_utf8(self):
89
        return ""
90
91
    def update_file(self, insertions: Iterable[Insertion], suffix: str) -> None:
92
        pass
93
94
95
def test_try_function_type_hints(py_strat):
96
    code = """
97
def foo(a):
98
    pass
99
    """
100
    fn_asts = python_cst.collect_function_asts(code)
101
    with patch(
102
        "menderbot.check.run_check",
103
        side_effect=lambda _: True,
104
    ):
105
        source_file = MockSourceFile()
106
        no_hints = try_function_type_hints("..mypy..", source_file, fn_asts[0], [])
107
        assert no_hints == []
108
        one_hint_no_results = try_function_type_hints(
109
            "..mypy..", source_file, fn_asts[0], ["a"]
110
        )
111
        assert one_hint_no_results == []
112
113
114
def test_indented_function(py_strat):
115
    """
116
    Currently a function too deeply indented will not parse as a function.
117
    This test documents that, in case we decide to change it.
118
    """
119
    code = """\n    def foo(a):\n        pass"""
120
    tree = parse_string_to_tree(
121
        code,
122
        py_strat,
123
    )
124
125
    function_nodes = py_strat.get_function_nodes(tree)
126
127
    assert len(function_nodes) == 0
128
129
130
def test_get_from_imports(py_strat):
131
    code = """
132
from typing import Foo, Bar
133
from typing import Baz
134
from otherlib import Quux
135
def foo(a):
136
    pass
137
"""
138
    tree = parse_string_to_tree(
139
        code,
140
        py_strat,
141
    )
142
    assert py_strat.get_imports(tree) == [
143
        ("typing", "Foo"),
144
        ("typing", "Bar"),
145
        ("typing", "Baz"),
146
        ("otherlib", "Quux"),
147
    ]
148
149
150
def test_get_non_from_imports(py_strat):
151
    code = """
152
import typing
153
import typing.Foo
154
import foo.Bar as Baz
155
def foo(a):
156
    pass
157
"""
158
    tree = parse_string_to_tree(
159
        code,
160
        py_strat,
161
    )
162
    assert py_strat.get_imports(tree) == [
163
        ("", "typing"),
164
        ("", "typing.Foo"),
165
        ("", "foo.Bar as Baz"),
166
    ]
167