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),
)
|