Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@
package org.sonar.plugins.python;

import com.google.common.collect.ImmutableList;

import org.sonar.api.SonarPlugin;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
import org.sonar.plugins.python.colorizer.PythonColorizer;
import org.sonar.plugins.python.coverage.PythonCoverageSensor;
import org.sonar.plugins.python.cpd.PythonCpdMapping;
import org.sonar.plugins.python.flake8.Flake8Configuration;
import org.sonar.plugins.python.flake8.Flake8RuleRepository;
import org.sonar.plugins.python.flake8.Flake8Sensor;
import org.sonar.plugins.python.pylint.PylintConfiguration;
import org.sonar.plugins.python.pylint.PylintRuleRepository;
import org.sonar.plugins.python.pylint.PylintSensor;
Expand Down Expand Up @@ -64,6 +68,10 @@ public List getExtensions() {
PylintSensor.class,
PylintRuleRepository.class,

Flake8Configuration.class,
Flake8Sensor.class,
Flake8RuleRepository.class,

PythonXunitSensor.class,
PythonCoverageSensor.class);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2011 SonarSource and Waleri Enns
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.plugins.python.flake8;

import org.sonar.api.utils.command.StreamConsumer;

import java.util.LinkedList;
import java.util.List;

class CommandStreamConsumer implements StreamConsumer {
private List<String> data = new LinkedList<String>();

public void consumeLine(String line) {
data.add(line);
}

public List<String> getData() {
return data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2011 SonarSource and Waleri Enns
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.plugins.python.flake8;

import org.apache.commons.lang.StringUtils;
import org.sonar.api.BatchExtension;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.config.Settings;
import org.sonar.api.scan.filesystem.ModuleFileSystem;

import java.io.File;

@Properties({
@Property(
key = Flake8Configuration.FLAKE8_CONFIG_KEY,
defaultValue = "",
name = "flake8 configuration",
description = "Path to the flake8 configuration file to use in flake8 analysis. Set to empty to use the default.",
global = false,
project = true),
@Property(
key = Flake8Configuration.FLAKE8_KEY,
defaultValue = "",
name = "flake8 executable",
description = "Path to the flake8 executable to use in flake8 analysis. Set to empty to use the default one.",
global = true,
project = false)
})
public class Flake8Configuration implements BatchExtension {

public static final String FLAKE8_CONFIG_KEY = "sonar.python.flake8_config";
public static final String FLAKE8_KEY = "sonar.python.flake8";

private final Settings conf;

public Flake8Configuration(Settings conf) {
this.conf = conf;
}

public String getFlake8ConfigPath(ModuleFileSystem fileSystem) {
String configPath = conf.getString(Flake8Configuration.FLAKE8_CONFIG_KEY);
if (StringUtils.isEmpty(configPath)) {
return null;
}
File configFile = new File(configPath);
if (!configFile.isAbsolute()) {
File projectRoot = fileSystem.baseDir();
configFile = new File(projectRoot.getPath(), configPath);
}
return configFile.getAbsolutePath();
}

public String getFlake8Path() {
return conf.getString(Flake8Configuration.FLAKE8_KEY);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2011 SonarSource and Waleri Enns
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.plugins.python.flake8;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.command.Command;
import org.sonar.api.utils.command.CommandExecutor;

import com.google.common.io.Files;

public class Flake8IssuesAnalyzer {

private static final Logger LOG = LoggerFactory.getLogger(Flake8Sensor.class);


private static final String FALLBACK_COMMAND = "flake8";
private static final Pattern PATTERN = Pattern.compile("([^:]+):([0-9]+):([0-9]+): (\\S+) (.*)");

private String flake8 = null;
private String flake8ConfigParam = null;

Flake8IssuesAnalyzer(String flake8Path, String flake8ConfigPath) {
flake8 = flake8PathWithDefault(flake8Path);

if (flake8ConfigPath != null) {
if (!new File(flake8ConfigPath).exists()) {
throw new SonarException("Cannot find the flake8 configuration file: " + flake8ConfigPath);
}
flake8ConfigParam = "--config=" + flake8ConfigPath;
}

}

private static String flake8PathWithDefault(String flake8Path) {
if (flake8Path != null) {
if (!new File(flake8Path).exists()) {
throw new SonarException("Cannot find the flake8 executable: " + flake8Path);
}
return flake8Path;
}
return FALLBACK_COMMAND;
}

public List<Issue> analyze(String path, Charset charset, File out) throws IOException {
Command command = Command.create(flake8).addArgument(path);

if (flake8ConfigParam != null) {
command.addArgument(flake8ConfigParam);
}

LOG.debug("Calling command: '{}'", command.toString());

long timeoutMS = 300000; // =5min
CommandStreamConsumer stdOut = new CommandStreamConsumer();
CommandStreamConsumer stdErr = new CommandStreamConsumer();
CommandExecutor.create().execute(command, stdOut, stdErr, timeoutMS);

// the error stream can contain a line like 'no custom config found, using default'
// any bigger output on the error stream is likely a flake8 malfunction
if (stdErr.getData().size() > 1) {
LOG.warn("Output on the error channel detected: this is probably due to a problem on flake8's side.");
LOG.warn("Content of the error stream: \n\"{}\"", StringUtils.join(stdErr.getData(), "\n"));
}

Files.write(StringUtils.join(stdOut.getData(), "\n"), out, charset);

return parseOutput(stdOut.getData());
}

protected List<Issue> parseOutput(List<String> lines) {
// Parse the output of flake8. Example of the format:
//
// complexity/code_chunks.py:62:32: W0104 Statement seems to have no effect

List<Issue> issues = new LinkedList<Issue>();

int linenr;
String filename = null;
String ruleid = null;
String descr = null;

if (!lines.isEmpty()) {
for (String line : lines) {
if (line.length() > 0) {
Matcher m = PATTERN.matcher(line);
if (m.matches() && m.groupCount() == 5) {
filename = m.group(1);
linenr = Integer.valueOf(m.group(2));
ruleid = m.group(4);
descr = m.group(5);
issues.add(new Issue(filename, linenr, ruleid, descr));
} else {
LOG.debug("Cannot parse the line: {}", line);
}
} else {
LOG.trace("Classifying as detail and ignoring line '{}'", line);
}
}
}

return issues;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2011 SonarSource and Waleri Enns
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.plugins.python.flake8;

import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleRepository;
import org.sonar.api.rules.XMLRuleParser;
import org.sonar.plugins.python.Python;

import java.util.List;

public class Flake8RuleRepository extends RuleRepository {

public static final String REPOSITORY_NAME = "Flake8";
public static final String REPOSITORY_KEY = REPOSITORY_NAME;

private static final String RULES_FILE = "/org/sonar/plugins/python/flake8/rules.xml";
private final XMLRuleParser ruleParser;

public Flake8RuleRepository(XMLRuleParser ruleParser) {
super(REPOSITORY_KEY, Python.KEY);
setName(REPOSITORY_NAME);
this.ruleParser = ruleParser;
}

@Override
public List<Rule> createRules() {
List<Rule> rules = ruleParser.parse(getClass().getResourceAsStream(RULES_FILE));
for (Rule r : rules) {
r.setRepositoryKey(REPOSITORY_KEY);
}
return rules;
}

}
Loading