forked from albertas/deadcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
86 lines (66 loc) · 2.71 KB
/
Copy pathutils.py
File metadata and controls
86 lines (66 loc) · 2.71 KB
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
import ast
from typing import Union
from deadcode.visitor.code_item import CodeItem
from deadcode.constants import UnusedCodeType
def _safe_eval(node: ast.AST, default: bool) -> bool:
"""
Safely evaluate the Boolean expression under the given AST node.
Substitute `default` for all sub-expressions that cannot be
evaluated (because variables or functions are undefined).
We could use eval() to evaluate more sub-expressions. However, this
function is not safe for arbitrary Python code. Even after
overwriting the "__builtins__" dictionary, the original dictionary
can be restored
(https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html).
"""
if isinstance(node, ast.BoolOp):
results = [_safe_eval(value, default) for value in node.values]
if isinstance(node.op, ast.And):
return all(results)
else:
return any(results)
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
return not _safe_eval(node.operand, not default)
else:
try:
eval_result: bool = ast.literal_eval(node)
return eval_result
except ValueError:
return default
def condition_is_always_false(condition: ast.AST) -> bool:
return not _safe_eval(condition, True)
def condition_is_always_true(condition: ast.AST) -> bool:
return _safe_eval(condition, False)
# def format_path(path: pathlib.Path) -> pathlib.Path:
# try:
# return path.relative_to(pathlib.Path.cwd())
# except ValueError:
# # Path is not below the current directory.
# return path
def get_decorator_name(decorator: Union[ast.Call, ast.Attribute]) -> str:
if isinstance(decorator, ast.Call):
decorator = decorator.func # type: ignore
parts = []
while isinstance(decorator, ast.Attribute):
parts.append(decorator.attr)
decorator = decorator.value # type: ignore
parts.append(str(id(decorator))) # type: ignore
return '@' + '.'.join(reversed(parts))
class LoggingList(list): # type: ignore
def __init__(self, type_: UnusedCodeType, verbose: bool) -> None:
self.type_ = type_
self._verbose = verbose
return super().__init__()
def append(self, item: CodeItem) -> None:
if self._verbose:
print(f'define {self.type_} "{item.name}"')
super().append(item)
class LoggingSet(set): # type: ignore
def __init__(self, type_: UnusedCodeType, verbose: bool) -> None:
self.type_ = type_
self._verbose = verbose
return super().__init__()
def add(self, name: str) -> None:
if self._verbose:
print(f'use {self.type_} "{name}"')
super().add(name)