1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-10-25 01:58:49 +02:00
phorge-arcanist/src/lint/linter/pylint/ArcanistPyLintLinter.php

169 lines
5.2 KiB
PHP
Raw Normal View History

<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Uses "PyLint" to detect various errors in Python code.
*
* @group linter
*/
class ArcanistPyLintLinter extends ArcanistLinter {
private function getMessageCodeSeverity($code) {
// The config file defines how PyLint message codes translate to
// arcanist severities. The config options provide regex's to
// match against the message codes generated by PyLint. Severity's
// are matched in the order of errors, warnings, then advice.
// The first severity that matches, in that order, is returned.
$working_copy = $this->getEngine()->getWorkingCopy();
$code_map = array(
ArcanistLintSeverity::SEVERITY_ERROR =>
$working_copy->getConfig('lint.pylint.codes.error'),
ArcanistLintSeverity::SEVERITY_WARNING =>
$working_copy->getConfig('lint.pylint.codes.warning'),
ArcanistLintSeverity::SEVERITY_ADVICE =>
$working_copy->getConfig('lint.pylint.codes.advice'),
);
foreach ($code_map as $sev => $codes) {
if ($codes !== null) {
foreach ($codes as $code_re) {
if (preg_match("/{$code_re}/", $code)) {
return $sev;
}
}
}
}
// If the message code doesn't match any of the provided regex's,
// then just disable it.
return ArcanistLintSeverity::SEVERITY_DISABLED;
}
private function getPyLintPath() {
$pylint_bin = "pylint";
// Use the PyLint prefix specified in the config file
$working_copy = $this->getEngine()->getWorkingCopy();
$prefix = $working_copy->getConfig('lint.pylint.prefix');
if ($prefix !== null) {
$pylint_bin = $prefix."/bin/".$pylint_bin;
}
return $pylint_bin;
}
private function getPyLintPythonPath() {
// Get non-default install locations for pylint and its dependencies
// libraries.
$working_copy = $this->getEngine()->getWorkingCopy();
$prefixes = array(
$working_copy->getConfig('lint.pylint.prefix'),
$working_copy->getConfig('lint.pylint.logilab_astng.prefix'),
$working_copy->getConfig('lint.pylint.logilab_common.prefix'),
);
// Add the libraries to the python search path
$python_path = array();
foreach ($prefixes as $prefix) {
if ($prefix !== null) {
$python_path[] = $prefix.'/lib/python2.6/site-packages';
}
}
$python_path[] = '';
return implode(":", $python_path);
}
private function getPyLintOptions() {
// Options to pass the PyLint
// - '-rn': don't print lint report/summary at end
// - '-iy': show message codes for lint warnings/errors
$options = array('-rn', '-iy');
// Add any options defined in the config file for PyLint
$working_copy = $this->getEngine()->getWorkingCopy();
$config_options = $working_copy->getConfig('lint.pylint.options');
if ($config_options !== null) {
$options += $config_options;
}
return implode(" ", $options);
}
public function willLintPaths(array $paths) {
return;
}
public function getLinterName() {
return 'PyLint';
}
public function getLintSeverityMap() {
return array();
}
public function getLintNameMap() {
return array();
}
public function lintPath($path) {
$pylint_bin = $this->getPyLintPath();
$python_path = $this->getPyLintPythonPath();
$options = $this->getPyLintOptions();
$path_on_disk = $this->getEngine()->getFilePathOnDisk($path);
try {
list($stdout, $_) = execx(
"/usr/bin/env PYTHONPATH=%s\$PYTHONPATH ".
"{$pylint_bin} {$options} {$path_on_disk}",
$python_path);
} catch (CommandException $e) {
// PyLint will return a non-zero exit code if warnings/errors are found.
// Therefore we detect command failure by checking that the stderr is
// some non-expected value.
if ($e->getStderr() !== "No config file found, ".
"using default configuration\n") {
throw $e;
}
$stdout = $e->getStdout();
}
$lines = explode("\n", $stdout);
$messages = array();
foreach ($lines as $line) {
$matches = null;
if (!preg_match('/([A-Z]\d+): *(\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($matches[1]);
$message->setName($this->getLinterName()." ".$matches[1]);
$message->setDescription($matches[3]);
$message->setSeverity($this->getMessageCodeSeverity($matches[1]));
$this->addLintMessage($message);
}
}
}