-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathflake8_deprecated.py
123 lines (105 loc) · 4.06 KB
/
flake8_deprecated.py
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
import ast
class Flake8Deprecated:
name = 'flake8_deprecated'
version = '1.2'
message = 'D001 found {0:s} replace it with {1:s}'
deprecations = {
'assertEqual': (
'failUnlessEqual',
'assertEquals',
),
'assertNotEqual': ('failIfEqual',),
'assertTrue': (
'failUnless',
'assert_',
),
'assertFalse': ('failIf',),
'assertRaises': ('failUnlessRaises',),
'assertAlmostEqual': ('failUnlessAlmostEqual',),
'assertNotAlmostEqual': ('failIfAlmostEqual',),
'AccessControl.ClassSecurityInfo.protected': ('declareProtected',),
'AccessControl.ClassSecurityInfo.private': ('declarePrivate',),
'AccessControl.ClassSecurityInfo.public': ('declarePublic',),
'zope.interface.provider': ('directlyProvides',),
'zope.interface.implementer': (
'classImplements',
'implements',
),
'self.loadZCML(': ('xmlconfig.file',),
'zope.component.adapter': ('adapts',),
}
def __init__(self, tree):
self.old_aliases = self._reverse_data()
self.tree = tree
def run(self):
for node in ast.walk(self.tree):
value = None
if isinstance(node, ast.Call):
value = self.check_calls(node)
elif isinstance(node, ast.FunctionDef):
value = self.check_decorators(node)
if value:
yield from value
def check_calls(self, node):
function_name = getattr(node.func, 'id', '')
if function_name:
value = self.check_function_call(node)
else:
value = self.check_method_call(node)
if value:
yield from value
def check_function_call(self, node):
function_name = node.func.id
for old_alias in self.old_aliases:
if function_name == old_alias:
yield self.error(node, old_alias)
def check_method_call(self, node):
"""Check method calls, i.e. self.SOME_CALL()
Note that this can be endlessly nested, i.e. self.obj.another.more.SOME_CALL()
"""
method_name = getattr(node.func, 'attr', None)
if not method_name:
return
is_obj = getattr(node.func, 'value', False)
for old_alias in self.old_aliases:
if method_name == old_alias:
yield self.error(node, old_alias)
elif '.' in old_alias and is_obj:
obj_name = getattr(node.func.value, 'attr', False)
obj_id = getattr(node.func.value, 'id', False)
for name in (obj_name, obj_id):
if f'{name}.{method_name}' == old_alias:
yield self.error(node, old_alias)
def check_decorators(self, node):
"""Check decorators names for deprecated aliases
Check for function-style decorators, i.e @my_deprecated_decorator()
as well as for alias-like decorators, i.e @my_deprecated_decorator
"""
for decorator in node.decorator_list:
name = None
if isinstance(decorator, ast.Attribute):
name = decorator.attr
elif isinstance(decorator, ast.Name):
name = decorator.id
if not name:
continue
for old_alias in self.old_aliases:
if name == old_alias:
yield self.error(node, old_alias)
def _reverse_data(self):
"""Reverse the deprecation dictionary
This way, we can more easily loop through the deprecated snippets.
We only care about the new version at error reporting time.
"""
return {
old_alias: new_version
for new_version, alias_list in self.deprecations.items()
for old_alias in alias_list
}
def error(self, statement, old_alias):
return (
statement.lineno,
statement.col_offset,
self.message.format(old_alias, self.old_aliases[old_alias]),
type(self),
)