Skip to content

Commit e20e803

Browse files
authored
3.0.0
Flake8 fix and master branch fixes
2 parents e27f3bd + 82b2211 commit e20e803

File tree

7 files changed

+479
-533
lines changed

7 files changed

+479
-533
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ python:
33
- "2.7"
44
- "3.4"
55
- "3.5"
6+
- "3.6"
67

78
env:
89
matrix:

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ available in ``flake8``::
2828
Changes
2929
-------
3030

31+
3.0.0 - 2017-05-11
32+
````````````````
33+
* fix the refactor of the detector in 2.0.0 that was removed from pypi.
34+
* fix a flake8 issue that had it turned off by default.
35+
36+
3137
2.0.0 - 2016-09-19
3238
````````````````
3339
* refactor detector

flake8_debugger.py

Lines changed: 117 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,13 @@
11
"""Extension for flake8 that finds usage of the debugger."""
22
import ast
3+
from itertools import chain
34

5+
import pycodestyle
46

5-
__version__ = '2.0.0'
6-
7-
DEBUGGER_ERROR_CODE = 'T002'
8-
9-
10-
def flake8ext(f):
11-
"""Decorate flake8 extension function."""
12-
f.name = 'flake8-debugger'
13-
f.version = __version__
14-
return f
157

8+
__version__ = '3.0.0'
169

17-
def format_debugger_message(import_type, item_imported, item_alias, trace_method, trace_alias):
18-
if import_type == 'import':
19-
if item_imported == item_alias:
20-
return '{0} import for {1} found'.format(DEBUGGER_ERROR_CODE, item_alias)
21-
else:
22-
return '{0} import for {1} found as {2}'.format(DEBUGGER_ERROR_CODE, item_imported, item_alias)
23-
elif import_type == 'import_trace':
24-
if trace_method == trace_alias:
25-
return '{0} import for {1}.{2} found'.format(DEBUGGER_ERROR_CODE, item_imported, trace_method)
26-
else:
27-
return '{0} import for {1}.{2} found as {3}'.format(DEBUGGER_ERROR_CODE, item_imported, trace_method, trace_alias)
28-
elif import_type == 'trace_used':
29-
if trace_method == trace_alias:
30-
return '{0} trace found: {1}.{2} used'.format(DEBUGGER_ERROR_CODE, item_imported, trace_method)
31-
else:
32-
return '{0} trace found: {1}.{2} used as {3}'.format(DEBUGGER_ERROR_CODE, item_imported, trace_method, trace_alias)
33-
10+
DEBUGGER_ERROR_CODE = 'T002'
3411

3512
debuggers = {
3613
'pdb': 'set_trace',
@@ -40,60 +17,116 @@ def format_debugger_message(import_type, item_imported, item_alias, trace_method
4017
}
4118

4219

43-
def check_for_debugger_import(logical_line, checker_state):
44-
for node in ast.walk(ast.parse(logical_line)):
45-
if isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
46-
47-
if hasattr(node, 'module') and node.module not in debuggers.keys():
48-
continue
49-
50-
module_names = (hasattr(node, 'names') and [module_name.name for module_name in node.names]) or []
51-
if isinstance(node, ast.Import):
52-
for debugger in debuggers.keys():
53-
if debugger in module_names:
54-
index = module_names.index(debugger)
55-
yield 'import', debugger, getattr(node.names[index], 'asname', None) or debugger, debuggers[debugger], debuggers[debugger]
56-
57-
elif isinstance(node, ast.ImportFrom):
58-
trace_methods = debuggers.values()
59-
traces_found = set([trace for trace in trace_methods if trace in module_names])
60-
if not traces_found:
61-
continue
62-
for trace in traces_found:
63-
trace_index = trace in module_names and module_names.index(trace)
64-
yield 'import_trace', node.module, node.module, debuggers[node.module], getattr(node.names[trace_index], 'asname', None) or debuggers[node.module]
65-
66-
67-
def check_for_set_trace_usage(logical_line, checker_state):
68-
for node in ast.walk(ast.parse(logical_line)):
69-
if isinstance(node, ast.Call):
70-
trace_methods = [checker_state['debuggers_found'][debugger]['trace_alias'] for debugger in checker_state['debuggers_found'].keys()]
71-
if (getattr(node.func, 'attr', None) in trace_methods or getattr(node.func, 'id', None) in trace_methods):
72-
for debugger, debugger_info in checker_state['debuggers_found'].items():
73-
trace_method_name = checker_state['debuggers_found'][debugger]['trace_alias']
74-
if (
75-
(hasattr(node.func, 'value') and node.func.value.id == debugger_info['alias']) or
76-
(hasattr(node.func, 'id') and trace_method_name and node.func.id == trace_method_name)
77-
):
78-
yield 'trace_used', debugger, debugger_info['alias'], debugger_info['trace_method'], debugger_info['trace_alias']
79-
break
80-
81-
82-
@flake8ext
83-
def debugger_usage(logical_line, checker_state=None, noqa=None):
84-
if 'debuggers_found' not in checker_state:
85-
checker_state['debuggers_found'] = {}
86-
generator = check_for_debugger_import(logical_line, checker_state.copy())
87-
88-
for import_type, item_imported, item_alias, trace_method, trace_alias in generator:
89-
if item_imported is not None:
90-
checker_state['debuggers_found'][item_imported] = {
91-
'alias': item_alias, 'trace_method': trace_method, 'trace_alias': trace_alias
92-
}
93-
if not noqa:
94-
yield 0, format_debugger_message(import_type, item_imported, item_alias, trace_method, trace_alias)
95-
96-
if not noqa:
97-
generator = check_for_set_trace_usage(logical_line, checker_state.copy())
98-
for usage in generator:
99-
yield 0, format_debugger_message(*usage)
20+
VIOLATIONS = {
21+
'found': {
22+
'set_trace': 'T001 set_trace found.',
23+
'InteractiveShellEmbed': 'T003 InteractiveShellEmbed found.',
24+
},
25+
'declared': {
26+
'print': 'T101 Python 2.x reserved word print used.',
27+
'pprint': 'T103 pprint declared',
28+
},
29+
}
30+
31+
32+
class DebuggerFinder(ast.NodeVisitor):
33+
def __init__(self, *args, **kwargs):
34+
super(DebuggerFinder, self).__init__(*args, **kwargs)
35+
self.debuggers_used = {}
36+
self.debuggers_traces_redefined = {}
37+
self.debuggers_traces_names = {}
38+
self.debugger_traces_imported = {}
39+
self.debuggers_names = {}
40+
self.debuggers_redefined = {}
41+
self.debuggers_imported = {}
42+
43+
def visit_Call(self, node):
44+
debugger_method_names = chain(debuggers.values(), self.debuggers_traces_names.values())
45+
is_debugger_function = getattr(node.func, "id", None) in list(debugger_method_names)
46+
if is_debugger_function:
47+
if node.func.id in self.debuggers_traces_names.values():
48+
debugger_method = next(item[0] for item in self.debuggers_traces_names.items() if item[1] == node.func.id)
49+
entry = self.debuggers_used.setdefault((node.lineno, node.col_offset), [])
50+
if debugger_method == node.func.id:
51+
entry.append('{0} trace found: {1} used'.format(DEBUGGER_ERROR_CODE, node.func.id))
52+
else:
53+
entry.append('{0} trace found: {1} used as {2}'.format(DEBUGGER_ERROR_CODE, debugger_method, node.func.id))
54+
55+
debugger_method_names = chain(debuggers.values(), self.debuggers_traces_names.values())
56+
is_debugger_attribute = getattr(node.func, "attr", None) in list(debugger_method_names)
57+
if is_debugger_attribute:
58+
caller = node.func.value.id
59+
entry = self.debuggers_used.setdefault((node.lineno, node.col_offset), [])
60+
if caller in self.debuggers_names.values():
61+
entry.append('{0} trace found: {1}.{2} used'.format(DEBUGGER_ERROR_CODE, caller, node.func.attr))
62+
else:
63+
entry.append('{0} trace found: {1} used'.format(DEBUGGER_ERROR_CODE, node.func.attr))
64+
self.generic_visit(node)
65+
66+
def visit_Import(self, node):
67+
for name_node in node.names:
68+
if name_node.name in list(debuggers.keys()):
69+
if name_node.asname is not None:
70+
self.debuggers_names[name_node.name] = name_node.asname
71+
entry = self.debuggers_redefined.setdefault((node.lineno, node.col_offset), [])
72+
entry.append('{0} import for {1} found as {2}'.format(DEBUGGER_ERROR_CODE, name_node.name, name_node.asname))
73+
else:
74+
self.debuggers_names[name_node.name] = name_node.name
75+
entry = self.debuggers_imported.setdefault((node.lineno, node.col_offset), [])
76+
entry.append('{0} import for {1} found'.format(DEBUGGER_ERROR_CODE, name_node.name))
77+
78+
def visit_ImportFrom(self, node):
79+
if node.module in list(debuggers.keys()):
80+
for name_node in node.names:
81+
if name_node.name == debuggers[node.module]:
82+
if name_node.asname is not None:
83+
self.debuggers_traces_names[name_node.name] = name_node.asname
84+
entry = self.debuggers_traces_redefined.setdefault((node.lineno, node.col_offset), [])
85+
entry.append('{0} import for {1} found as {2}'.format(DEBUGGER_ERROR_CODE, name_node.name, name_node.asname))
86+
else:
87+
self.debuggers_traces_names[name_node.name] = name_node.name
88+
entry = self.debugger_traces_imported.setdefault((node.lineno, node.col_offset), [])
89+
entry.append('{0} import for {1} found'.format(DEBUGGER_ERROR_CODE, name_node.name))
90+
91+
92+
class DebuggerChecker(object):
93+
options = None
94+
name = 'flake8-debugger'
95+
version = __version__
96+
97+
def __init__(self, tree, filename):
98+
self.tree = tree
99+
self.filename = filename
100+
self.lines = None
101+
102+
def load_file(self):
103+
if self.filename in ("stdin", "-", None):
104+
self.filename = "stdin"
105+
self.lines = pycodestyle.stdin_get_value().splitlines(True)
106+
else:
107+
self.lines = pycodestyle.readlines(self.filename)
108+
109+
if not self.tree:
110+
self.tree = ast.parse("".join(self.lines))
111+
112+
def run(self):
113+
if not self.tree or not self.lines:
114+
self.load_file()
115+
116+
parser = DebuggerFinder()
117+
parser.visit(self.tree)
118+
for error, messages in parser.debuggers_used.items():
119+
if not pycodestyle.noqa(self.lines[error[0] - 1]):
120+
for message in messages:
121+
yield (error[0], error[1], message, DebuggerChecker)
122+
123+
for error, messages in chain(
124+
parser.debuggers_traces_redefined.items(),
125+
parser.debugger_traces_imported.items(),
126+
parser.debuggers_redefined.items(),
127+
parser.debuggers_imported.items(),
128+
):
129+
if error not in parser.debuggers_used:
130+
if not pycodestyle.noqa(self.lines[error[0] - 1]):
131+
for message in messages:
132+
yield (error[0], error[1], message, DebuggerChecker)

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[aliases]
2+
test=pytest

setup.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ def get_long_description():
1818
descr.append(f.read())
1919
return '\n\n'.join(descr)
2020

21-
install_requires = ['flake8']
2221

23-
test_requires = ['nose', 'flake8>=1.5', 'unittest2==1.1.0', 'pep8']
22+
install_requires = ['flake8>=1.5', 'pycodestyle']
23+
24+
test_requires = ['pytest', 'flake8>=1.5', 'pycodestyle']
2425

2526
setup(
2627
name='flake8-debugger',
@@ -36,11 +37,12 @@ def get_long_description():
3637
zip_safe=False,
3738
entry_points={
3839
'flake8.extension': [
39-
'flake8_debugger = flake8_debugger:debugger_usage',
40+
'T = flake8_debugger:DebuggerChecker',
4041
],
4142
},
4243
install_requires=install_requires,
4344
tests_require=test_requires,
45+
setup_requires=['pytest-runner'],
4446
test_suite="nose.collector",
4547
classifiers=[
4648
'Development Status :: 4 - Beta',

0 commit comments

Comments
 (0)