commit be09186f744d9fd335fb354d957e5e766e847709
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jan 2 08:29:15 2014 -0800
Use pyflakes and pep8 apis rather than shelling out
Bringing over stem's recent improvements to use pyflakes and pep8 directly
rather than shelling out. This results in a 62.7% reduction in our test runtime
(from 2.63 to 0.98 seconds on my system).
---
arm/headerPanel.py | 4 +-
run_tests.py | 138 ++++++++++++++++++++++++++++++++--------------------
2 files changed, 88 insertions(+), 54 deletions(-)
diff --git a/arm/headerPanel.py b/arm/headerPanel.py
index addde9d..00796a1 100644
--- a/arm/headerPanel.py
+++ b/arm/headerPanel.py
@@ -21,8 +21,6 @@ import threading
import arm.util.tracker
-import stem
-
from stem.control import State
from stem.util import conf, log, str_tools
@@ -173,6 +171,8 @@ class HeaderPanel(panel.Panel, threading.Thread):
# torTools.getConn().init(controller)
# log.notice("Reconnected to Tor's control port")
# arm.popups.showMsg("Tor reconnected", 1)
+
+ pass
else: isKeystrokeConsumed = False
return isKeystrokeConsumed
diff --git a/run_tests.py b/run_tests.py
index 3ed5e25..13570bc 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -79,6 +79,34 @@ def clean_orphaned_pyc():
os.remove(pyc_path)
+def is_pyflakes_available():
+ """
+ Checks if pyflakes is availalbe.
+
+ :returns: **True** if we can use pyflakes and **False** otherwise
+ """
+
+ try:
+ import pyflakes
+ return True
+ except ImportError:
+ return False
+
+
+def is_pep8_available():
+ """
+ Checks if pep8 is availalbe.
+
+ :returns: **True** if we can use pep8 and **False** otherwise
+ """
+
+ try:
+ import pep8
+ return True
+ except ImportError:
+ return False
+
+
def get_stylistic_issues(paths):
"""
Checks for stylistic issues that are an issue according to the parts of PEP8
@@ -94,26 +122,25 @@ def get_stylistic_issues(paths):
:returns: **dict** of the form ``path => [(line_number, message)...]``
"""
- ignored_issues = ','.join(CONFIG["pep8.ignore"])
issues = {}
- for path in paths:
- pep8_output = stem.util.system.call(
- "pep8 --ignore %s %s" % (ignored_issues, path),
- ignore_exit_status = True,
- )
+ if is_pep8_available():
+ import pep8
- for line in pep8_output:
- line_match = re.match("^(.*):(\d+):(\d+): (.*)$", line)
+ class StyleReport(pep8.BaseReport):
+ def __init__(self, options):
+ super(StyleReport, self).__init__(options)
- if line_match:
- path, line, _, issue = line_match.groups()
+ def error(self, line_number, offset, text, check):
+ code = super(StyleReport, self).error(line_number, offset, text, check)
- if path in CONFIG["pep8.blacklist"]:
- continue
+ if code:
+ issues.setdefault(self.filename, []).append((offset + line_number, "%s %s" % (code, text)))
- issues.setdefault(path, []).append((int(line), issue))
+ style_checker = pep8.StyleGuide(ignore = CONFIG["pep8.ignore"], reporter = StyleReport)
+ style_checker.check_files(filter(lambda path: path not in CONFIG["pep8.blacklist"], _python_files(paths)))
+ for path in paths:
for file_path in _get_files_with_suffix(path):
if file_path in CONFIG["pep8.blacklist"]:
continue
@@ -121,21 +148,25 @@ def get_stylistic_issues(paths):
with open(file_path) as f:
file_contents = f.read()
- lines, file_issues, prev_indent = file_contents.split("\n"), [], 0
+ lines, prev_indent = file_contents.split("\n"), 0
is_block_comment = False
for index, line in enumerate(lines):
whitespace, content = re.match("^(\s*)(.*)$", line).groups()
+ # TODO: This does not check that block indentations are two spaces
+ # because differentiating source from string blocks ("""foo""") is more
+ # of a pita than I want to deal with right now.
+
if '"""' in content:
is_block_comment = not is_block_comment
if "\t" in whitespace:
- file_issues.append((index + 1, "indentation has a tab"))
+ issues.setdefault(file_path, []).append((index + 1, "indentation has a tab"))
elif "\r" in content:
- file_issues.append((index + 1, "contains a windows newline"))
+ issues.setdefault(file_path, []).append((index + 1, "contains a windows newline"))
elif content != content.rstrip():
- file_issues.append((index + 1, "line has trailing whitespace"))
+ issues.setdefault(file_path, []).append((index + 1, "line has trailing whitespace"))
elif content.lstrip().startswith("except") and content.endswith(", exc:"):
# Python 2.6 - 2.7 supports two forms for exceptions...
#
@@ -145,10 +176,7 @@ def get_stylistic_issues(paths):
# The former is the old method and no longer supported in python 3
# going forward.
- file_issues.append((index + 1, "except clause should use 'as', not comma"))
-
- if file_issues:
- issues[file_path] = file_issues
+ issues.setdefault(file_path, []).append((index + 1, "except clause should use 'as', not comma"))
return issues
@@ -162,47 +190,47 @@ def get_pyflakes_issues(paths):
:returns: dict of the form ``path => [(line_number, message)...]``
"""
- pyflakes_ignore = {}
+ issues = {}
- for line in CONFIG["pyflakes.ignore"]:
- path, issue = line.split("=>")
- pyflakes_ignore.setdefault(path.strip(), []).append(issue.strip())
+ if is_pyflakes_available():
+ import pyflakes.api
+ import pyflakes.reporter
- def is_ignored(path, issue):
- # Paths in pyflakes_ignore are relative, so we need to check to see if our
- # path ends with any of them.
+ class Reporter(pyflakes.reporter.Reporter):
+ def __init__(self):
+ self._ignored_issues = {}
- for ignore_path in pyflakes_ignore:
- if path.endswith(ignore_path) and issue in pyflakes_ignore[ignore_path]:
- return True
+ for line in CONFIG["pyflakes.ignore"]:
+ path, issue = line.split("=>")
+ self._ignored_issues.setdefault(path.strip(), []).append(issue.strip())
- return False
+ def unexpectedError(self, filename, msg):
+ self._register_issue(filename, None, msg)
- # Pyflakes issues are of the form...
- #
- # FILE:LINE: ISSUE
- #
- # ... for instance...
- #
- # stem/prereq.py:73: 'long_to_bytes' imported but unused
- # stem/control.py:957: undefined name 'entry'
+ def syntaxError(self, filename, msg, lineno, offset, text):
+ self._register_issue(filename, lineno, msg)
- issues = {}
+ def flake(self, msg):
+ self._register_issue(msg.filename, msg.lineno, msg.message % msg.message_args)
- for path in paths:
- pyflakes_output = stem.util.system.call(
- "pyflakes %s" % path,
- ignore_exit_status = True,
- )
+ def _is_ignored(self, path, issue):
+ # Paths in pyflakes_ignore are relative, so we need to check to see if our
+ # path ends with any of them.
+
+ for ignored_path, ignored_issues in self._ignored_issues.items():
+ if path.endswith(ignored_path) and issue in ignored_issues:
+ return True
- for line in pyflakes_output:
- line_match = re.match("^(.*):(\d+): (.*)$", line)
+ return False
- if line_match:
- path, line, issue = line_match.groups()
+ def _register_issue(self, path, line_number, issue):
+ if not self._is_ignored(path, issue):
+ issues.setdefault(path, []).append((line_number, issue))
- if not is_ignored(path, issue):
- issues.setdefault(path, []).append((int(line), issue))
+ reporter = Reporter()
+
+ for path in _python_files(paths):
+ pyflakes.api.checkPath(path, reporter)
return issues
@@ -228,5 +256,11 @@ def _get_files_with_suffix(base_path, suffix = ".py"):
yield os.path.join(root, filename)
+def _python_files(paths):
+ for path in paths:
+ for file_path in _get_files_with_suffix(path):
+ yield file_path
+
+
if __name__ == '__main__':
main()