1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
import ast
import os
import pathlib
import sys
import pytest
from vulture import utils
class TestFormatPath:
@pytest.fixture
def tmp_cwd(self, tmp_path, monkeypatch):
cwd = tmp_path / "workingdir"
cwd.mkdir()
monkeypatch.chdir(cwd)
return cwd
def test_relative_inside(self):
filepath = pathlib.Path("testfile.py")
formatted = utils.format_path(filepath)
assert formatted == filepath
assert not formatted.is_absolute()
def test_relative_outside(self, tmp_cwd):
filepath = pathlib.Path(os.pardir) / "testfile.py"
formatted = utils.format_path(filepath)
assert formatted == filepath
assert not formatted.is_absolute()
def test_absolute_inside(self, tmp_cwd):
filepath = tmp_cwd / "testfile.py"
formatted = utils.format_path(filepath)
assert formatted == pathlib.Path("testfile.py")
assert not formatted.is_absolute()
def test_absolute_outside(self, tmp_cwd):
filepath = (tmp_cwd / os.pardir / "testfile.py").resolve()
formatted = utils.format_path(filepath)
assert formatted == filepath
assert formatted.is_absolute()
def check_decorator_names(code, expected_names):
decorator_names = []
def visit_FunctionDef(node):
for decorator in node.decorator_list:
decorator_names.append(utils.get_decorator_name(decorator))
node_visitor = ast.NodeVisitor()
node_visitor.visit_AsyncFunctionDef = visit_FunctionDef
node_visitor.visit_ClassDef = visit_FunctionDef
node_visitor.visit_FunctionDef = visit_FunctionDef
node_visitor.visit(ast.parse(code))
assert expected_names == decorator_names
def test_get_decorator_name_simple():
code = """\
@foobar
def hoo():
pass
"""
check_decorator_names(code, ["@foobar"])
def test_get_decorator_name_call():
code = """\
@xyz()
def bar():
pass
"""
check_decorator_names(code, ["@xyz"])
def test_get_decorator_name_async():
code = """\
@foo.bar.route('/foobar')
async def async_function(request):
print(request)
"""
check_decorator_names(code, ["@foo.bar.route"])
def test_get_decorator_name_multiple_attrs():
code = """\
@x.y.z
def doo():
pass
"""
check_decorator_names(code, ["@x.y.z"])
def test_get_decorator_name_multiple_attrs_called():
code = """\
@a.b.c.d.foo("Foo and Bar")
def hoofoo():
pass
"""
check_decorator_names(code, ["@a.b.c.d.foo"])
def test_get_decorator_name_multiple_decorators():
code = """\
@foo
@bar()
@x.y.z.a('foobar')
def func():
pass
"""
check_decorator_names(code, ["@foo", "@bar", "@x.y.z.a"])
def test_get_decorator_name_class():
code = """\
@foo
@bar.yz
class Foo:
pass
"""
check_decorator_names(code, ["@foo", "@bar.yz"])
def test_get_decorator_name_end_function_call():
code = """\
@foo.bar(x, y, z)
def bar():
pass
"""
check_decorator_names(code, ["@foo.bar"])
@pytest.mark.skipif(
sys.version_info < (3, 9), reason="requires Python 3.9 or higher"
)
@pytest.mark.parametrize(
"decorated",
[
("def foo():"),
("async def foo():"),
("class Foo:"),
],
)
def test_get_decorator_name_multiple_callables(decorated):
decorated = f"{decorated}\n pass"
code = f"""\
@foo
@bar.prop
@z.func("hi").bar().k.foo
@k("hello").doo("world").x
@k.hello("world")
@foo[2]
{decorated}
"""
check_decorator_names(
code,
["@foo", "@bar.prop", "@", "@", "@k.hello", "@"],
)
|