From f8c3a5d5555690bb98a88eac5d6096a4349ac535 Mon Sep 17 00:00:00 2001 From: Andrew Gallagher Date: Thu, 19 May 2011 02:00:59 -0700 Subject: [PATCH] arc lint: add support for PyFlakes Summary: Provide a simple linter wrapper around pyflakes. This relies on finding pyflakes via: - lint.pyflakes.path - arcconfig setting of absolute path to pyflakes - lint.pyflakes.prefix - arcconfig setting of the prefix that pyflakes was installed under - users path Test Plan: linted python code with PyFlakes warnings Reviewed By: epriestley Reviewers: jungejason, epriestley Commenters: jungejason CC: aran, epriestley, andrewjcg, jungejason Differential Revision: 310 --- src/__phutil_library_map__.php | 2 + .../pyflakes/ArcanistPyFlakesLinter.php | 113 ++++++++++++++++++ src/lint/linter/pyflakes/__init__.php | 16 +++ 3 files changed, 131 insertions(+) create mode 100644 src/lint/linter/pyflakes/ArcanistPyFlakesLinter.php create mode 100644 src/lint/linter/pyflakes/__init__.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index cf5667c8..8b081296 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -61,6 +61,7 @@ phutil_register_library_map(array( 'ArcanistPhutilModuleLinter' => 'lint/linter/phutilmodule', 'ArcanistPhutilTestCase' => 'unit/engine/phutil/testcase', 'ArcanistPhutilTestTerminatedException' => 'unit/engine/phutil/testcase/exception', + 'ArcanistPyFlakesLinter' => 'lint/linter/pyflakes', 'ArcanistRepositoryAPI' => 'repository/api/base', 'ArcanistShellCompleteWorkflow' => 'workflow/shell-complete', 'ArcanistSubversionAPI' => 'repository/api/subversion', @@ -113,6 +114,7 @@ phutil_register_library_map(array( 'ArcanistPEP8Linter' => 'ArcanistLinter', 'ArcanistPatchWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistPhutilModuleLinter' => 'ArcanistLinter', + 'ArcanistPyFlakesLinter' => 'ArcanistLinter', 'ArcanistShellCompleteWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI', 'ArcanistSvnHookPreCommitWorkflow' => 'ArcanistBaseWorkflow', diff --git a/src/lint/linter/pyflakes/ArcanistPyFlakesLinter.php b/src/lint/linter/pyflakes/ArcanistPyFlakesLinter.php new file mode 100644 index 00000000..3b2dfbaa --- /dev/null +++ b/src/lint/linter/pyflakes/ArcanistPyFlakesLinter.php @@ -0,0 +1,113 @@ +getEngine()->getWorkingCopy(); + $pyflakes_path = $working_copy->getConfig('lint.pyflakes.path'); + $pyflakes_prefix = $working_copy->getConfig('lint.pyflakes.prefix'); + + // Default to just finding pyflakes in the users path + $pyflakes_bin = 'pyflakes'; + $python_path = ''; + + // If a pyflakes path was specified, then just use that as the + // pyflakes binary and assume that the libraries will be imported + // correctly. + // + // If no pyflakes path was specified and a pyflakes prefix was + // specified, then use the binary from this prefix and add it to + // the PYTHONPATH environment variable so that the libs are imported + // correctly. This is useful when pyflakes is installed into a + // non-default location. + if ($pyflakes_path !== null) { + $pyflakes_bin = $pyflakes_path; + } else if ($pyflakes_prefix !== null) { + $pyflakes_bin = $pyflakes_prefix.'/bin/pyflakes'; + $python_path = $pyflakes_prefix.'/lib/python2.6/site-packages:'; + } + + $options = $this->getPyFlakesOptions(); + + $f = new ExecFuture( + "/usr/bin/env PYTHONPATH=%s\$PYTHONPATH ". + "{$pyflakes_bin} {$options}", $python_path); + $f->write($this->getData($path)); + + try { + list($stdout, $_) = $f->resolvex(); + } catch (CommandException $e) { + // PyFlakes will return an exit code of 1 if warnings/errors + // are found but print nothing to stderr in this case. Therefore, + // if we see any output on stderr or a return code other than 1 or 0, + // pyflakes failed. + if ($e->getError() !== 1 || $e->getStderr() !== '') { + throw $e; + } else { + $stdout = $e->getStdout(); + } + } + + $lines = explode("\n", $stdout); + $messages = array(); + foreach ($lines as $line) { + $matches = null; + if (!preg_match('/^(.*?):(\d+): (.*)$/', $line, $matches)) { + continue; + } + foreach ($matches as $key => $match) { + $matches[$key] = trim($match); + } + $message = new ArcanistLintMessage(); + $message->setPath($path); + $message->setLine($matches[2]); + $message->setCode($this->getLinterName()); + $message->setDescription($matches[3]); + $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING); + $this->addLintMessage($message); + } + } + +} diff --git a/src/lint/linter/pyflakes/__init__.php b/src/lint/linter/pyflakes/__init__.php new file mode 100644 index 00000000..8a81e2cf --- /dev/null +++ b/src/lint/linter/pyflakes/__init__.php @@ -0,0 +1,16 @@ +