From 25bfcc7c8a49d283cd779b3e8f533fcba149bbbd Mon Sep 17 00:00:00 2001 From: Guillaume Dequenne Date: Mon, 6 Jul 2020 10:47:46 +0200 Subject: [PATCH 1/5] Prepare for next development iteration --- its/plugin/it-python-plugin-test/pom.xml | 2 +- its/plugin/pom.xml | 2 +- its/plugin/python-custom-rules-plugin/pom.xml | 2 +- its/pom.xml | 2 +- its/ruling/pom.xml | 2 +- pom.xml | 2 +- python-checks-testkit/pom.xml | 2 +- python-checks/pom.xml | 2 +- python-frontend/pom.xml | 2 +- sonar-python-plugin/pom.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/its/plugin/it-python-plugin-test/pom.xml b/its/plugin/it-python-plugin-test/pom.xml index 4b8305cc49..dfa5da2cd3 100644 --- a/its/plugin/it-python-plugin-test/pom.xml +++ b/its/plugin/it-python-plugin-test/pom.xml @@ -7,7 +7,7 @@ it-python-plugin org.sonarsource.python - 2.14-SNAPSHOT + 3.0-SNAPSHOT it-python-plugin-test diff --git a/its/plugin/pom.xml b/its/plugin/pom.xml index dd81260a88..64f84cc6c8 100644 --- a/its/plugin/pom.xml +++ b/its/plugin/pom.xml @@ -5,7 +5,7 @@ org.sonarsource.python python-its - 2.14-SNAPSHOT + 3.0-SNAPSHOT diff --git a/its/plugin/python-custom-rules-plugin/pom.xml b/its/plugin/python-custom-rules-plugin/pom.xml index 916ce7c703..b199bc8e38 100644 --- a/its/plugin/python-custom-rules-plugin/pom.xml +++ b/its/plugin/python-custom-rules-plugin/pom.xml @@ -6,7 +6,7 @@ org.sonarsource.python it-python-plugin - 2.14-SNAPSHOT + 3.0-SNAPSHOT python-custom-rules-plugin diff --git a/its/pom.xml b/its/pom.xml index 57b0b6e135..f589fa5b52 100644 --- a/its/pom.xml +++ b/its/pom.xml @@ -5,7 +5,7 @@ org.sonarsource.python python - 2.14-SNAPSHOT + 3.0-SNAPSHOT python-its diff --git a/its/ruling/pom.xml b/its/ruling/pom.xml index 14d816e5cf..5f516c15e0 100644 --- a/its/ruling/pom.xml +++ b/its/ruling/pom.xml @@ -6,7 +6,7 @@ org.sonarsource.python python-its - 2.14-SNAPSHOT + 3.0-SNAPSHOT it-python-ruling diff --git a/pom.xml b/pom.xml index d049358c7f..272adf6762 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.sonarsource.python python - 2.14-SNAPSHOT + 3.0-SNAPSHOT pom Python diff --git a/python-checks-testkit/pom.xml b/python-checks-testkit/pom.xml index f9cda17797..98d409c671 100644 --- a/python-checks-testkit/pom.xml +++ b/python-checks-testkit/pom.xml @@ -6,7 +6,7 @@ python org.sonarsource.python - 2.14-SNAPSHOT + 3.0-SNAPSHOT python-checks-testkit diff --git a/python-checks/pom.xml b/python-checks/pom.xml index 3b144b7d24..60c6c9d6b5 100644 --- a/python-checks/pom.xml +++ b/python-checks/pom.xml @@ -5,7 +5,7 @@ org.sonarsource.python python - 2.14-SNAPSHOT + 3.0-SNAPSHOT python-checks diff --git a/python-frontend/pom.xml b/python-frontend/pom.xml index 5055a93e36..096195fe6b 100644 --- a/python-frontend/pom.xml +++ b/python-frontend/pom.xml @@ -5,7 +5,7 @@ org.sonarsource.python python - 2.14-SNAPSHOT + 3.0-SNAPSHOT python-frontend diff --git a/sonar-python-plugin/pom.xml b/sonar-python-plugin/pom.xml index bc4c38f7a1..81d8bcfc4b 100644 --- a/sonar-python-plugin/pom.xml +++ b/sonar-python-plugin/pom.xml @@ -5,7 +5,7 @@ org.sonarsource.python python - 2.14-SNAPSHOT + 3.0-SNAPSHOT sonar-python-plugin From 5bd36987282330ff829ed9a33d3c64610955028a Mon Sep 17 00:00:00 2001 From: Guillaume Dequenne Date: Tue, 7 Jul 2020 10:23:58 +0200 Subject: [PATCH 2/5] SONARPY-748 Drop Pylint execution mode and old style issue importing (#807) --- .../com/sonar/python/it/plugin/Tests.java | 1 - .../sonar/plugins/python/PythonPlugin.java | 40 --- .../python/pylint/CommandStreamConsumer.java | 38 --- .../sonar/plugins/python/pylint/Issue.java | 62 ---- .../python/pylint/PylintArguments.java | 63 ---- .../python/pylint/PylintConfiguration.java | 59 ---- .../python/pylint/PylintImportSensor.java | 147 --------- .../python/pylint/PylintIssuesAnalyzer.java | 115 ------- .../python/pylint/PylintReportParser.java | 114 ------- .../python/pylint/PylintRuleParser.java | 73 ----- .../python/pylint/PylintRuleRepository.java | 78 ----- .../plugins/python/pylint/PylintSensor.java | 137 -------- .../plugins/python/PythonPluginTest.java | 2 +- .../python/pylint/PylintArgumentsTest.java | 60 ---- .../pylint/PylintConfigurationTest.java | 66 ---- .../python/pylint/PylintImportSensorTest.java | 144 -------- .../python/pylint/PylintIssuesAnalyzerIT.java | 50 --- .../pylint/PylintIssuesAnalyzerTest.java | 170 ---------- .../python/pylint/PylintRuleParserTest.java | 60 ---- .../pylint/PylintRuleRepositoryTest.java | 53 --- .../python/pylint/PylintSensorTest.java | 212 ------------ .../org/sonar/plugins/python/pylint/empty.xml | 3 - .../sonar/plugins/python/pylint/executable | 0 .../plugins/python/pylint/pylintrc_sample | 309 ------------------ 24 files changed, 1 insertion(+), 2055 deletions(-) delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/CommandStreamConsumer.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/Issue.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintArguments.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintConfiguration.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintImportSensor.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzer.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintReportParser.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleParser.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleRepository.java delete mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintArgumentsTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintConfigurationTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintImportSensorTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerIT.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleParserTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleRepositoryTest.java delete mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/empty.xml delete mode 100755 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/executable delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylintrc_sample diff --git a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java index e8c6c64592..62278cf0fa 100644 --- a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java +++ b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java @@ -50,7 +50,6 @@ MetricsTest.class, CPDTest.class, CoverageTest.class, - PylintReportTest.class, TestReportTest.class, NoSonarTest.class, SonarLintTest.class diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java index a6074b66af..c2ccfd1613 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java @@ -31,10 +31,6 @@ import org.sonar.plugins.python.coverage.PythonCoverageSensor; import org.sonar.plugins.python.flake8.Flake8RulesDefinition; import org.sonar.plugins.python.flake8.Flake8Sensor; -import org.sonar.plugins.python.pylint.PylintConfiguration; -import org.sonar.plugins.python.pylint.PylintImportSensor; -import org.sonar.plugins.python.pylint.PylintRuleRepository; -import org.sonar.plugins.python.pylint.PylintSensor; import org.sonar.plugins.python.warnings.DefaultAnalysisWarningsWrapper; import org.sonar.plugins.python.xunit.PythonXUnitSensor; @@ -79,7 +75,6 @@ public void define(Context context) { context.addExtension(DefaultAnalysisWarningsWrapper.class); addCoberturaExtensions(context); addXUnitExtensions(context); - addPylintExtensions(context); addBanditExtensions(context); addFlake8Extensions(context); } @@ -136,41 +131,6 @@ private static void addXUnitExtensions(Context context) { PythonXUnitSensor.class); } - private static void addPylintExtensions(Context context) { - context.addExtensions( - PropertyDefinition.builder(PylintConfiguration.PYLINT_CONFIG_KEY) - .index(30) - .name("Pylint configuration") - .description("Path to the pylint configuration file to use in pylint analysis. Set to empty to use the default.") - .category(PYTHON_CATEGORY) - .subCategory(PYLINT) - .onQualifiers(Qualifiers.PROJECT) - .defaultValue("") - .build(), - PropertyDefinition.builder(PylintConfiguration.PYLINT_KEY) - .index(31) - .name("Pylint executable") - .description("Path to the pylint executable to use in pylint analysis. Set to empty to use the default one.") - .category(PYTHON_CATEGORY) - .subCategory(PYLINT) - .onQualifiers(Qualifiers.PROJECT) - .defaultValue("") - .build(), - PropertyDefinition.builder(PylintImportSensor.REPORT_PATH_KEY) - .index(32) - .name("Pylint's reports") - .description("Path to Pylint's report file, relative to projects root") - .category(PYTHON_CATEGORY) - .subCategory(PYLINT) - .onQualifiers(Qualifiers.PROJECT) - .defaultValue("") - .build(), - PylintConfiguration.class, - PylintSensor.class, - PylintImportSensor.class, - PylintRuleRepository.class); - } - private static void addBanditExtensions(Context context) { context.addExtension(BanditSensor.class); boolean externalIssuesSupported = context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2)); diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/CommandStreamConsumer.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/CommandStreamConsumer.java deleted file mode 100644 index b43a9bb1dc..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/CommandStreamConsumer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import org.sonar.api.utils.command.StreamConsumer; - -import java.util.LinkedList; -import java.util.List; - -class CommandStreamConsumer implements StreamConsumer { - private List data = new LinkedList<>(); - - @Override - public void consumeLine(String line) { - data.add(line); - } - - public List getData() { - return data; - } -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/Issue.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/Issue.java deleted file mode 100644 index ffc98db8ef..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/Issue.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -class Issue { - - private final String filename; - private final int line; - private final String ruleId; - private final String objname; - private final String descr; - - Issue(String filename, int line, String ruleId, String objname, String descr) { - this.filename = filename; - this.line = line; - this.ruleId = ruleId; - this.objname = objname; - this.descr = descr; - } - - @Override - public String toString() { - return "(" + filename + ", " + line + ", " + ruleId + ", " + objname + ", " + descr + ")"; - } - - String getFilename() { - return filename; - } - - int getLine() { - return line; - } - - String getRuleId() { - return ruleId; - } - - String getObjName() { - return objname; - } - - String getDescription() { - return descr; - } -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintArguments.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintArguments.java deleted file mode 100644 index 647461c207..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintArguments.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; -import org.sonar.api.utils.command.Command; -import org.sonar.api.utils.command.CommandExecutor; - -public class PylintArguments { - - private static final Pattern PYLINT_VERSION_PATTERN = Pattern.compile(".*pylint[^ ]* ([0-9\\.]+).*"); - private static final String[] ARGS_PYLINT_0_X = {"-i", "y", "-f", "parseable", "-r", "n"}; - private static final String[] ARGS_PYLINT_1_X = {"--msg-template", "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}", "-r", "n"}; - - private final String[] arguments; - - public PylintArguments(Command command) { - String pylintVersion = pylintVersion(command); - this.arguments = pylintVersion.startsWith("0") ? ARGS_PYLINT_0_X : ARGS_PYLINT_1_X; - } - - private static String pylintVersion(Command command) { - long timeout = 10_000; - CommandStreamConsumer out = new CommandStreamConsumer(); - CommandStreamConsumer err = new CommandStreamConsumer(); - CommandExecutor.create().execute(command, out, err, timeout); - Stream outputLines = Stream.concat(out.getData().stream(), err.getData().stream()); - - for (String outLine : (Iterable) outputLines::iterator) { - Matcher matcher = PYLINT_VERSION_PATTERN.matcher(outLine); - if (matcher.matches()) { - return matcher.group(1); - } - } - String message = String.format("Failed to determine pylint version with command: \"%s\", received %d line(s) of output:%n%s", - command.toCommandLine(), out.getData().size() + err.getData().size(), out.getData() + "\n" + err.getData()); - throw new IllegalArgumentException(message); - } - - public String[] arguments() { - return arguments; - } - -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintConfiguration.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintConfiguration.java deleted file mode 100644 index 7ab346f910..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintConfiguration.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.ExtensionPoint; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.config.Configuration; - -@ScannerSide -@ExtensionPoint -public class PylintConfiguration { - - public static final String PYLINT_CONFIG_KEY = "sonar.python.pylint_config"; - public static final String PYLINT_KEY = "sonar.python.pylint"; - - private final Configuration conf; - - public PylintConfiguration(Configuration conf) { - this.conf = conf; - } - - public String getPylintConfigPath(FileSystem fileSystem) { - String configPath = conf.get(PylintConfiguration.PYLINT_CONFIG_KEY).orElse(""); - 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 getPylintPath() { - return conf.get(PylintConfiguration.PYLINT_KEY).orElse(null); - } - -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintImportSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintImportSensor.java deleted file mode 100644 index 698f84d511..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintImportSensor.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; -import java.util.Set; -import javax.annotation.Nullable; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.rule.ActiveRule; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.batch.sensor.issue.NewIssue; -import org.sonar.api.config.Configuration; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.plugins.python.PythonReportSensor; -import org.sonar.plugins.python.warnings.AnalysisWarningsWrapper; - -public class PylintImportSensor extends PythonReportSensor { - public static final String REPORT_PATH_KEY = "sonar.python.pylint.reportPath"; - private static final String DEFAULT_REPORT_PATH = "pylint-reports/pylint-result-*.txt"; - - private static final Logger LOG = Loggers.get(PylintImportSensor.class); - private static final PylintRuleParser pylintRules = new PylintRuleParser(PylintRuleRepository.RULES_FILE); - private static final Set warningAlreadyLogged = new HashSet<>(); - - public PylintImportSensor(Configuration conf, AnalysisWarningsWrapper analysisWarnings) { - super(conf, analysisWarnings, "Pylint"); - } - - @Override - public void describe(SensorDescriptor descriptor) { - super.describe(descriptor); - descriptor - .createIssuesForRuleRepository(PylintRuleRepository.REPOSITORY_KEY) - .onlyWhenConfiguration(conf -> conf.hasKey(REPORT_PATH_KEY)); - } - - @Override - protected String reportPathKey() { - return REPORT_PATH_KEY; - } - - @Override - protected String defaultReportPath() { - return DEFAULT_REPORT_PATH; - } - - @Override - protected void processReports(final SensorContext context, List reports) { - List issues = new LinkedList<>(); - for (File report : reports) { - try { - issues.addAll(parse(report, context.fileSystem())); - } catch (java.io.FileNotFoundException e) { - LOG.error("Report '{}' cannot be found, details: '{}'", report, e); - } catch (IOException e) { - LOG.error("Report '{}' cannot be read, details: '{}'", report, e); - } - } - - saveIssues(issues, context); - } - - private static List parse(File report, FileSystem fileSystem) throws IOException { - List issues = new LinkedList<>(); - - PylintReportParser parser = new PylintReportParser(); - Scanner sc; - for (sc = new Scanner(report.toPath(), fileSystem.encoding().name()); sc.hasNext(); ) { - String line = sc.nextLine(); - Issue issue = parser.parseLine(line); - if (issue != null) { - issues.add(issue); - } - } - sc.close(); - return issues; - } - - private static void saveIssues(List issues, SensorContext context) { - FileSystem fileSystem = context.fileSystem(); - for (Issue pylintIssue : issues) { - String filepath = pylintIssue.getFilename(); - InputFile pyfile = fileSystem.inputFile(fileSystem.predicates().hasPath(filepath)); - if (pyfile != null) { - ActiveRule rule = context.activeRules().find(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, pylintIssue.getRuleId())); - processRule(pylintIssue, pyfile, rule, context); - } else { - LOG.warn("Cannot find the file '{}' in SonarQube, ignoring violation", filepath); - } - } - } - - public static void processRule(Issue pylintIssue, InputFile pyfile, @Nullable ActiveRule rule, SensorContext context) { - if (rule != null) { - NewIssue newIssue = context - .newIssue() - .forRule(rule.ruleKey()); - newIssue.at( - newIssue.newLocation() - .on(pyfile) - .at(pyfile.selectLine(pylintIssue.getLine())) - .message(pylintIssue.getDescription())); - newIssue.save(); - } else if (!pylintRules.hasRuleDefinition(pylintIssue.getRuleId())) { - logUnknownRuleWarning(pylintIssue.getRuleId()); - } - } - - private static void logUnknownRuleWarning(String ruleId) { - if (!warningAlreadyLogged.contains(ruleId)) { - warningAlreadyLogged.add(ruleId); - LOG.warn("Pylint rule '{}' is unknown in Sonar", ruleId); - } - } - - // Visible for testing - static void clearLoggedWarnings() { - warningAlreadyLogged.clear(); - } - -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzer.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzer.java deleted file mode 100644 index 6b3bba4414..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzer.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.util.LinkedList; -import java.util.List; -import javax.annotation.Nullable; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.command.Command; -import org.sonar.api.utils.command.CommandExecutor; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; - -public class PylintIssuesAnalyzer { - - private static final Logger LOG = Loggers.get(PylintIssuesAnalyzer.class); - - private static final String FALLBACK_PYLINT = "pylint"; - - private String pylint = null; - private String pylintConfigParam = null; - private PylintArguments pylintArguments; - - PylintIssuesAnalyzer(String pylintPath, String pylintConfigPath) { - this(pylintPath, pylintConfigPath, new PylintArguments(Command.create(pylintPathWithDefault(pylintPath)).addArgument("--version"))); - } - - PylintIssuesAnalyzer(String pylintPath, @Nullable String pylintConfigPath, PylintArguments arguments) { - pylint = pylintPathWithDefault(pylintPath); - - if (pylintConfigPath != null) { - if (!new File(pylintConfigPath).exists()) { - throw new IllegalStateException("Cannot find the pylint configuration file: " + pylintConfigPath); - } - pylintConfigParam = "--rcfile=" + pylintConfigPath; - } - - pylintArguments = arguments; - } - - private static String pylintPathWithDefault(@Nullable String pylintPath) { - if (pylintPath != null) { - if (!new File(pylintPath).exists()) { - throw new IllegalStateException("Cannot find the pylint executable: " + pylintPath); - } - return pylintPath; - } - return FALLBACK_PYLINT; - } - - public List analyze(String path, Charset charset, File out) throws IOException { - Command command = Command.create(pylint).addArguments(pylintArguments.arguments()).addArgument(path); - - if (pylintConfigParam != null) { - command.addArgument(pylintConfigParam); - } - - LOG.debug("Calling command: '{}'", command); - - long timeoutMS = 300_000; // =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 pylint malfunction - if (stdErr.getData().size() > 1) { - LOG.warn("Output on the error channel detected: this is probably due to a problem on pylint's side."); - String data = StringUtils.join(stdErr.getData(), "\n"); - LOG.warn("Content of the error stream: \n\"{}\"", data); - } - - String str = StringUtils.join(stdOut.getData(), "\n"); - Files.write(out.toPath(), str.getBytes(charset)); - - return parseOutput(stdOut.getData()); - } - - protected List parseOutput(List lines) { - List issues = new LinkedList<>(); - - PylintReportParser parser = new PylintReportParser(); - if (!lines.isEmpty()) { - for (String line : lines) { - Issue issue = parser.parseLine(line); - if (issue != null) { - issues.add(issue); - } - } - } - - return issues; - } -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintReportParser.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintReportParser.java deleted file mode 100644 index 0df734efbc..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintReportParser.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; - -public class PylintReportParser { - private static final Pattern PATTERN = Pattern.compile("(.+):([0-9]+): \\[(.*)\\] (.*)"); - private static final Logger LOG = Loggers.get(PylintReportParser.class); - - // Pylint 0.24 brings a nasty reidentifying of some rules... - // To avoid burdening of users with rule clones we map the ids. - // This workaround can die as soon as pylints <= 0.23.X become obsolete. - private static final Map ID_MAP = initializeIdMap(); - - private static Map initializeIdMap() { - Map map = new HashMap<>(); - map.put("E9900", "E1300"); - map.put("E9901", "E1301"); - map.put("E9902", "E1302"); - map.put("E9903", "E1303"); - map.put("E9904", "E1304"); - map.put("E9905", "E1305"); - map.put("E9906", "E1306"); - map.put("W6501", "W1201"); - map.put("W9900", "W1300"); - map.put("W9901", "W1301"); - return Collections.unmodifiableMap(map); - } - - public Issue parseLine(String line) { - // Parse the output of pylint. Example of the format: - // - // complexity/code_chunks.py:62: [W0104, list_compr] Statement seems to have no effect - // complexity/code_chunks.py:64: [C0111, list_compr_filter] Missing docstring - // ... - - Issue issue = null; - - int linenr; - String ruleid; - String objname; - String descr; - String filename; - - if (line.length() > 0) { - if (!isDetail(line)) { - Matcher m = PATTERN.matcher(line); - if (m.matches() && m.groupCount() == 4) { - filename = m.group(1); - linenr = Integer.valueOf(m.group(2)); - String[] parts = m.group(3).split(","); - - ruleid = ruleId(parts[0].trim()); - - if (parts.length == 2) { - objname = parts[1].trim(); - } else { - objname = ""; - } - - descr = m.group(4); - issue = new Issue(filename, linenr, ruleid, objname, descr); - } else { - LOG.debug("Cannot parse the line: {}", line); - } - } else { - LOG.trace("Classifying as detail and ignoring line '{}'", line); - } - } - - return issue; - } - - private static String ruleId(String ruleAndMessageIds) { - String ruleid = ruleAndMessageIds; - int parenthesisIndex = ruleid.indexOf('('); - if (parenthesisIndex > -1) { - ruleid = ruleid.substring(0, parenthesisIndex); - } - if (ID_MAP.containsKey(ruleid)) { - ruleid = ID_MAP.get(ruleid); - } - return ruleid; - } - - private static boolean isDetail(String line) { - char first = line.charAt(0); - return first == ' ' || first == '\t' || first == '\n'; - } -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleParser.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleParser.java deleted file mode 100644 index efe5f8881e..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleParser.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonarsource.analyzer.commons.xml.SafetyFactory; - -public class PylintRuleParser { - - private static final Logger LOG = Loggers.get(PylintRuleParser.class); - private Set definedRulesId = new HashSet<>(); - private StringBuilder currentKey = new StringBuilder(); - - public PylintRuleParser(String rulesPath) { - try (InputStream inputStream = getClass().getResourceAsStream(rulesPath)) { - XMLEventReader reader = SafetyFactory.createXMLInputFactory().createXMLEventReader(inputStream); - while (reader.hasNext()) { - onXmlEvent(reader.nextEvent()); - } - } catch (IOException | XMLStreamException | IllegalArgumentException e) { - LOG.warn("Unable to parse the Pylint rules definition XML file"); - } - - if (definedRulesId.isEmpty()) { - LOG.warn("No rule key found for Pylint"); - } - } - - private void onXmlEvent(XMLEvent event) { - if (event.isStartElement()) { - StartElement element = event.asStartElement(); - String elementName = element.getName().getLocalPart(); - if ("key".equals(elementName)) { - currentKey = new StringBuilder(); - } - } else if (event.isCharacters()) { - currentKey.append(event.asCharacters().getData()); - } else if (event.isEndElement() && "key".equals(event.asEndElement().getName().getLocalPart())) { - definedRulesId.add(currentKey.toString()); - } - } - - public boolean hasRuleDefinition(String ruleId) { - return definedRulesId.contains(ruleId); - } - -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleRepository.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleRepository.java deleted file mode 100644 index f91cd9bffd..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRuleRepository.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.server.rule.RulesDefinitionXmlLoader; -import org.sonar.plugins.python.Python; - -import static java.nio.charset.StandardCharsets.UTF_8; - -public class PylintRuleRepository implements RulesDefinition { - - public static final String REPOSITORY_NAME = "Pylint"; - public static final String REPOSITORY_KEY = REPOSITORY_NAME; - - public static final String RULES_FILE = "/org/sonar/plugins/python/pylint/rules.xml"; - private static final String REMEDIATION_FILE = "/org/sonar/plugins/python/pylint/remediation-cost.csv"; - - private final RulesDefinitionXmlLoader xmlLoader; - - public PylintRuleRepository(RulesDefinitionXmlLoader xmlLoader) { - this.xmlLoader = xmlLoader; - } - - @Override - public void define(Context context) { - NewRepository repository = context - .createRepository(REPOSITORY_KEY, Python.KEY) - .setName(REPOSITORY_NAME); - xmlLoader.load(repository, getClass().getResourceAsStream(RULES_FILE), UTF_8.name()); - defineRemediationFunction(repository); - repository.done(); - } - - private static void defineRemediationFunction(NewRepository repository) { - Map remediationCostMap = loadRemediationCostMap(); - for (NewRule rule : repository.rules()) { - String gap = remediationCostMap.get(rule.key()); - if (gap == null) { - throw new IllegalStateException("Missing remediation cost for rule " + rule.key()); - } else if (!gap.equals("null")) { - rule.setDebtRemediationFunction(rule.debtRemediationFunctions().linear(gap)); - } - } - } - - private static Map loadRemediationCostMap() { - Map map = new HashMap<>(); - try (Scanner scanner = new Scanner(PylintRuleRepository.class.getResourceAsStream(REMEDIATION_FILE), UTF_8.name())) { - while (scanner.hasNext()) { - String[] cols = scanner.next().split(","); - map.put(cols[0], cols[1]); - } - } - return map; - } - -} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java deleted file mode 100644 index 5852d17941..0000000000 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import org.apache.commons.io.FileUtils; -import org.sonar.api.batch.fs.FilePredicates; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.rule.ActiveRule; -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.config.Configuration; -import org.sonar.api.notifications.AnalysisWarnings; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.plugins.python.Python; - -public class PylintSensor implements Sensor { - - private static final Logger LOG = Loggers.get(PylintSensor.class); - - private final PylintConfiguration conf; - private final Configuration settings; - private PylintIssuesAnalyzer analyzer; - private final AnalysisWarnings analysisWarnings; - - public PylintSensor(PylintConfiguration conf, Configuration settings, AnalysisWarnings analysisWarnings) { - this.conf = conf; - this.settings = settings; - this.analysisWarnings = analysisWarnings; - } - - @Override - public void describe(SensorDescriptor descriptor) { - descriptor - .name("PylintSensor") - .onlyOnLanguage(Python.KEY) - .onlyOnFileType(InputFile.Type.MAIN) - .createIssuesForRuleRepository(PylintRuleRepository.REPOSITORY_KEY); - } - - boolean shouldExecute() { - return !settings.get(PylintImportSensor.REPORT_PATH_KEY).isPresent(); - } - - @Override - public void execute(SensorContext sensorContext) { - File workDir = new File(sensorContext.fileSystem().workDir(), "pylint"); - if (!sensorContext.activeRules().findByRepository("Pylint").isEmpty()) { - analysisWarnings.addUnique("Deprecation notice and future breaking changes: The import of Pylint issues will soon change. " + - "Please follow the instructions in documentation’s section \"Analyzing Source Code\" >> \"Languages\" >> \"Python\" >> \"Pylint\"."); - } - if (!shouldExecute() || !prepareWorkDir(workDir) || !initializeAnalyzer(sensorContext)) { - return; - } - - LOG.warn("Execution of Pylint is deprecated and will be removed." + - " Instead, Pylint should be executed before sonar-scanner and its report should be imported using the '" + PylintImportSensor.REPORT_PATH_KEY + "' property."); - - int i = 0; - FileSystem fileSystem = sensorContext.fileSystem(); - FilePredicates p = fileSystem.predicates(); - Iterable files = fileSystem.inputFiles(p.and(p.hasType(InputFile.Type.MAIN), p.hasLanguage(Python.KEY))); - for (InputFile file : files) { - try { - File out = new File(workDir, i + ".out"); - analyzeFile(sensorContext, file, out); - i++; - } catch (Exception e) { - LOG.warn("Cannot analyse file '{}', the following exception occurred:", file.toString(), e); - } - } - } - - private boolean initializeAnalyzer(SensorContext context) { - try { - String pylintConfigPath = conf.getPylintConfigPath(context.fileSystem()); - String pylintPath = conf.getPylintPath(); - analyzer = createAnalyzer(pylintConfigPath, pylintPath); - return true; - } catch (Exception e) { - LOG.warn("Unable to use pylint for analysis. Error:", e); - return false; - } - } - - // Visible for testing - PylintIssuesAnalyzer createAnalyzer(String pylintConfigPath, String pylintPath) { - return new PylintIssuesAnalyzer(pylintPath, pylintConfigPath); - } - - private void analyzeFile(SensorContext context, InputFile file, File out) throws IOException { - FileSystem fileSystem = context.fileSystem(); - - List issues = analyzer.analyze(file.absolutePath(), fileSystem.encoding(), out); - - for (Issue pylintIssue : issues) { - ActiveRule rule = context.activeRules().find(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, pylintIssue.getRuleId())); - PylintImportSensor.processRule(pylintIssue, file, rule, context); - } - } - - private static boolean prepareWorkDir(File dir) { - try { - FileUtils.forceMkdir(dir); - // directory is cleaned, because Sonar 3.0 will not do this for us - FileUtils.cleanDirectory(dir); - return true; - } catch (IOException e) { - LOG.warn("Cannot create directory '{}'. Error:", dir, e); - return false; - } - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java index 2a1a882333..ef92e93ff1 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java @@ -37,7 +37,7 @@ public class PythonPluginTest { public void testGetExtensions() { Version v74 = Version.create(7, 9); SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(v74, SonarQubeSide.SERVER, SonarEdition.DEVELOPER); - assertThat(extensions(runtime)).hasSize(25); + assertThat(extensions(runtime)).hasSize(18); assertThat(extensions(runtime)).contains(DefaultAnalysisWarningsWrapper.class); assertThat(extensions(SonarRuntimeImpl.forSonarLint(v74))).hasSize(5); } diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintArgumentsTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintArgumentsTest.java deleted file mode 100644 index f9aa457089..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintArgumentsTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import org.apache.commons.lang.SystemUtils; -import org.junit.Test; -import org.sonar.api.utils.command.Command; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PylintArgumentsTest { - - @Test - public void pylint_0_x() { - String[] arguments = new PylintArguments(command("pylint 0.28.0")).arguments(); - assertThat(arguments).containsOnly("-i", "y", "-f", "parseable", "-r", "n"); - } - - @Test - public void pylint_bat_0_x() { - String[] arguments = new PylintArguments(command("pylint.bat 0.28.0")).arguments(); - assertThat(arguments).containsOnly("-i", "y", "-f", "parseable", "-r", "n"); - } - - @Test - public void pylint_1_x() { - String[] arguments = new PylintArguments(command("pylint 1.1.0")).arguments(); - assertThat(arguments).containsOnly("--msg-template", "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}", "-r", "n"); - } - - @Test(expected = IllegalArgumentException.class) - public void unknown() { - new PylintArguments(command("")); - } - - private static Command command(String toOutput) { - if (SystemUtils.IS_OS_WINDOWS) { - return Command.create("cmd.exe").addArguments(new String[] {"/c", "echo", toOutput}); - } - return Command.create("echo").addArgument(toOutput); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintConfigurationTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintConfigurationTest.java deleted file mode 100644 index dc808e3a08..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintConfigurationTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.config.internal.ConfigurationBridge; -import org.sonar.api.config.internal.MapSettings; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PylintConfigurationTest { - - private MapSettings settings; - private PylintConfiguration pylintConfiguration; - - @Before - public void setUp() { - settings = new MapSettings(); - pylintConfiguration = new PylintConfiguration(new ConfigurationBridge(settings)); - } - - @Test - public void shouldGetCorrectPylintPath() { - DefaultFileSystem fs = new DefaultFileSystem(new File("").getAbsoluteFile()); - - assertThat(pylintConfiguration.getPylintConfigPath(fs)).isNull(); - - settings.setProperty(PylintConfiguration.PYLINT_CONFIG_KEY, (String) null); - assertThat(pylintConfiguration.getPylintConfigPath(fs)).isNull(); - - settings.setProperty(PylintConfiguration.PYLINT_CONFIG_KEY, ".pylintrc"); - assertThat(pylintConfiguration.getPylintConfigPath(fs)).isEqualTo(new File(".pylintrc").getAbsolutePath()); - - String absolutePath = new File("/absolute/.pylintrc").getAbsolutePath(); - settings.setProperty(PylintConfiguration.PYLINT_CONFIG_KEY, absolutePath); - assertThat(pylintConfiguration.getPylintConfigPath(fs)).isEqualTo(absolutePath); - } - - @Test - public void getPylintPath() { - String path = "test/path"; - settings.setProperty(PylintConfiguration.PYLINT_KEY, path); - - assertThat(pylintConfiguration.getPylintPath()).isEqualTo(path); - } -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintImportSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintImportSensorTest.java deleted file mode 100644 index ad6e25b75f..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintImportSensorTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import com.google.common.collect.ImmutableMap; -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.function.Predicate; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.fs.internal.TestInputFileBuilder; -import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; -import org.sonar.api.batch.rule.internal.NewActiveRule; -import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; -import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Configuration; -import org.sonar.api.config.internal.ConfigurationBridge; -import org.sonar.api.config.internal.MapSettings; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.plugins.python.Python; -import org.sonar.plugins.python.TestUtils; -import org.sonar.plugins.python.warnings.AnalysisWarningsWrapper; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.spy; - -public class PylintImportSensorTest { - - public static final String FILE1_PATH = "src/file1.py"; - public static final String RULE_C0103 = "C0103"; - public static final String RULE_C0111 = "C0111"; - private final File baseDir = new File("src/test/resources/org/sonar/plugins/python/pylint"); - private final SensorContextTester context = SensorContextTester.create(baseDir); - private final AnalysisWarningsWrapper analysisWarnings = spy(AnalysisWarningsWrapper.class); - - @Rule - public LogTester logTester = new LogTester(); - - @Test - public void parse_report() { - context.settings().setProperty(PylintImportSensor.REPORT_PATH_KEY, "pylint-report.txt"); - - File file = new File(baseDir, FILE1_PATH); - DefaultInputFile inputFile = TestInputFileBuilder.create("", FILE1_PATH) - .setLanguage(Python.KEY) - .initMetadata(TestUtils.fileContent(file, StandardCharsets.UTF_8)) - .build(); - context.fileSystem().add(inputFile); - - context.setActiveRules( - new ActiveRulesBuilder() - .addRule(new NewActiveRule.Builder() - .setRuleKey(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, RULE_C0103)) - .setName("Invalid name") - .build()) - .addRule(new NewActiveRule.Builder() - .setRuleKey(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, RULE_C0111)) - .setName("Missing docstring") - .build()) - .build()); - - PylintImportSensor sensor = new PylintImportSensor(context.config(), analysisWarnings); - sensor.execute(context); - assertThat(context.allIssues()).hasSize(3); - assertThat(context.allIssues()).extracting(issue -> issue.primaryLocation().inputComponent().key()) - .containsOnly(inputFile.key()); - } - - @Test - public void logsOnlyUnknownRules () { - context.settings().setProperty(PylintImportSensor.REPORT_PATH_KEY, "pylint-report-unknown-rules.txt"); - - File file = new File(baseDir, FILE1_PATH); - DefaultInputFile inputFile = TestInputFileBuilder.create("", FILE1_PATH) - .setLanguage(Python.KEY) - .initMetadata(TestUtils.fileContent(file, StandardCharsets.UTF_8)) - .build(); - context.fileSystem().add(inputFile); - - context.setActiveRules( - new ActiveRulesBuilder() - .addRule(new NewActiveRule.Builder() - .setRuleKey(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, RULE_C0103)) - .setName("Invalid name") - .build()) - .build()); - - PylintImportSensor sensor = new PylintImportSensor(context.config(), analysisWarnings); - PylintImportSensor.clearLoggedWarnings(); - sensor.execute(context); - assertThat(context.allIssues()).hasSize(1); - assertThat(context.allIssues()).extracting(issue -> issue.primaryLocation().inputComponent().key()).containsOnly(inputFile.key()); - assertThat(context.allIssues()).extracting(issue -> issue.ruleKey().rule()).containsExactly(RULE_C0103); - assertThat(logTester.logs(LoggerLevel.WARN)).containsExactly("Pylint rule 'C8888' is unknown in Sonar", "Pylint rule 'C9999' is unknown in Sonar"); - } - - @Test - public void sensor_descriptor() { - DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); - new PylintImportSensor(context.config(), analysisWarnings).describe(descriptor); - assertThat(descriptor.name()).isEqualTo("PylintImportSensor"); - assertThat(descriptor.languages()).containsOnly("py"); - assertThat(descriptor.type()).isEqualTo(InputFile.Type.MAIN); - assertThat(descriptor.ruleRepositories()).containsExactly(PylintRuleRepository.REPOSITORY_KEY); - Predicate configurationPredicate = descriptor.configurationPredicate(); - assertThat(configurationPredicate.test(configuration(ImmutableMap.of(PylintImportSensor.REPORT_PATH_KEY, "something")))).isTrue(); - assertThat(configurationPredicate.test(configuration(ImmutableMap.of("xxx", "yyy")))).isFalse(); - } - - @Test - public void no_default_report_log() { - SensorContextTester defaultContext = SensorContextTester.create(baseDir); - PylintImportSensor sensor = new PylintImportSensor(defaultContext.config(), analysisWarnings); - sensor.execute(defaultContext); - assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("No report was found for sonar.python.pylint.reportPath using default pattern pylint-reports/pylint-result-*.txt"); - } - - private static Configuration configuration(Map mapproperties) { - return new ConfigurationBridge(new MapSettings().addProperties(mapproperties)); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerIT.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerIT.java deleted file mode 100644 index 07fbbf7fa6..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerIT.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.nio.charset.StandardCharsets; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.plugins.python.pylint.PylintIssuesAnalyzerTest.RESOURCE_DIR; - -public class PylintIssuesAnalyzerIT { - - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); - - @Test - public void issuesTest() throws Exception { - String pylintrcResource = RESOURCE_DIR + "/org/sonar/plugins/python/pylint/pylintrc_sample"; - String codeChunksResource = "/org/sonar/plugins/python/code_chunks_2.py"; - String codeChunksPathName = getClass().getResource(codeChunksResource).getPath(); - String pylintPath = null; - File out = tempFolder.newFile(); - - List issues = new PylintIssuesAnalyzer(pylintPath, pylintrcResource).analyze(codeChunksPathName, StandardCharsets.UTF_8, out); - assertThat(issues).isNotEmpty(); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerTest.java deleted file mode 100644 index 2ad17550d2..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintIssuesAnalyzerTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import org.junit.Test; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -public class PylintIssuesAnalyzerTest { - - private static final Logger LOG = Loggers.get(PylintIssuesAnalyzerTest.class); - static final String RESOURCE_DIR = "src/test/resources"; - - @Test - public void shouldParseCorrectly() { - String resourceName = "/org/sonar/plugins/python/pylint/sample_pylint_output.txt"; - String pylintConfigPath = null; - String pylintPath = null; - List lines = readFile(resourceName); - List issues = analyzer(pylintPath, pylintConfigPath).parseOutput(lines); - assertThat(issues.size()).isEqualTo(21); - } - - @Test - public void shouldParseCorrectlyNewFormat() { - String resourceName = "/org/sonar/plugins/python/pylint/sample_pylint_output_new_format.txt"; - String pylintConfigPath = null; - String pylintPath = null; - List lines = readFile(resourceName); - List issues = analyzer(pylintPath, pylintConfigPath).parseOutput(lines); - assertThat(issues.size()).isEqualTo(1); - assertThat(issues.get(0).getRuleId()).isEqualTo("C0111"); - } - - @Test - public void shouldParseCorrectlyOutputWithWindowsPaths() { - String resourceName = "/org/sonar/plugins/python/pylint/sample_pylint_output_with_win_paths.txt"; - String pylintConfigPath = null; - String pylintPath = null; - List lines = readFile(resourceName); - List issues = analyzer(pylintPath, pylintConfigPath).parseOutput(lines); - assertThat(issues.size()).isEqualTo(1); - } - - @Test - public void shouldMapIssuesIdsCorrectly() { - String resourceOld = "/org/sonar/plugins/python/pylint/sample_pylint_output_oldids.txt"; - String resourceNew = "/org/sonar/plugins/python/pylint/sample_pylint_output_newids.txt"; - String pylintConfigPath = null; - String pylintPath = null; - List linesOld = readFile(resourceOld); - List linesNew = readFile(resourceNew); - List issuesOld = analyzer(pylintPath, pylintConfigPath).parseOutput(linesOld); - List issuesNew = analyzer(pylintPath, pylintConfigPath).parseOutput(linesNew); - assertThat(getIds(issuesOld)).isEqualTo(getIds(issuesNew)); - } - - @Test - public void shouldWorkWithValidCustomConfig() { - String resourceName = "/org/sonar/plugins/python/pylint/pylintrc_sample"; - String pylintConfigPath = getClass().getResource(resourceName).getPath(); - String pylintPath = null; - analyzer(pylintPath, pylintConfigPath); - } - - @Test(expected = IllegalStateException.class) - public void shouldFailIfGivenInvalidConfig() { - String pylintConfigPath = "xx_path_that_doesnt_exist_xx"; - String pylintPath = null; - analyzer(pylintPath, pylintConfigPath); - } - - @Test - public void shouldInstantiateWhenGivenValidParams() { - String pylintrcResource = "/org/sonar/plugins/python/pylint/pylintrc_sample"; - String pylintrcPath = getClass().getResource(pylintrcResource).getPath(); - String executableResource = "/org/sonar/plugins/python/pylint/executable"; - String executablePath = getClass().getResource(executableResource).getPath(); - final String[] validParameters = - { - null, null, - executablePath, null, - null, pylintrcPath, - executablePath, pylintrcPath - }; - - int numberOfParams = validParameters.length; - for(int i = 0; i readFile(String path) { - try { - return Files.readAllLines(Paths.get(RESOURCE_DIR, path), UTF_8); - } catch (IOException e) { - LOG.error("Cannot read the file '{}'", path); - return Collections.emptyList(); - } - } - - private static List getIds(List issues){ - List ids = new LinkedList<>(); - for(Issue issue: issues) { - ids.add(issue.getRuleId()); - } - return ids; - } - - private static PylintIssuesAnalyzer analyzer(String pylintPath, String pylintConfigPath) { - PylintArguments arguments = mock(PylintArguments.class); - return new PylintIssuesAnalyzer(pylintPath, pylintConfigPath, arguments); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleParserTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleParserTest.java deleted file mode 100644 index 8c201cd9fe..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleParserTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.utils.log.LogTester; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PylintRuleParserTest { - - private static final String NO_RULE_FOUND_MESSAGE = "No rule key found for Pylint"; - - @Rule - public LogTester logTester = new LogTester(); - - @Test - public void hasExpectedRules() { - PylintRuleParser pylintRuleParser = new PylintRuleParser(PylintRuleRepository.RULES_FILE); - assertThat(pylintRuleParser.hasRuleDefinition("C0102")).isTrue(); - assertThat(pylintRuleParser.hasRuleDefinition("C9999")).isFalse(); - } - - @Test - public void logsWhenEmpty() { - new PylintRuleParser("/org/sonar/plugins/python/pylint/empty.xml"); - assertThat(logTester.logs()).containsExactly(NO_RULE_FOUND_MESSAGE); - } - - @Test - public void logsWhenFileNotFound() { - new PylintRuleParser("/org/sonar/plugins/python/pylint/no-file.xml"); - assertThat(logTester.logs()).containsExactly("Unable to parse the Pylint rules definition XML file", NO_RULE_FOUND_MESSAGE); - } - - @Test - public void logsWhenException() { - new PylintRuleParser("/org/sonar/plugins/python/pylint/pylint-report.txt"); - assertThat(logTester.logs()).containsExactly("Unable to parse the Pylint rules definition XML file", NO_RULE_FOUND_MESSAGE); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleRepositoryTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleRepositoryTest.java deleted file mode 100644 index c5f7560a34..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRuleRepositoryTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.util.List; -import org.junit.Test; -import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.server.rule.RulesDefinitionXmlLoader; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PylintRuleRepositoryTest { - - @Test - public void createRulesTest() { - PylintRuleRepository ruleRepository = new PylintRuleRepository(new RulesDefinitionXmlLoader()); - RulesDefinition.Context context = new RulesDefinition.Context(); - ruleRepository.define(context); - - RulesDefinition.Repository repository = context.repository(PylintRuleRepository.REPOSITORY_KEY); - - assertThat(repository).isNotNull(); - assertThat(repository.language()).isEqualTo("py"); - assertThat(repository.name()).isEqualTo("Pylint"); - - List rules = repository.rules(); - assertThat(rules).isNotNull(); - assertThat(rules).hasSize(370); - - long rulesWithoutRemediationCost = rules.stream() - .filter(rule -> rule.debtRemediationFunction() == null) - .count(); - assertThat(rulesWithoutRemediationCost).isEqualTo(0); - } - -} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java deleted file mode 100644 index 23a134fbd7..0000000000 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * SonarQube Python Plugin - * Copyright (C) 2011-2020 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.plugins.python.pylint; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.Nullable; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.mockito.Mockito; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.fs.internal.TestInputFileBuilder; -import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; -import org.sonar.api.batch.rule.internal.NewActiveRule; -import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; -import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.internal.ConfigurationBridge; -import org.sonar.api.config.internal.MapSettings; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.plugins.python.Python; -import org.sonar.plugins.python.TestUtils; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class PylintSensorTest { - - private static final String FILE1_PATH = "file1.py"; - private static final File moduleBaseDir = new File("src/test/resources/org/sonar/plugins/python/pylint").getAbsoluteFile(); - public static final String C0103_RULE_KEY = "C0103"; - - @Rule - public LogTester logTester = new LogTester(); - @Rule - public TemporaryFolder tmpDir = new TemporaryFolder(); - - private File workDir = null; - private PylintConfiguration conf; - - @Before - public void setup() throws Exception { - conf = mock(PylintConfiguration.class); - if (workDir == null) { - setupWorkDir(); - } - } - - @Test - public void sensor_descriptor() { - DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); - List warnings = new ArrayList<>(); - new PylintSensor(conf, new ConfigurationBridge(new MapSettings()), warnings::add).describe(descriptor); - assertThat(descriptor.name()).isEqualTo("PylintSensor"); - assertThat(descriptor.languages()).containsOnly("py"); - assertThat(descriptor.type()).isEqualTo(InputFile.Type.MAIN); - assertThat(descriptor.ruleRepositories()).containsExactly(PylintRuleRepository.REPOSITORY_KEY); - assertThat(warnings).isEmpty(); - } - - @Test - public void shouldExecuteOnlyWhenNecessary() { - assertThat(shouldExecute(null)).isTrue(); - assertThat(shouldExecute("result.txt")).isFalse(); - } - - @Test - public void testWhenNoPylint() { - SensorContextTester context = SensorContextTester.create(workDir); - context.fileSystem().setWorkDir(workDir.toPath()); - when(conf.getPylintPath()).thenReturn("[---/this/should/definitely/not/exist---]"); - - List warnings = new ArrayList<>(); - PylintSensor sensor = new PylintSensor(conf, new ConfigurationBridge(new MapSettings()), warnings::add); - sensor.execute(context); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Unable to use pylint for analysis. Error:"); - assertThat(warnings).isEmpty(); - } - - @Test - public void testWithFakePylint() throws IOException { - SensorContextTester context = SensorContextTester.create(workDir); - context.fileSystem().setWorkDir(workDir.toPath()); - - createInputFile(workDir, context, FILE1_PATH); - - context.setActiveRules( - new ActiveRulesBuilder() - .addRule(new NewActiveRule.Builder() - .setRuleKey(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, C0103_RULE_KEY)) - .setName("Invalid name") - .build()) - .build()); - - Issue issue1 = new Issue(FILE1_PATH, 1, C0103_RULE_KEY, "name1", "desc1"); - Issue issue2 = new Issue(FILE1_PATH, 2, "C0111", "name2", "desc2"); - Issue issue3 = new Issue(FILE1_PATH, 3, "C9999", "name3", "desc3"); - - MapSettings settings = new MapSettings(); - settings.setProperty(PylintImportSensor.REPORT_PATH_KEY, "report-is-set"); - List warnings = new ArrayList<>(); - PylintSensor sensor = spy(new PylintSensor(conf, new ConfigurationBridge(settings), warnings::add)); - PylintIssuesAnalyzer analyzer = mock(PylintIssuesAnalyzer.class); - String absolutePath = new File(FILE1_PATH).getAbsolutePath().replace("\\", "/"); - when(analyzer.analyze(Mockito.eq(absolutePath), any(), any())).thenReturn(asList(issue1, issue2, issue3)); - doReturn(analyzer).when(sensor).createAnalyzer(any(), any()); - - sensor.execute(context); - - // sensor was not executed as the 'sonar.python.pylint.reportPath' is set - assertThat(context.allIssues()).hasSize(0); - assertThat(warnings).hasSize(1); - assertThat(warnings.get(0)).isEqualTo("Deprecation notice and future breaking changes: The import of Pylint issues will soon change. " + - "Please follow the instructions in documentation’s section \"Analyzing Source Code\" >> \"Languages\" >> \"Python\" >> \"Pylint\"."); - - PylintImportSensor.clearLoggedWarnings(); - settings.clear(); - sensor.execute(context); - - assertThat(context.allIssues()).hasSize(1); - assertThat(context.allIssues().iterator().next().ruleKey().rule()).isEqualTo(C0103_RULE_KEY); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Pylint rule 'C9999' is unknown in Sonar"); - assertThat(logTester.logs(LoggerLevel.WARN)).doesNotContain("Pylint rule 'C0111' is unknown in Sonar"); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Execution of Pylint is deprecated and will be removed." + - " Instead, Pylint should be executed before sonar-scanner and its report should be imported using the 'sonar.python.pylint.reportPath' property."); - } - - @Test - public void testErrorOnFileContinueAnalysis() throws IOException { - SensorContextTester context = SensorContextTester.create(workDir); - context.fileSystem().setWorkDir(workDir.toPath()); - - createInputFile(workDir, context, FILE1_PATH); - createInputFile(workDir, context, "file2.py"); - - List warnings = new ArrayList<>(); - PylintSensor sensor = spy(new PylintSensor(conf, new ConfigurationBridge(new MapSettings()), warnings::add)); - PylintIssuesAnalyzer analyzer = mock(PylintIssuesAnalyzer.class); - when(analyzer.analyze(any(), any(), any())).thenThrow(RuntimeException.class); - doReturn(analyzer).when(sensor).createAnalyzer(any(), any()); - - sensor.execute(context); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Cannot analyse file 'file1.py', the following exception occurred:"); - // no rule activated = no warning - assertThat(warnings).isEmpty(); - verify(analyzer, times(2)).analyze(any(), any(), any()); - } - - private static void createInputFile(File baseDir, SensorContextTester context, String filePath) { - File file = new File(baseDir, filePath); - DefaultInputFile inputFile = TestInputFileBuilder.create("", filePath) - .setLanguage(Python.KEY) - .initMetadata(TestUtils.fileContent(file, StandardCharsets.UTF_8)) - .build(); - context.fileSystem().add(inputFile); - } - - private boolean shouldExecute(@Nullable String pylintReportPath) { - MapSettings settings = new MapSettings(); - if (pylintReportPath != null) { - settings.setProperty(PylintImportSensor.REPORT_PATH_KEY, pylintReportPath); - } - PylintSensor sensor = new PylintSensor(conf, new ConfigurationBridge(settings), s -> {}); - return sensor.shouldExecute(); - } - - private void setupWorkDir() throws Exception { - workDir = tmpDir.newFolder("python-pylint"); - - Path file1SourcePath = new File(moduleBaseDir, "src/file1.py").toPath(); - Path file1TargetPath = new File(workDir, FILE1_PATH).toPath(); - Path file2SourcePath = new File(moduleBaseDir, "src/file2.py").toPath(); - Path file2TargetPath = new File(workDir, "file2.py").toPath(); - - Files.copy(file1SourcePath, file1TargetPath); - Files.copy(file2SourcePath, file2TargetPath); - } - -} diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/empty.xml b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/empty.xml deleted file mode 100644 index 0a3d245827..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/empty.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/executable b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/executable deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylintrc_sample b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylintrc_sample deleted file mode 100644 index 0dbb2680c7..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylintrc_sample +++ /dev/null @@ -1,309 +0,0 @@ -# lint Python modules using external checkers. -# -# This is the main checker controlling the other ones and the reports -# generation. It is itself both a raw checker and an astng checker in order -# to: -# * handle message activation / deactivation at the module level -# * handle some basic but necessary stats'data (number of classes, methods...) -# -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add to the black list. It should be a base name, not a -# path. You may set this option multiple times. -ignore=CVS .svn - -# Pickle collected data for later comparisons. -persistent=yes - -# Set the cache size for astng objects. -cache-size=500 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable only checker(s) with the given id(s). This option conflicts with the -# disable-checker option -#enable-checker= - -# Enable all checker(s) except those with the given id(s). This option -# conflicts with the enable-checker option -#disable-checker= - -# Enable all messages in the listed categories (IRCWEF). -#enable-msg-cat= - -# Disable all messages in the listed categories (IRCWEF). -disable-msg-cat=I - -# Enable the message(s) with the given id(s). -#enable-msg= - -# Disable the message(s) with the given id(s). -disable-msg=W0704 C0103 - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=text - -# Include message's id in output -include-ids=no - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (R0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (R0004). -comment=no - -# Enable the report(s) with the given id(s). -#enable-report= - -# Disable the report(s) with the given id(s). -#disable-report= - - -# try to find bugs in the code using type inference -# -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. -generated-members=REQUEST,acl_users,aq_parent - - -# checks for -# * unused variables / imports -# * undefined variables -# * redefinition of variable from builtins or from an outer scope -# * use of variable before assignment -# -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching names used for dummy variables (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -# checks for : -# * doc strings -# * modules / classes / functions / methods / arguments / variables name -# * number of arguments, local variables, branches, returns and statements in -# functions, methods -# * required module attributes -# * dangerous default values as arguments -# * redefinition of function / method / class -# * uses of the global statement -# -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - - -# checks for : -# * methods without self as first argument -# * overridden methods signature -# * access only to existent members via self -# * attributes not defined in the __init__ method -# * supported interfaces implementation -# * unreachable code -# -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - - -# checks for sign of poor/misdesign: -# * number of methods, attributes, local variables... -# * size, complexity of functions, methods -# -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branchs=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -# checks for -# * external modules dependencies -# * relative / wildcard imports -# * cyclic imports -# * uses of deprecated modules -# -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report R0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report R0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report R0402 must -# not be disabled) -int-import-graph= - - -# checks for: -# * warning notes in the code like FIXME, XXX -# * PEP 263: source code with non ascii character but no encoding declaration -# -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -# checks for similarities and duplicated code. This computation may be -# memory / CPU intensive, so you should disable it if you experiments some -# problems. -# -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -# checks for : -# * unauthorized constructions -# * strict indentation -# * line length -# * use of <> instead of != -# -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=80 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' From 877c7c75032269ee74412c1ed27c689b6a02a73b Mon Sep 17 00:00:00 2001 From: Guillaume Dequenne Date: Thu, 9 Jul 2020 14:22:18 +0200 Subject: [PATCH 3/5] SONARPY-749 Add new style importing of Pylint reports (#808) --- .../python/it/plugin/PylintReportTest.java | 22 +- .../com/sonar/python/it/plugin/Tests.java | 1 + .../plugins/python/ExternalIssuesSensor.java | 55 +- .../sonar/plugins/python/PythonPlugin.java | 19 +- ...eportReader.java => TextReportReader.java} | 87 +- .../plugins/python/bandit/BanditSensor.java | 14 +- .../plugins/python/flake8/Flake8Sensor.java | 49 +- .../python/pylint/PylintRulesDefinition.java | 36 + .../plugins/python/pylint/PylintSensor.java | 61 + .../sonar/plugins/python/pylint/rules.json | 1852 +++++++++++++++++ .../plugins/python/PythonPluginTest.java | 8 +- .../pylint/PylintRulesDefinitionTest.java | 52 + .../python/pylint/PylintSensorTest.java | 240 +++ .../org/sonar/plugins/python/pylint/file1.py | 30 + .../pylint/pylint-report-unknown-rules.txt | 7 - .../plugins/python/pylint/pylint-report.txt | 4 - .../plugins/python/pylint/pylint_brackets.txt | 10 + .../pylint/pylint_names_in_brackets.txt | 1 + .../pylint/pylint_report_default_format.txt | 14 + .../python/pylint/pylint_report_no_column.txt | 4 + .../pylint/pylint_report_unknown_files.txt | 13 + .../python/pylint/sample_pylint_output.txt | 21 - .../sample_pylint_output_new_format.txt | 2 - .../pylint/sample_pylint_output_newids.txt | 10 - .../pylint/sample_pylint_output_oldids.txt | 10 - .../sample_pylint_output_with_win_paths.txt | 1 - .../sonar/plugins/python/pylint/src/file1.py | 1 - .../sonar/plugins/python/pylint/src/file2.py | 1 - 28 files changed, 2461 insertions(+), 164 deletions(-) rename sonar-python-plugin/src/main/java/org/sonar/plugins/python/{flake8/Flake8ReportReader.java => TextReportReader.java} (52%) create mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRulesDefinition.java create mode 100644 sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java create mode 100644 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.json create mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRulesDefinitionTest.java create mode 100644 sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/file1.py delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report-unknown-rules.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report.txt create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_brackets.txt create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_names_in_brackets.txt create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_default_format.txt create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_no_column.txt create mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_unknown_files.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_new_format.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_newids.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_oldids.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_with_win_paths.txt delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file1.py delete mode 100644 sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file2.py diff --git a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java index 8a98ac2d04..153294b9d1 100644 --- a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java +++ b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java @@ -49,38 +49,42 @@ public void import_report() { @Test public void missing_report() { analyseProjectWithReport("missing"); - assertThat(issues()).hasSize(0); + assertThat(issues()).isEmpty(); } @Test public void invalid_report() { BuildResult result = analyseProjectWithReport("invalid.txt"); assertThat(result.getLogs()).contains("Cannot parse the line: trash"); - assertThat(issues()).hasSize(0); + assertThat(issues()).isEmpty(); } @Test public void unknown_rule() { BuildResult result = analyseProjectWithReport("rule-unknown.txt"); - assertThat(result.getLogs()).doesNotContain("Pylint rule 'C0102' is unknown"); - assertThat(result.getLogs()).containsOnlyOnce("Pylint rule 'C8888' is unknown"); - assertThat(result.getLogs()).containsOnlyOnce("Pylint rule 'C9999' is unknown"); - assertThat(issues()).hasSize(0); + assertThat(issues()).hasSize(4); + } + + @Test + public void multiple_reports() { + analyseProjectWithReport("pylint-report.txt, rule-unknown.txt"); + assertThat(issues()).hasSize(8); } private static List issues() { return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(PROJECT))).getIssuesList(); } - private static BuildResult analyseProjectWithReport(String reportPath) { + private static BuildResult analyseProjectWithReport(String reportPaths) { ORCHESTRATOR.resetData(); ORCHESTRATOR.getServer().provisionProject(PROJECT, PROJECT); - ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT, "py", "pylint-rules"); + ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT, "py", "no_rule"); + return ORCHESTRATOR.executeBuild( SonarScanner.create() .setDebugLogs(true) .setProjectDir(new File("projects/pylint_project")) - .setProperty("sonar.python.pylint.reportPath", reportPath)); + .setProperty("sonar.python.pylint.reportPaths", reportPaths)); } } diff --git a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java index 62278cf0fa..0da2259c53 100644 --- a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java +++ b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java @@ -47,6 +47,7 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ BanditReportTest.class, + PylintReportTest.class, MetricsTest.class, CPDTest.class, CoverageTest.class, diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java index 84e25d7d9f..42f384262c 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java @@ -21,19 +21,27 @@ import java.io.File; +import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.sonar.api.utils.log.Logger; import java.util.stream.Collectors; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.issue.NewExternalIssue; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.rules.RuleType; +import org.sonar.api.utils.log.Logger; import org.sonarsource.analyzer.commons.ExternalReportProvider; +import org.sonarsource.analyzer.commons.internal.json.simple.parser.ParseException; public abstract class ExternalIssuesSensor implements Sensor { private static final int MAX_LOGGED_FILE_NAMES = 20; + private static final Long DEFAULT_CONSTANT_DEBT_MINUTES = 5L; @Override public void describe(SensorDescriptor descriptor) { @@ -47,10 +55,18 @@ public void describe(SensorDescriptor descriptor) { public void execute(SensorContext context) { Set unresolvedInputFiles = new HashSet<>(); List reportFiles = ExternalReportProvider.getReportFiles(context, reportPathKey()); - reportFiles.forEach(report -> importReport(report, context, unresolvedInputFiles)); + reportFiles.forEach(report -> importExternalReport(report, context, unresolvedInputFiles)); logUnresolvedInputFiles(unresolvedInputFiles); } + private void importExternalReport(File reportPath, SensorContext context, Set unresolvedInputFiles) { + try { + importReport(reportPath, context, unresolvedInputFiles); + } catch (IOException | ParseException | RuntimeException e) { + logFileCantBeRead(e, reportPath); + } + } + private void logUnresolvedInputFiles(Set unresolvedInputFiles) { if (unresolvedInputFiles.isEmpty()) { return; @@ -62,7 +78,40 @@ private void logUnresolvedInputFiles(Set unresolvedInputFiles) { logger().warn("Failed to resolve {} file path(s) in " + linterName() + " report. No issues imported related to file(s): {}", unresolvedInputFiles.size(), fileList); } - protected abstract void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles); + private void logFileCantBeRead(Exception e, File reportPath) { + logger().error("No issues information will be saved as the report file '{}' can't be read. {}: {}" + , reportPath, e.getClass().getSimpleName(), e.getMessage()); + } + + protected void saveIssue(SensorContext context, TextReportReader.Issue issue, Set unresolvedInputFiles, String linterKey) { + InputFile inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(issue.filePath)); + if (inputFile == null) { + unresolvedInputFiles.add(issue.filePath); + return; + } + + NewExternalIssue newExternalIssue = context.newExternalIssue(); + newExternalIssue + .type(RuleType.CODE_SMELL) + .severity(Severity.MAJOR) + .remediationEffortMinutes(DEFAULT_CONSTANT_DEBT_MINUTES); + + NewIssueLocation primaryLocation = newExternalIssue.newLocation() + .message(issue.message) + .on(inputFile); + if (issue.columnNumber != null && issue.columnNumber < inputFile.selectLine(issue.lineNumber).end().lineOffset()) { + primaryLocation.at(inputFile.newRange(issue.lineNumber, issue.columnNumber, issue.lineNumber, issue.columnNumber + 1)); + } else { + // Pylint formatted issues might not provide column information + primaryLocation.at(inputFile.selectLine(issue.lineNumber)); + } + + newExternalIssue.at(primaryLocation); + newExternalIssue.engineId(linterKey).ruleId(issue.ruleKey); + newExternalIssue.save(); + } + + protected abstract void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) throws IOException, ParseException; protected abstract String linterName(); diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java index c2ccfd1613..311d0eb471 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java @@ -31,6 +31,8 @@ import org.sonar.plugins.python.coverage.PythonCoverageSensor; import org.sonar.plugins.python.flake8.Flake8RulesDefinition; import org.sonar.plugins.python.flake8.Flake8Sensor; +import org.sonar.plugins.python.pylint.PylintRulesDefinition; +import org.sonar.plugins.python.pylint.PylintSensor; import org.sonar.plugins.python.warnings.DefaultAnalysisWarningsWrapper; import org.sonar.plugins.python.xunit.PythonXUnitSensor; @@ -75,6 +77,7 @@ public void define(Context context) { context.addExtension(DefaultAnalysisWarningsWrapper.class); addCoberturaExtensions(context); addXUnitExtensions(context); + addPylintExtensions(context); addBanditExtensions(context); addFlake8Extensions(context); } @@ -148,9 +151,21 @@ private static void addBanditExtensions(Context context) { } } + private static void addPylintExtensions(Context context) { + context.addExtensions(PylintSensor.class, + PropertyDefinition.builder(PylintSensor.REPORT_PATH_KEY) + .name("Pylint Report Files") + .description("Paths (absolute or relative) to report files with Pylint issues.") + .category(EXTERNAL_ANALYZERS_CATEGORY) + .subCategory(PYTHON_CATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .multiValues(true) + .build(), + PylintRulesDefinition.class); + } + private static void addFlake8Extensions(Context context) { - context.addExtension(Flake8Sensor.class); - context.addExtensions( + context.addExtensions(Flake8Sensor.class, PropertyDefinition.builder(Flake8Sensor.REPORT_PATH_KEY) .name("Flake8 Report Files") .description("Paths (absolute or relative) to report files with Flake8 issues.") diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8ReportReader.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/TextReportReader.java similarity index 52% rename from sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8ReportReader.java rename to sonar-python-plugin/src/main/java/org/sonar/plugins/python/TextReportReader.java index 70fc64d80b..3df25d3051 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8ReportReader.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/TextReportReader.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.plugins.python.flake8; +package org.sonar.plugins.python; import java.io.File; import java.io.IOException; @@ -31,11 +31,22 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -public class Flake8ReportReader { +/** + * Common implementation to parse Flake8 and Pylint reports + */ +public class TextReportReader { + + private static final Pattern DEFAULT_PATTERN = Pattern.compile("(.+):(\\d+):(\\d+): (\\S+[^:]):? (.*)"); + private static final Pattern LEGACY_PATTERN = Pattern.compile("(.+):(\\d+): \\[(.*)\\] (.*)"); + private static final Logger LOG = Loggers.get(TextReportReader.class); + public static final int COLUMN_ZERO_BASED = 0; + public static final int COLUMN_ONE_BASED = 1; + + private final int reportOffset; - private static final Logger LOG = Loggers.get(Flake8ReportReader.class); - private static final Pattern DEFAULT_PATTERN = Pattern.compile("(.+):(\\d+):(\\d+): (\\S+) (.*)"); - private static final Pattern PYLINT_PATTERN = Pattern.compile("(.+):(\\d+): \\[(.*)\\] (.*)"); + public TextReportReader(int columnStartIndex) { + this.reportOffset = columnStartIndex; + } public List parse(File report, FileSystem fileSystem) throws IOException { List issues = new ArrayList<>(); @@ -50,51 +61,55 @@ public List parse(File report, FileSystem fileSystem) throws IOException return issues; } - private static Issue parseLine(String line) { - + private Issue parseLine(String line) { if (line.length() > 0) { - if (!startsWithWhitespace(line)) { - Matcher m = DEFAULT_PATTERN.matcher(line); - if (m.matches()) { - String filePath = m.group(1); - int lineNumber = Integer.parseInt(m.group(2)); - int columnNumber = Integer.parseInt(m.group(3)); - String ruleKey = m.group(4); - String message = m.group(5); - return new Issue(filePath, ruleKey, message, lineNumber, columnNumber); - } - m = PYLINT_PATTERN.matcher(line); - if (m.matches()) { - String filePath = m.group(1); - int lineNumber = Integer.parseInt(m.group(2)); - String ruleKey = m.group(3); - String message = m.group(4); - return new Issue(filePath, ruleKey, message, lineNumber, null); - } - LOG.debug("Cannot parse the line: {}", line); - } else { - LOG.debug("Classifying as detail and ignoring line '{}'", line); + Matcher m = TextReportReader.DEFAULT_PATTERN.matcher(line); + if (m.matches()) { + return extractDefaultStyleIssue(m); + } + m = TextReportReader.LEGACY_PATTERN.matcher(line); + if (m.matches()) { + return extractLegacyStyleIssue(m); } + LOG.debug("Cannot parse the line: {}", line); } return null; } - private static boolean startsWithWhitespace(String line) { - char first = line.charAt(0); - return first == ' ' || first == '\t' || first == '\n'; + private Issue extractDefaultStyleIssue(Matcher m) { + String filePath = m.group(1); + int lineNumber = Integer.parseInt(m.group(2)); + int columnNumber = Integer.parseInt(m.group(3)); + // Flake8 column numbering starts at 1 + columnNumber -= this.reportOffset; + String ruleKey = m.group(4); + String message = m.group(5); + return new Issue(filePath, ruleKey, message, lineNumber, columnNumber); + } + + private static Issue extractLegacyStyleIssue(Matcher m) { + String filePath = m.group(1); + int lineNumber = Integer.parseInt(m.group(2)); + String ruleKey = m.group(3); + int keyLastIndex = ruleKey.indexOf("("); + if (keyLastIndex > 0) { + ruleKey = ruleKey.substring(0, keyLastIndex); + } + String message = m.group(4); + return new Issue(filePath, ruleKey, message, lineNumber, null); } public static class Issue { - String filePath; + public final String filePath; - String ruleKey; + public final String ruleKey; - String message; + public final String message; - Integer lineNumber; + public final Integer lineNumber; - Integer columnNumber; + public final Integer columnNumber; public Issue(String filePath, String ruleKey, String message, Integer lineNumber, @Nullable Integer columnNumber) { this.filePath = filePath; diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java index 352ebb5576..6c3cf60806 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java @@ -51,15 +51,11 @@ public class BanditSensor extends ExternalIssuesSensor { private static final Long DEFAULT_CONSTANT_DEBT_MINUTES = 5L; @Override - protected void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) { - try (InputStream in = new FileInputStream(reportPath)) { - LOG.info("Importing {}", reportPath); - boolean engineIdIsSupported = context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 4)); - BanditJsonReportReader.read(in, issue -> saveIssue(context, issue, unresolvedInputFiles, engineIdIsSupported)); - } catch (IOException | ParseException | RuntimeException e) { - LOG.error("No issues information will be saved as the report file '{}' can't be read. " + - e.getClass().getSimpleName() + ": " + e.getMessage(), reportPath, e); - } + protected void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) throws IOException, ParseException { + InputStream in = new FileInputStream(reportPath); + LOG.info("Importing {}", reportPath); + boolean engineIdIsSupported = context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 4)); + BanditJsonReportReader.read(in, issue -> saveIssue(context, issue, unresolvedInputFiles, engineIdIsSupported)); } private static void saveIssue(SensorContext context, Issue issue, Set unresolvedInputFiles, boolean engineIdIsSupported) { diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java index d87f5aa541..365b6af6b6 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java @@ -23,15 +23,12 @@ import java.io.IOException; import java.util.List; import java.util.Set; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.issue.NewExternalIssue; -import org.sonar.api.batch.sensor.issue.NewIssueLocation; -import org.sonar.api.rules.RuleType; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.plugins.python.ExternalIssuesSensor; +import org.sonar.plugins.python.TextReportReader; +import org.sonar.plugins.python.TextReportReader.Issue; public class Flake8Sensor extends ExternalIssuesSensor { @@ -41,46 +38,10 @@ public class Flake8Sensor extends ExternalIssuesSensor { public static final String LINTER_KEY = "flake8"; public static final String REPORT_PATH_KEY = "sonar.python.flake8.reportPaths"; - private static final Long DEFAULT_CONSTANT_DEBT_MINUTES = 5L; - @Override - protected void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) { - try { - List issues = new Flake8ReportReader().parse(reportPath, context.fileSystem()); - issues.forEach(i -> saveIssue(context, i, unresolvedInputFiles)); - } catch (IOException e) { - LOG.error("No issues information will be saved as the report file '{}' can't be read. " + - e.getClass().getSimpleName() + ": " + e.getMessage(), reportPath, e); - } - } - - private static void saveIssue(SensorContext context, Flake8ReportReader.Issue issue, Set unresolvedInputFiles) { - InputFile inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(issue.filePath)); - if (inputFile == null) { - unresolvedInputFiles.add(issue.filePath); - return; - } - - NewExternalIssue newExternalIssue = context.newExternalIssue(); - newExternalIssue - .type(RuleType.CODE_SMELL) - .severity(Severity.MAJOR) - .remediationEffortMinutes(DEFAULT_CONSTANT_DEBT_MINUTES); - - NewIssueLocation primaryLocation = newExternalIssue.newLocation() - .message(issue.message) - .on(inputFile); - if (issue.columnNumber != null && issue.columnNumber < inputFile.selectLine(issue.lineNumber).end().lineOffset() + 1) { - inputFile.selectLine(issue.lineNumber).end().lineOffset(); - primaryLocation.at(inputFile.newRange(issue.lineNumber, issue.columnNumber - 1, issue.lineNumber, issue.columnNumber)); - } else { - // Pylint formatted issues don't provide column information - primaryLocation.at(inputFile.selectLine(issue.lineNumber)); - } - - newExternalIssue.at(primaryLocation); - newExternalIssue.engineId(LINTER_KEY).ruleId(issue.ruleKey); - newExternalIssue.save(); + protected void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) throws IOException { + List issues = new TextReportReader(TextReportReader.COLUMN_ONE_BASED).parse(reportPath, context.fileSystem()); + issues.forEach(i -> saveIssue(context, i, unresolvedInputFiles, LINTER_KEY)); } @Override diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRulesDefinition.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRulesDefinition.java new file mode 100644 index 0000000000..83117d6e04 --- /dev/null +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintRulesDefinition.java @@ -0,0 +1,36 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.plugins.python.pylint; + +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.plugins.python.Python; +import org.sonarsource.analyzer.commons.ExternalRuleLoader; + +public class PylintRulesDefinition implements RulesDefinition { + + private static final String RULES_JSON = "org/sonar/plugins/python/pylint/rules.json"; + private static final ExternalRuleLoader RULE_LOADER = new ExternalRuleLoader(PylintSensor.LINTER_KEY, PylintSensor.LINTER_NAME, RULES_JSON, Python.KEY); + + + @Override + public void define(RulesDefinition.Context context) { + RULE_LOADER.createExternalRuleRepository(context); + } +} diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java new file mode 100644 index 0000000000..1e808985eb --- /dev/null +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java @@ -0,0 +1,61 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.plugins.python.pylint; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.plugins.python.ExternalIssuesSensor; +import org.sonar.plugins.python.TextReportReader; +import org.sonar.plugins.python.TextReportReader.Issue; + +public class PylintSensor extends ExternalIssuesSensor { + + private static final Logger LOG = Loggers.get(PylintSensor.class); + + public static final String LINTER_NAME = "Pylint"; + public static final String LINTER_KEY = "pylint"; + public static final String REPORT_PATH_KEY = "sonar.python.pylint.reportPaths"; + + @Override + protected void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) throws IOException { + List issues = new TextReportReader(TextReportReader.COLUMN_ZERO_BASED).parse(reportPath, context.fileSystem()); + issues.forEach(i -> saveIssue(context, i, unresolvedInputFiles, LINTER_KEY)); + } + + @Override + protected String reportPathKey() { + return REPORT_PATH_KEY; + } + + @Override + protected String linterName() { + return LINTER_NAME; + } + + @Override + protected Logger logger() { + return LOG; + } +} diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.json b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.json new file mode 100644 index 0000000000..89ae29a3aa --- /dev/null +++ b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.json @@ -0,0 +1,1852 @@ +[ + { + "key": "C0102", + "name": "Black listed name", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0103", + "name": "Invalid name", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0111", + "name": "Missing docstring", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0112", + "name": "Empty docstring", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0113", + "name": "Useless negation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0121", + "name": "Singleton comparison", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0122", + "name": "Misplaced comparison constant", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0123", + "name": "Using type() instead of isinstance() for a typecheck.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0200", + "name": "Consider using enumerate instead of iterating with range and len", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0201", + "name": "Consider iterating the dictionary directly instead of calling .keys()", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0202", + "name": "Class method should have \"cls\" as first argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0203", + "name": "Metaclass method should have \"mcs\" as first argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0204", + "name": "Metaclass class method first argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0205", + "name": "Class __slots__ should be a non-string iterable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0301", + "name": "Line too long", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0302", + "name": "Too many lines in module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0303", + "name": "Trailing whitespace", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0304", + "name": "Final newline missing", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0305", + "name": "Trailing newlines", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0321", + "name": "More than one statement on a single line", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0322", + "name": "Operator not preceded by a space", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0323", + "name": "Operator not followed by a space", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0324", + "name": "Comma not followed by a space", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0325", + "name": "Unnecessary parentheses", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0326", + "name": "Wrong number of spaces around an operator, bracket, or comma, or before a block opener", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0327", + "name": "Mixed line endings LF and CRLF", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0328", + "name": "Unexpected line ending format", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0330", + "name": "Bad continuation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0401", + "name": "Wrong spelling of a word in a comment", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0402", + "name": "Wrong spelling of a word in a docstring", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0403", + "name": "Invalid characters in a docstring", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0410", + "name": "Multiple imports on one line", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0411", + "name": "Wrong import order", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0412", + "name": "Ungrouped imports", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0413", + "name": "Wrong import position", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C1001", + "name": "Old-style class defined.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C0414", + "name": "Import alias does not rename original package", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "C1801", + "name": "Do not use `len(SEQUENCE)` to determine if a sequence is empty", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0001", + "name": "Syntax error", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0011", + "name": "Unrecognized file option", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0012", + "name": "Bad option value", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0100", + "name": "__init__ method is a generator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0101", + "name": "Explicit return in __init__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0102", + "name": "Redefined function/class/method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0103", + "name": "Usage of 'break' or 'continue' outside of a loop", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0104", + "name": "Return outside function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0105", + "name": "Yield outside function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0106", + "name": "Return with argument inside generator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0107", + "name": "Use of a non-existent operator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0108", + "name": "Duplicate argument name in function definition", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0109", + "name": "Missing argument to reversed()", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0110", + "name": "Abstract class instantiated", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0111", + "name": "The first reversed() argument is not a sequence", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0112", + "name": "More than one starred expression in assignment", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0113", + "name": "Starred assignment target must be in a list or tuple", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0114", + "name": "Can use starred expression only in assignment target", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0115", + "name": "Name is nonlocal and global", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0116", + "name": "'continue' not supported inside 'finally' clause", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0117", + "name": "Nonlocal name found without binding", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0119", + "name": "Format function is not called on str", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0202", + "name": "Method hidden by attribute of super class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0203", + "name": "Access to member before its definition", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0211", + "name": "Method has no argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0213", + "name": "Method should have \"self\" as first argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0221", + "name": "Implemented interface must be a class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0222", + "name": "Missing method from interface", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0235", + "name": "__exit__ must accept 3 arguments: type, value, traceback", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0236", + "name": "Invalid object in __slots__, must contain only non empty strings", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0237", + "name": "Assigning to attribute not defined in class slots", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0238", + "name": "Invalid __slots__ object", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0239", + "name": "Inheriting from non-class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0240", + "name": "Inconsistent method resolution order", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0241", + "name": "Duplicate bases", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0301", + "name": "__iter__ returns non-iterator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0302", + "name": "Unexpected special method signature", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0303", + "name": "__len__ does not return non-negative integer", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0401", + "name": "Import error", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0402", + "name": "Attempted relative import beyond top-level package", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0501", + "name": "Non-ASCII characters found but no encoding specified (PEP 263)", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0502", + "name": "Wrong encoding specified", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0503", + "name": "Unknown encoding specified", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0601", + "name": "Using variable before assignment", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0602", + "name": "Undefined variable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0603", + "name": "Undefined variable name in __all__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0604", + "name": "Invalid object in __all__, must contain only strings", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0611", + "name": "Undefined name in module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0632", + "name": "Unbalanced tuple unpacking", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0633", + "name": "Attempting to unpack a non-sequence", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0701", + "name": "Bad except clauses order", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0702", + "name": "Raising only allowed for classes, instances or strings", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0703", + "name": "Exception context set to something which is not an exception, nor None", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0704", + "name": "The raise statement is not inside an except clause", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0710", + "name": "Raising a new style class which doesn't inherit from BaseException", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0711", + "name": "NotImplemented raised - should raise NotImplementedError", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E0712", + "name": "Catching an exception which doesn't inherit from Exception", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1001", + "name": "Use of __slots__ on an old style class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1002", + "name": "Use of super on an old style class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1003", + "name": "Bad first argument given to super", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1004", + "name": "Missing argument to super()", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1101", + "name": "Access of nonexistent member", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1102", + "name": "Calling of not callable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1103", + "name": "Accessing nonexistent member (type information incomplete)", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1111", + "name": "Assigning result of a function call, where the function has no return", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1120", + "name": "Too few arguments", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1121", + "name": "Too many positional arguments for function call", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1122", + "name": "Duplicate keyword argument in function call", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1123", + "name": "Passing unexpected keyword argument in function call", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1124", + "name": "Multiple values passed for parameter in function call", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1125", + "name": "Missing mandatory keyword argument in call", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1126", + "name": "Sequence index is not an int, slice, or instance with __index__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1127", + "name": "Slice index is not an int, None, or instance with __index__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1128", + "name": "Assigning result of a function call, where the function returns None", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1129", + "name": "Context manager doesn't implement __enter__ and __exit__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1130", + "name": "Invalid unary operand type", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1131", + "name": "Unsupported binary operation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1132", + "name": "Multiple values for keyword argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1133", + "name": "Non-iterable value used in an iterating context", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1134", + "name": "Non-mapping value used in a mapping context", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1135", + "name": "Unsupported membership test", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1136", + "name": "Subscripted value doesn't support subscription", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1137", + "name": "Object does not support item assignment", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1138", + "name": "Object does not support item deletion", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1139", + "name": "Invalid metaclass used", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1140", + "name": "Dict key is unhashable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1200", + "name": "Unsupported logging format character", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1201", + "name": "Logging format string ends in middle of conversion specifier", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1205", + "name": "Too many arguments for logging format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1206", + "name": "Not enough arguments for logging format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1300", + "name": "Unsupported format character", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1301", + "name": "Format string ends in middle of conversion specifier", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1302", + "name": "Mixing named and unnamed conversion specifiers in format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1303", + "name": "Expected mapping for format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1304", + "name": "Missing key in format string dictionary", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1305", + "name": "Too many arguments for format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1306", + "name": "Not enough arguments for format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1307", + "name": "Argument does not match format type", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1310", + "name": "Suspicious argument in lstrip/rstrip", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1507", + "name": "Env manipulation functions does not support type argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1601", + "name": "print statement used", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1602", + "name": "Parameter unpacking specified", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1603", + "name": "Implicit unpacking of exceptions is not supported in Python 3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1604", + "name": "Use raise ErrorClass(args) instead of raise ErrorClass, args.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1605", + "name": "Use of the `` operator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1606", + "name": "Use of long suffix", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1607", + "name": "Use of the <> operator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1608", + "name": "Use of old octal literal", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1609", + "name": "Import * only allowed at module level", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1610", + "name": "Non-ascii bytes literals not supported in 3.x", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1700", + "name": "Yield inside async function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "E1701", + "name": "Async context manager doesn't implement __aenter__ and __aexit__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0001", + "name": "Analysis failed", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0002", + "name": "Internal Pylint error", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0003", + "name": "Ignored builtin module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0004", + "name": "Unexpected inferred value", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0010", + "name": "Error while code parsing", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0202", + "name": "Unable to check methods signature", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0220", + "name": "Failed to resolve interfaces", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0321", + "name": "Format detection error", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "F0401", + "name": "Unable to import module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0001", + "name": "Unable to run raw checkers on built-in module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0010", + "name": "Unable to consider inline option", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0011", + "name": "Locally disabling message", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0012", + "name": "Locally enabling message", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0013", + "name": "Ignoring entire file", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0020", + "name": "Suppressed message", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0021", + "name": "Useless suppression of message", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0022", + "name": "Deprecated pragma", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I0023", + "name": "Use symbolic message", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "I1101", + "name": "Non-existent member of C extension", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0123", + "name": "Comparison to literal", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0124", + "name": "Redundant comparison", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0201", + "name": "Method could be a function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0202", + "name": "Consider using a decorator instead of calling classmethod", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0203", + "name": "Consider using a decorator instead of calling staticmethod", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0205", + "name": "Class inherits from object, can be safely removed from bases in python3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0401", + "name": "Cyclic import", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0801", + "name": "Similar lines", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0901", + "name": "Too many ancestors", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0902", + "name": "Too many instance attributes", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0903", + "name": "Too few public methods", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0904", + "name": "Too many public methods", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0911", + "name": "Too many return statements", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0912", + "name": "Too many branches", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0913", + "name": "Too many arguments", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0914", + "name": "Too many local variables", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0915", + "name": "Too many statements", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0916", + "name": "Too many boolean expressions in if statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0921", + "name": "Abstract class not referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0922", + "name": "Abstract class used too few times", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R0923", + "name": "Interface not implemented", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1701", + "name": "Consider merging isinstance calls", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1702", + "name": "Too many nested blocks", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1703", + "name": "Simplifiable if statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1704", + "name": "Redefining argument with local name", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1705", + "name": "Unnecessary \"else\" after \"return\"", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1706", + "name": "Consider using ternary", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1707", + "name": "Disallow trailing comma tuple", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1708", + "name": "Do not raise StopIteration in generator, use return statement instead", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1709", + "name": "Boolean expression may be simplified", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1710", + "name": "Either all return statements in a function should return an expression, or none of them should.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1711", + "name": "Useless return at end of function or method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1712", + "name": "Consider using tuple unpacking for swapping variables", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1713", + "name": "Consider using str.join(sequence) for concatenating strings from an iterable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1714", + "name": "Consider using \"in\"", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1715", + "name": "Consider using dict.get for getting values from a dict if a key is present or a default if not", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1716", + "name": "Simplify chained comparison between the operands", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1717", + "name": "Consider using a dictionary comprehension", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1718", + "name": "Consider using a set comprehension", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1719", + "name": "Simplifiable if expression", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "R1720", + "name": "Unnecessary \"else\" after \"raise\"", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0101", + "name": "Unreachable code", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0102", + "name": "Dangerous default value as argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0104", + "name": "Statement seems to have no effect", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0105", + "name": "String statement has no effect", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0106", + "name": "Expression is assigned to nothing", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0107", + "name": "Unnecessary pass statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0108", + "name": "Lambda may not be necessary", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0109", + "name": "Duplicate key in dictionary", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0110", + "name": "map/filter on lambda could be replaced by comprehension", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0111", + "name": "Assignments should not be made to new Python keywords", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0120", + "name": "Else clause on loop without a break statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0121", + "name": "Use raise ErrorClass(args) instead of raise ErrorClass, args.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0122", + "name": "Use of exec", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0123", + "name": "Use of eval", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0124", + "name": "Following \"as\" with another context manager looks like a tuple.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0125", + "name": "Using a conditional statement with a constant value", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0141", + "name": "Used black listed builtin function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0142", + "name": "Used * or ** magic", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0143", + "name": "Comparing against a callable, did you omit the parenthesis?", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0150", + "name": "Statement in finally block may swallow exception", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0199", + "name": "Assert called on a 2-uple", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0201", + "name": "Attribute defined outside __init__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0211", + "name": "Static method with \"self\" or \"cls\" as first argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0212", + "name": "Access to a protected member of a client class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0221", + "name": "Parameter number discrepancy", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0222", + "name": "Method signature discrepancy", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0223", + "name": "Abstract method is not overridden", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0231", + "name": "__init__ method from base class is not called", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0232", + "name": "Class has no __init__ method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0233", + "name": "__init__ method from a non direct base class is called", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0234", + "name": "__iter__ returns non-iterator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0235", + "name": "Useless super delegation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0301", + "name": "Unnecessary semicolon", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0311", + "name": "Bad indentation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0312", + "name": "Mixed tabs/spaces indentation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0331", + "name": "Use of the <> operator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0332", + "name": "Use of \"l\" as long integer identifier", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0333", + "name": "Use of the `` operator", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0401", + "name": "Wildcard import", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0402", + "name": "Uses of a deprecated module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0403", + "name": "Relative import", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0404", + "name": "Reimport", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0406", + "name": "Module imports itself", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0410", + "name": "__future__ import is not the first non docstring statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0511", + "name": "Task marker found", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0512", + "name": "Source line cannot be decoded using the specified source file encoding", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0601", + "name": "Global variable undefined at the module level", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0602", + "name": "Unassigned global variable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0603", + "name": "Using the global statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0604", + "name": "Using the global statement at the module level", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0611", + "name": "Unused import", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0612", + "name": "Unused variable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0613", + "name": "Unused argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0614", + "name": "Unused import from wildcard import", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0621", + "name": "Redefining name from outer scope", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0622", + "name": "Redefining built-in", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0623", + "name": "Redefining name in exception handler", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0631", + "name": "Using possibly undefined loop variable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0632", + "name": "Possible unbalanced tuple unpacking", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0633", + "name": "Attempting to unpack a non-sequence", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0640", + "name": "Cell variable defined in loop", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0641", + "name": "Possibly unused variable", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0642", + "name": "Invalid assignment in method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0701", + "name": "Raising a string exception", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0702", + "name": "No exception type(s) specified", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0703", + "name": "Catching too general exception", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0704", + "name": "Except doesn't do anything", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0705", + "name": "Catching previously caught exception type", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0706", + "name": "The except handler raises immediately", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0710", + "name": "Exception doesn't inherit from standard \"Exception\" class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0711", + "name": "Exception to catch is the result of a binary operation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0712", + "name": "Implicit unpacking of exceptions is not supported in Python 3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0715", + "name": "Exception arguments suggest string formatting might be intended", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W0716", + "name": "Invalid exception operation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1001", + "name": "Use of \"property\" on an old style class", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1111", + "name": "Assigning to function call which only returns None", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1113", + "name": "Keyword argument before variable positional arguments list", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1201", + "name": "Specify string format arguments as logging function parameters", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1202", + "name": "Logging format interpolation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1203", + "name": "Usage of % formatting in logging functions", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1300", + "name": "Format string dictionary key should be a string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1301", + "name": "Unused key in format string dictionary", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1302", + "name": "Invalid format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1303", + "name": "Missing keyword argument for format string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1304", + "name": "Unused format argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1305", + "name": "Format string contains both automatic field numbering and manual field specification", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1306", + "name": "Missing format attribute", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1307", + "name": "Using invalid lookup key in format specifier", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1308", + "name": "Duplicate string formatting argument", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1401", + "name": "Anomalous backslash escape", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1402", + "name": "Anomalous Unicode escape in byte string", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1403", + "name": "Implicit string concatenation", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1501", + "name": "Invalid mode for open", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1502", + "name": "Using datetime.time in a boolean context.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1503", + "name": "Redundant unittest assert", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1505", + "name": "Using deprecated method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1506", + "name": "threading.Thread needs the target function", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1507", + "name": "Using copy.copy(os.environ). Use os.environ.copy() instead.", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1508", + "name": "Invalid type in env manipulation functions", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1509", + "name": "Using preexec_fn keyword which may be unsafe in the presence of threads", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1601", + "name": "apply built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1602", + "name": "basestring built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1603", + "name": "buffer built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1604", + "name": "cmp built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1605", + "name": "coerce built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1606", + "name": "execfile built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1607", + "name": "file built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1608", + "name": "long built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1609", + "name": "raw_input built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1610", + "name": "reduce built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1611", + "name": "StandardError built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1612", + "name": "unicode built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1613", + "name": "xrange built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1614", + "name": "__coerce__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1615", + "name": "__delslice__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1616", + "name": "__getslice__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1617", + "name": "__setslice__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1618", + "name": "import missing `from __future__ import absolute_import`", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1619", + "name": "division w/o __future__ statement", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1620", + "name": "Calling a dict.iter*() method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1621", + "name": "Calling a dict.view*() method", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1622", + "name": "Called a next() method on an object", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1623", + "name": "Assigning to a class's __metaclass__ attribute", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1624", + "name": "Indexing exceptions will not work on Python 3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1625", + "name": "Raising a string exception", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1626", + "name": "reload built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1627", + "name": "__oct__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1628", + "name": "__hex__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1629", + "name": "__nonzero__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1630", + "name": "__cmp__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1632", + "name": "input built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1633", + "name": "round built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1634", + "name": "intern built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1635", + "name": "unichr built-in referenced", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1636", + "name": "map built-in referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1637", + "name": "zip built-in referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1638", + "name": "range built-in referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1639", + "name": "filter built-in referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1640", + "name": "Using the cmp argument for list.sort / sorted", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1641", + "name": "Implementing __eq__ without also implementing __hash__", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1642", + "name": "__div__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1643", + "name": "__idiv__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1644", + "name": "__rdiv__ method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1645", + "name": "Exception.message removed in Python 3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1646", + "name": "non-text encoding used in str.decode", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1647", + "name": "sys.maxint removed in Python 3", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1649", + "name": "Accessing a deprecated function on the string module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1650", + "name": "Using str.translate with deprecated deletechars parameters", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1651", + "name": "Accessing a deprecated function on the itertools module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1652", + "name": "Accessing a deprecated fields on the types module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1653", + "name": "next method defined", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1654", + "name": "dict.items referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1655", + "name": "dict.keys referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1656", + "name": "dict.values referenced when not iterating", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1657", + "name": "Accessing a removed attribute on the operator module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1658", + "name": "Accessing a removed attribute on the urllib module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1659", + "name": "Accessing a removed xreadlines attribute", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1660", + "name": "Accessing a removed attribute on the sys module", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1661", + "name": "Using an exception object that was bound by an except handler", + "url": "http://pylint.pycqa.org/en/latest/" + }, + { + "key": "W1662", + "name": "Using a variable that was bound inside a comprehension", + "url": "http://pylint.pycqa.org/en/latest/" + } +] diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java index ef92e93ff1..78e2feb982 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonPluginTest.java @@ -35,11 +35,11 @@ public class PythonPluginTest { @Test public void testGetExtensions() { - Version v74 = Version.create(7, 9); - SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(v74, SonarQubeSide.SERVER, SonarEdition.DEVELOPER); - assertThat(extensions(runtime)).hasSize(18); + Version v79 = Version.create(7, 9); + SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(v79, SonarQubeSide.SERVER, SonarEdition.DEVELOPER); + assertThat(extensions(runtime)).hasSize(21); assertThat(extensions(runtime)).contains(DefaultAnalysisWarningsWrapper.class); - assertThat(extensions(SonarRuntimeImpl.forSonarLint(v74))).hasSize(5); + assertThat(extensions(SonarRuntimeImpl.forSonarLint(v79))).hasSize(5); } private static List extensions(SonarRuntime runtime) { diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRulesDefinitionTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRulesDefinitionTest.java new file mode 100644 index 0000000000..adeb1c617d --- /dev/null +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintRulesDefinitionTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.plugins.python.pylint; + +import org.junit.Test; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PylintRulesDefinitionTest { + + @Test + public void pylint_external_repository() { + RulesDefinition.Context context = new RulesDefinition.Context(); + PylintRulesDefinition rulesDefinition = new PylintRulesDefinition(); + rulesDefinition.define(context); + + assertThat(context.repositories()).hasSize(1); + RulesDefinition.Repository repository = context.repository("external_pylint"); + assertThat(repository).isNotNull(); + assertThat(repository.name()).isEqualTo("Pylint"); + assertThat(repository.language()).isEqualTo("py"); + assertThat(repository.isExternal()).isTrue(); + assertThat(repository.rules().size()).isEqualTo(370); + + RulesDefinition.Rule rule = repository.rule("C0121"); + assertThat(rule).isNotNull(); + assertThat(rule.name()).isEqualTo("Singleton comparison"); + assertThat(rule.htmlDescription()).isEqualTo("See description of Pylint rule C0121 at " + + "the Pylint website."); + assertThat(rule.debtRemediationFunction().type().name()).isEqualTo("CONSTANT_ISSUE"); + assertThat(rule.debtRemediationFunction().baseEffort()).isEqualTo("5min"); + assertThat(rule.tags()).isEmpty(); + } +} diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java new file mode 100644 index 0000000000..560cdb0265 --- /dev/null +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java @@ -0,0 +1,240 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.plugins.python.pylint; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.batch.sensor.issue.IssueLocation; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.rules.RuleType; +import org.sonar.api.utils.Version; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +public class PylintSensorTest { + + private static final String PYLINT_FILE = "python-project:pylint/file1.py"; + private static final String PYLINT_REPORT_DEFAULT_FORMAT = "pylint_report_default_format.txt"; + private static final String PYLINT_REPORT_NO_COLUMN = "pylint_report_no_column.txt"; + + private static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "org", "sonar", "plugins", "python", "pylint"); + + private static PylintSensor pylintSensor = new PylintSensor(); + + @Rule + public LogTester logTester = new LogTester(); + + @Test + public void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + pylintSensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of Pylint issues"); + assertThat(sensorDescriptor.languages()).containsOnly("py"); + assertThat(sensorDescriptor.configurationPredicate()).isNotNull(); + assertNoErrorWarnLogs(logTester); + } + + @Test + public void issues_default_format() throws IOException { + List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_DEFAULT_FORMAT); + assertThat(externalIssues).hasSize(10); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.ruleKey()).hasToString("external_pylint:C0114"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + IssueLocation firstPrimaryLoc = first.primaryLocation(); + assertThat(firstPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(firstPrimaryLoc.message()) + .isEqualTo("Missing module docstring (missing-module-docstring)"); + TextRange firstTextRange = firstPrimaryLoc.textRange(); + assertThat(firstTextRange).isNotNull(); + assertThat(firstTextRange.start().line()).isEqualTo(1); + assertThat(firstTextRange.start().lineOffset()).isZero(); + assertThat(firstTextRange.end().line()).isEqualTo(1); + assertThat(firstTextRange.end().lineOffset()).isEqualTo(1); + + // Issue on column >= last column is reported on whole line instead + ExternalIssue last = externalIssues.get(9); + IssueLocation lastPrimaryLoc = last.primaryLocation(); + TextRange lastTextRange = lastPrimaryLoc.textRange(); + assertThat(lastTextRange).isNotNull(); + assertThat(lastTextRange.start().line()).isEqualTo(1); + assertThat(lastTextRange.start().lineOffset()).isZero(); + assertThat(lastTextRange.end().line()).isEqualTo(1); + assertThat(lastTextRange.end().lineOffset()).isEqualTo(13); + + assertNoErrorWarnLogs(logTester); + } + + @Test + public void issues_no_column_info() throws IOException { + List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_NO_COLUMN); + assertThat(externalIssues).hasSize(3); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.ruleKey()).hasToString("external_pylint:C0111"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + IssueLocation firstPrimaryLoc = first.primaryLocation(); + assertThat(firstPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(firstPrimaryLoc.message()) + .isEqualTo("Missing module docstring"); + TextRange firstTextRange = firstPrimaryLoc.textRange(); + assertThat(firstTextRange).isNotNull(); + assertThat(firstTextRange.start().line()).isEqualTo(1); + assertThat(firstTextRange.start().lineOffset()).isZero(); + assertThat(firstTextRange.end().line()).isEqualTo(1); + assertThat(firstTextRange.end().lineOffset()).isEqualTo(13); + + ExternalIssue second = externalIssues.get(1); + assertThat(second.ruleKey()).hasToString("external_pylint:C0103"); + assertThat(second.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + IssueLocation secondPrimaryLoc = second.primaryLocation(); + assertThat(secondPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(secondPrimaryLoc.message()).isEqualTo("Invalid argument name \"n\""); + TextRange secondTextRange = secondPrimaryLoc.textRange(); + assertThat(secondTextRange).isNotNull(); + assertThat(secondTextRange.start().line()).isEqualTo(1); + assertThat(secondTextRange.start().lineOffset()).isZero(); + assertThat(secondTextRange.end().line()).isEqualTo(1); + assertThat(secondTextRange.end().lineOffset()).isEqualTo(13); + + ExternalIssue third = externalIssues.get(2); + assertThat(third.ruleKey()).hasToString("external_pylint:C0111"); + assertThat(third.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(third.severity()).isEqualTo(Severity.MAJOR); + IssueLocation thirdPrimaryLoc = third.primaryLocation(); + assertThat(thirdPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(thirdPrimaryLoc.message()).isEqualTo("Missing function docstring"); + + assertNoErrorWarnLogs(logTester); + assertThat(onlyOneLogElement(logTester.logs(LoggerLevel.DEBUG))) + .isEqualTo("Cannot parse the line: ************* Module src.prod"); + } + + @Test + public void issues_other_formats() throws IOException { + List externalIssues = executeSensorImporting(7, 9, "pylint_brackets.txt"); + assertThat(externalIssues).hasSize(10); + ExternalIssue first = externalIssues.get(0); + assertThat(first.ruleKey()).hasToString("external_pylint:E1300"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + IssueLocation firstPrimaryLoc = first.primaryLocation(); + assertThat(firstPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(firstPrimaryLoc.message()) + .isEqualTo("message"); + + externalIssues = executeSensorImporting(7, 9, "pylint_names_in_brackets.txt"); + assertThat(externalIssues).hasSize(1); + first = externalIssues.get(0); + assertThat(first.ruleKey()).hasToString("external_pylint:C0111"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + firstPrimaryLoc = first.primaryLocation(); + assertThat(firstPrimaryLoc.inputComponent().key()).isEqualTo(PYLINT_FILE); + assertThat(firstPrimaryLoc.message()) + .isEqualTo("Missing docstring"); + } + + @Test + public void no_issues_unknown_files() throws IOException { + List externalIssues = executeSensorImporting(7, 9, "pylint_report_unknown_files.txt"); + assertThat(externalIssues).isEmpty(); + assertThat(onlyOneLogElement(logTester.logs(LoggerLevel.WARN))).hasToString("Failed to resolve 1 file path(s) in Pylint report. " + + "No issues imported related to file(s): pylint/unknown_file.py"); + } + + @Test + public void no_issues_with_invalid_report_path() throws IOException { + List externalIssues = executeSensorImporting(7, 9, "invalid-path.txt"); + assertThat(externalIssues).isEmpty(); + assertThat(onlyOneLogElement(logTester.logs(LoggerLevel.ERROR))) + .startsWith("No issues information will be saved as the report file '") + .contains("invalid-path.txt' can't be read."); + } + + private static List executeSensorImporting(int majorVersion, int minorVersion, @Nullable String fileName) throws IOException { + Path baseDir = PROJECT_DIR.getParent(); + SensorContextTester context = SensorContextTester.create(baseDir); + try (Stream fileStream = Files.list(PROJECT_DIR)) { + fileStream.forEach(file -> addFileToContext(context, baseDir, file)); + context.setRuntime(SonarRuntimeImpl.forSonarQube(Version.create(majorVersion, minorVersion), SonarQubeSide.SERVER, SonarEdition.DEVELOPER)); + if (fileName != null) { + String path = PROJECT_DIR.resolve(fileName).toAbsolutePath().toString(); + context.settings().setProperty("sonar.python.pylint.reportPaths", path); + } + pylintSensor.execute(context); + return new ArrayList<>(context.allExternalIssues()); + } + } + + private static void addFileToContext(SensorContextTester context, Path projectDir, Path file) { + try { + String projectId = projectDir.getFileName().toString() + "-project"; + context.fileSystem().add(TestInputFileBuilder.create(projectId, projectDir.toFile(), file.toFile()) + .setCharset(UTF_8) + .setLanguage(language(file)) + .setContents(new String(Files.readAllBytes(file), UTF_8)) + .setType(InputFile.Type.MAIN) + .build()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static String language(Path file) { + String path = file.toString(); + return path.substring(path.lastIndexOf('.') + 1); + } + + public static String onlyOneLogElement(List elements) { + assertThat(elements).hasSize(1); + return elements.get(0); + } + + public static void assertNoErrorWarnLogs(LogTester logTester) { + assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty(); + assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty(); + } + +} diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/file1.py b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/file1.py new file mode 100644 index 0000000000..b14d5f7e3e --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/file1.py @@ -0,0 +1,30 @@ +def myfunc(): + def unused() -> str: + smth = 42 + max = 24 + if smth == smth: + print("Hello?") + return smth + + print("Hello!") + + +def my_other_func(): + my_list = [] + if my_list is []: + print("Impossible!") + my_tuple = (1, 2) + try: + my_tuple + my_list + except TypeError: + print("Hello there!") + return 24 + + +def main(): + myfunc() + my_other_func() + + +if __name__ == "__main__": + main() diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report-unknown-rules.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report-unknown-rules.txt deleted file mode 100644 index 5a8f103eb9..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report-unknown-rules.txt +++ /dev/null @@ -1,7 +0,0 @@ -************* Module src.prod -src/file1.py:1: [C0111(missing-docstring), ] Missing module docstring -src/file1.py:1: [C0103(invalid-name), factorial] Invalid argument name "n" -src/file1.py:1: [C0111(missing-docstring), factorial] Missing function docstring -src/file1.py:1: [C8888(unknown-rule1), ] This rule is not defined 1 -src/file1.py:1: [C9999(unknown-rule2), ] This rule is not defined 2 -src/file1.py:1: [C9999(unknown-rule2), ] This rule is not defined 2 \ No newline at end of file diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report.txt deleted file mode 100644 index 09c32309a4..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint-report.txt +++ /dev/null @@ -1,4 +0,0 @@ -************* Module src.prod -src/file1.py:1: [C0111(missing-docstring), ] Missing module docstring -src/file1.py:1: [C0103(invalid-name), factorial] Invalid argument name "n" -src/file1.py:1: [C0111(missing-docstring), factorial] Missing function docstring diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_brackets.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_brackets.txt new file mode 100644 index 0000000000..fbc5ff9f90 --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_brackets.txt @@ -0,0 +1,10 @@ +pylint/file1.py:1: [E1300] message +pylint/file1.py:1: [E1301] message +pylint/file1.py:1: [E1302] message +pylint/file1.py:1: [E1303] message +pylint/file1.py:1: [E1304] message +pylint/file1.py:1: [E1305] message +pylint/file1.py:1: [E1306] message +pylint/file1.py:1: [W1201] message +pylint/file1.py:1: [W1300] message +pylint/file1.py:1: [W1301] message diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_names_in_brackets.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_names_in_brackets.txt new file mode 100644 index 0000000000..38fcd1767f --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_names_in_brackets.txt @@ -0,0 +1 @@ +pylint/file1.py:1: [C0111] Missing docstring diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_default_format.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_default_format.txt new file mode 100644 index 0000000000..bba1d554d2 --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_default_format.txt @@ -0,0 +1,14 @@ +************* Module src.main +pylint/file1.py:1:0: C0114: Missing module docstring (missing-module-docstring) +pylint/file1.py:1:0: C0116: Missing function or method docstring (missing-function-docstring) +pylint/file1.py:4:8: W0622: Redefining built-in 'max' (redefined-builtin) +pylint/file1.py:5:11: R0124: Redundant comparison - smth == smth (comparison-with-itself) +pylint/file1.py:4:8: W0612: Unused variable 'max' (unused-variable) +pylint/file1.py:2:4: W0612: Unused variable 'unused' (unused-variable) +pylint/file1.py:12:0: C0116: Missing function or method docstring (missing-function-docstring) +pylint/file1.py:14:7: R0123: Comparison to literal (literal-comparison) +pylint/file1.py:24:0: C0116: Missing function or method docstring (missing-function-docstring) + +pylint/file1.py:1:13: TEST1: No problem when issue is on last column +------------------------------------------------------------------ +Your code has been rated at 6.09/10 (previous run: 6.09/10, +0.00) diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_no_column.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_no_column.txt new file mode 100644 index 0000000000..8b4ab2d9e4 --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_no_column.txt @@ -0,0 +1,4 @@ +************* Module src.prod +pylint/file1.py:1: [C0111(missing-docstring), ] Missing module docstring +pylint/file1.py:1: [C0103(invalid-name), factorial] Invalid argument name "n" +pylint/file1.py:1: [C0111(missing-docstring), factorial] Missing function docstring diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_unknown_files.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_unknown_files.txt new file mode 100644 index 0000000000..5efa46a55e --- /dev/null +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/pylint_report_unknown_files.txt @@ -0,0 +1,13 @@ +************* Module src.main +pylint/unknown_file.py:1:0: C0114: Missing module docstring (missing-module-docstring) +pylint/unknown_file.py:1:0: C0116: Missing function or method docstring (missing-function-docstring) +pylint/unknown_file.py:4:8: W0622: Redefining built-in 'max' (redefined-builtin) +pylint/unknown_file.py:5:11: R0124: Redundant comparison - smth == smth (comparison-with-itself) +pylint/unknown_file.py:4:8: W0612: Unused variable 'max' (unused-variable) +pylint/unknown_file.py:2:4: W0612: Unused variable 'unused' (unused-variable) +pylint/unknown_file.py:12:0: C0116: Missing function or method docstring (missing-function-docstring) +pylint/unknown_file.py:14:7: R0123: Comparison to literal (literal-comparison) +pylint/unknown_file.py:24:0: C0116: Missing function or method docstring (missing-function-docstring) + +------------------------------------------------------------------ +Your code has been rated at 6.09/10 (previous run: 6.09/10, +0.00) diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output.txt deleted file mode 100644 index 3c14fc41ad..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output.txt +++ /dev/null @@ -1,21 +0,0 @@ -code_chunks.py:1: [C0111] Missing docstring -code_chunks.py:1: [C0111, if_else] Missing docstring -code_chunks.py:7: [C0111, if_elif_else] Missing docstring -code_chunks.py:15: [C0111, if_compl_cond1] Missing docstring -code_chunks.py:21: [C0111, if_compl_cond2] Missing docstring -code_chunks.py:27: [C0111, for_else] Missing docstring -code_chunks.py:28: [C0103, for_else] Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$) -code_chunks.py:28: [W0612, for_else] Unused variable 'x' -code_chunks.py:33: [C0111, while_comp_cond] Missing docstring -code_chunks.py:37: [C0111, while_else] Missing docstring -code_chunks.py:43: [C0111, while_else_compl_cond1] Missing docstring -code_chunks.py:49: [C0111, while_else_compl_cond2] Missing docstring -code_chunks.py:55: [C0111, while_else_compl_cond3] Missing docstring -code_chunks.py:61: [C0111, list_compr] Missing docstring -code_chunks.py:62: [W0104, list_compr] Statement seems to have no effect -code_chunks.py:64: [C0111, list_compr_filter] Missing docstring -code_chunks.py:65: [W0104, list_compr_filter] Statement seems to have no effect -code_chunks.py:67: [C0111, gen_expr] Missing docstring -code_chunks.py:68: [W0104, gen_expr] Statement seems to have no effect -code_chunks.py:70: [C0111, gen_expr_filter] Missing docstring -code_chunks.py:71: [W0104, gen_expr_filter] Statement seems to have no effect diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_new_format.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_new_format.txt deleted file mode 100644 index 3332a7553d..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_new_format.txt +++ /dev/null @@ -1,2 +0,0 @@ -code_chunks.py:1: [C0111(missing-docstring)] Missing docstring - diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_newids.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_newids.txt deleted file mode 100644 index 2ec21efe4d..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_newids.txt +++ /dev/null @@ -1,10 +0,0 @@ -code_chunks.py:1: [E1300] message -code_chunks.py:1: [E1301] message -code_chunks.py:1: [E1302] message -code_chunks.py:1: [E1303] message -code_chunks.py:1: [E1304] message -code_chunks.py:1: [E1305] message -code_chunks.py:1: [E1306] message -code_chunks.py:1: [W1201] message -code_chunks.py:1: [W1300] message -code_chunks.py:1: [W1301] message diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_oldids.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_oldids.txt deleted file mode 100644 index d30cf0ac70..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_oldids.txt +++ /dev/null @@ -1,10 +0,0 @@ -code_chunks.py:1: [E9900] message -code_chunks.py:1: [E9901] message -code_chunks.py:1: [E9902] message -code_chunks.py:1: [E9903] message -code_chunks.py:1: [E9904] message -code_chunks.py:1: [E9905] message -code_chunks.py:1: [E9906] message -code_chunks.py:1: [W6501] message -code_chunks.py:1: [W9900] message -code_chunks.py:1: [W9901] message diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_with_win_paths.txt b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_with_win_paths.txt deleted file mode 100644 index 268c5254ff..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/sample_pylint_output_with_win_paths.txt +++ /dev/null @@ -1 +0,0 @@ -C:\code_chunks.py:1: [C0111] Missing docstring diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file1.py b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file1.py deleted file mode 100644 index 11b15b1a45..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file1.py +++ /dev/null @@ -1 +0,0 @@ -print("hello") diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file2.py b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file2.py deleted file mode 100644 index 0239905611..0000000000 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/pylint/src/file2.py +++ /dev/null @@ -1 +0,0 @@ -print("hello2") From 8471488d1232be84abc17ff255d9930e7b300f3f Mon Sep 17 00:00:00 2001 From: Guillaume Dequenne Date: Thu, 9 Jul 2020 16:45:43 +0200 Subject: [PATCH 4/5] SONARPY-754 Support old reportPath property for Pylint import (#809) --- .../python/it/plugin/PylintReportTest.java | 22 +++++++--- .../plugins/python/ExternalIssuesSensor.java | 11 ++++- .../plugins/python/bandit/BanditSensor.java | 7 +++ .../plugins/python/flake8/Flake8Sensor.java | 6 +++ .../plugins/python/pylint/PylintSensor.java | 6 +++ .../python/bandit/BanditSensorTest.java | 11 +++++ .../python/flake8/Flake8SensorTest.java | 11 +++++ .../python/pylint/PylintSensorTest.java | 44 +++++++++++++++---- 8 files changed, 102 insertions(+), 16 deletions(-) diff --git a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java index 153294b9d1..f63cf55f2a 100644 --- a/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java +++ b/its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java @@ -36,38 +36,46 @@ public class PylintReportTest { private static final String PROJECT = "pylint_project"; + private static final String DEFAULT_PROPERTY = "sonar.python.pylint.reportPaths"; + private static final String LEGACY_PROPERTY = "sonar.python.pylint.reportPath"; @ClassRule public static final Orchestrator ORCHESTRATOR = Tests.ORCHESTRATOR; @Test public void import_report() { - analyseProjectWithReport("pylint-report.txt"); + analyseProjectWithReport(DEFAULT_PROPERTY, "pylint-report.txt"); + assertThat(issues()).hasSize(4); + } + + @Test + public void import_report_legacy_key() { + analyseProjectWithReport(LEGACY_PROPERTY, "pylint-report.txt"); assertThat(issues()).hasSize(4); } @Test public void missing_report() { - analyseProjectWithReport("missing"); + analyseProjectWithReport(DEFAULT_PROPERTY, "missing"); assertThat(issues()).isEmpty(); } @Test public void invalid_report() { - BuildResult result = analyseProjectWithReport("invalid.txt"); + BuildResult result = analyseProjectWithReport(DEFAULT_PROPERTY, "invalid.txt"); assertThat(result.getLogs()).contains("Cannot parse the line: trash"); assertThat(issues()).isEmpty(); } @Test public void unknown_rule() { - BuildResult result = analyseProjectWithReport("rule-unknown.txt"); + BuildResult result = analyseProjectWithReport(DEFAULT_PROPERTY, "rule-unknown.txt"); assertThat(issues()).hasSize(4); } @Test public void multiple_reports() { - analyseProjectWithReport("pylint-report.txt, rule-unknown.txt"); + analyseProjectWithReport(DEFAULT_PROPERTY, "pylint-report.txt, rule-unknown.txt"); assertThat(issues()).hasSize(8); } @@ -75,7 +83,7 @@ private static List issues() { return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(PROJECT))).getIssuesList(); } - private static BuildResult analyseProjectWithReport(String reportPaths) { + private static BuildResult analyseProjectWithReport(String property, String reportPaths) { ORCHESTRATOR.resetData(); ORCHESTRATOR.getServer().provisionProject(PROJECT, PROJECT); ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT, "py", "no_rule"); @@ -84,7 +92,7 @@ private static BuildResult analyseProjectWithReport(String reportPaths) { SonarScanner.create() .setDebugLogs(true) .setProjectDir(new File("projects/pylint_project")) - .setProperty("sonar.python.pylint.reportPaths", reportPaths)); + .setProperty(property, reportPaths)); } } diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java index 42f384262c..39567a5ba1 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/ExternalIssuesSensor.java @@ -33,8 +33,10 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.issue.NewExternalIssue; import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.config.Configuration; import org.sonar.api.rules.RuleType; import org.sonar.api.utils.log.Logger; +import org.sonar.plugins.python.pylint.PylintSensor; import org.sonarsource.analyzer.commons.ExternalReportProvider; import org.sonarsource.analyzer.commons.internal.json.simple.parser.ParseException; @@ -42,11 +44,12 @@ public abstract class ExternalIssuesSensor implements Sensor { private static final int MAX_LOGGED_FILE_NAMES = 20; private static final Long DEFAULT_CONSTANT_DEBT_MINUTES = 5L; + protected static final String PYLINT_LEGACY_KEY = "sonar.python.pylint.reportPath"; @Override public void describe(SensorDescriptor descriptor) { descriptor - .onlyWhenConfiguration(conf -> conf.hasKey(reportPathKey())) + .onlyWhenConfiguration(this::shouldExecute) .onlyOnLanguage(Python.KEY) .name("Import of " + linterName() + " issues"); } @@ -55,6 +58,10 @@ public void describe(SensorDescriptor descriptor) { public void execute(SensorContext context) { Set unresolvedInputFiles = new HashSet<>(); List reportFiles = ExternalReportProvider.getReportFiles(context, reportPathKey()); + if (reportFiles.isEmpty() && context.config().hasKey(PYLINT_LEGACY_KEY)) { + reportFiles = ExternalReportProvider.getReportFiles(context, PYLINT_LEGACY_KEY); + logger().warn("The use of '{}' is deprecated. Please use the '{}' property instead.", PYLINT_LEGACY_KEY, PylintSensor.REPORT_PATH_KEY); + } reportFiles.forEach(report -> importExternalReport(report, context, unresolvedInputFiles)); logUnresolvedInputFiles(unresolvedInputFiles); } @@ -113,6 +120,8 @@ protected void saveIssue(SensorContext context, TextReportReader.Issue issue, Se protected abstract void importReport(File reportPath, SensorContext context, Set unresolvedInputFiles) throws IOException, ParseException; + protected abstract boolean shouldExecute(Configuration conf); + protected abstract String linterName(); protected abstract String reportPathKey(); diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java index 6c3cf60806..82ea7a182e 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/bandit/BanditSensor.java @@ -29,6 +29,7 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.issue.NewExternalIssue; import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.config.Configuration; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.RuleType; import org.sonar.api.utils.Version; @@ -58,6 +59,7 @@ protected void importReport(File reportPath, SensorContext context, Set BanditJsonReportReader.read(in, issue -> saveIssue(context, issue, unresolvedInputFiles, engineIdIsSupported)); } + private static void saveIssue(SensorContext context, Issue issue, Set unresolvedInputFiles, boolean engineIdIsSupported) { if (isEmpty(issue.ruleKey) || isEmpty(issue.filePath) || isEmpty(issue.message)) { LOG.debug("Missing information for ruleKey:'{}', filePath:'{}', message:'{}'", issue.ruleKey, issue.filePath, issue.message); @@ -106,6 +108,11 @@ private static Severity toSonarQubeSeverity(String severity, String confidence) } } + @Override + protected boolean shouldExecute(Configuration conf) { + return conf.hasKey(REPORT_PATH_KEY); + } + @Override protected String linterName() { return LINTER_NAME; diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java index 365b6af6b6..28571ba469 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/flake8/Flake8Sensor.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Set; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.plugins.python.ExternalIssuesSensor; @@ -44,6 +45,11 @@ protected void importReport(File reportPath, SensorContext context, Set issues.forEach(i -> saveIssue(context, i, unresolvedInputFiles, LINTER_KEY)); } + @Override + protected boolean shouldExecute(Configuration conf) { + return conf.hasKey(REPORT_PATH_KEY); + } + @Override protected String linterName() { return LINTER_NAME; diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java index 1e808985eb..0a229b8c35 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintSensor.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Set; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.plugins.python.ExternalIssuesSensor; @@ -44,6 +45,11 @@ protected void importReport(File reportPath, SensorContext context, Set issues.forEach(i -> saveIssue(context, i, unresolvedInputFiles, LINTER_KEY)); } + @Override + protected boolean shouldExecute(Configuration conf) { + return conf.hasKey(REPORT_PATH_KEY) || conf.hasKey(PYLINT_LEGACY_KEY); + } + @Override protected String reportPathKey() { return REPORT_PATH_KEY; diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/bandit/BanditSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/bandit/BanditSensorTest.java index 324a0350c2..6e49478599 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/bandit/BanditSensorTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/bandit/BanditSensorTest.java @@ -52,6 +52,7 @@ public class BanditSensorTest { private static final String BANDIT_FILE = "python-project:bandit/file1.py"; private static final String BANDIT_B413 = "external_bandit:B413"; + private static final String BANDIT_PROPERTY = "sonar.python.bandit.reportPaths"; private static final String BANDIT_REPORT_JSON = "bandit-report.json"; private static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "org", "sonar", "plugins", "python", "bandit"); @@ -69,6 +70,16 @@ public void test_descriptor() { assertThat(sensorDescriptor.languages()).containsOnly("py"); assertThat(sensorDescriptor.configurationPredicate()).isNotNull(); assertNoErrorWarnDebugLogs(logTester); + + Path baseDir = PROJECT_DIR.getParent(); + SensorContextTester context = SensorContextTester.create(baseDir); + context.settings().setProperty(BANDIT_PROPERTY, "path/to/report"); + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isTrue(); + + context = SensorContextTester.create(baseDir); + context.settings().setProperty("sonar.python.bandit.reportPath", "path/to/report"); + // No support of "reportPath" property for Bandit + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isFalse(); } diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/flake8/Flake8SensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/flake8/Flake8SensorTest.java index 801c54abf0..37a8fec5c7 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/flake8/Flake8SensorTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/flake8/Flake8SensorTest.java @@ -53,6 +53,7 @@ public class Flake8SensorTest { private static final String FLAKE8_FILE = "python-project:flake8/file1.py"; private static final String FLAKE8_F401 = "external_flake8:F401"; private static final String FLAKE_8_REPORT = "flake8-report.txt"; + private static final String FLAKE_8_PROPERTY = "sonar.python.flake8.reportPaths"; private static final String FLAKE_8_REPORT_UNKNOWN_FILES = "flake8-report-unknown-files.txt"; private static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "org", "sonar", "plugins", "python", "flake8"); @@ -70,6 +71,16 @@ public void test_descriptor() { assertThat(sensorDescriptor.languages()).containsOnly("py"); assertThat(sensorDescriptor.configurationPredicate()).isNotNull(); assertNoErrorWarnDebugLogs(logTester); + + Path baseDir = PROJECT_DIR.getParent(); + SensorContextTester context = SensorContextTester.create(baseDir); + context.settings().setProperty(FLAKE_8_PROPERTY, "path/to/report"); + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isTrue(); + + context = SensorContextTester.create(baseDir); + context.settings().setProperty("sonar.python.flake8.reportPath", "path/to/report"); + // No support of "reportPath" property for Flake8 + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isFalse(); } @Test diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java index 560cdb0265..eadccb485d 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintSensorTest.java @@ -53,6 +53,8 @@ public class PylintSensorTest { private static final String PYLINT_FILE = "python-project:pylint/file1.py"; private static final String PYLINT_REPORT_DEFAULT_FORMAT = "pylint_report_default_format.txt"; private static final String PYLINT_REPORT_NO_COLUMN = "pylint_report_no_column.txt"; + private static final String DEFAULT_PROPERTY = "sonar.python.pylint.reportPaths"; + private static final String LEGACY_PROPERTY = "sonar.python.pylint.reportPath"; private static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "org", "sonar", "plugins", "python", "pylint"); @@ -69,11 +71,25 @@ public void test_descriptor() { assertThat(sensorDescriptor.languages()).containsOnly("py"); assertThat(sensorDescriptor.configurationPredicate()).isNotNull(); assertNoErrorWarnLogs(logTester); + + Path baseDir = PROJECT_DIR.getParent(); + SensorContextTester context = SensorContextTester.create(baseDir); + context.settings().setProperty(DEFAULT_PROPERTY, "path/to/report"); + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isTrue(); + + context = SensorContextTester.create(baseDir); + context.settings().setProperty(LEGACY_PROPERTY, "path/to/report"); + // Support of legacy "reportPath" property for Pylint + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isTrue(); + + context = SensorContextTester.create(baseDir); + context.settings().setProperty("random.key", "path/to/report"); + assertThat(sensorDescriptor.configurationPredicate().test(context.config())).isFalse(); } @Test public void issues_default_format() throws IOException { - List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_DEFAULT_FORMAT); + List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_DEFAULT_FORMAT, false); assertThat(externalIssues).hasSize(10); ExternalIssue first = externalIssues.get(0); @@ -106,7 +122,7 @@ public void issues_default_format() throws IOException { @Test public void issues_no_column_info() throws IOException { - List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_NO_COLUMN); + List externalIssues = executeSensorImporting(7, 9, PYLINT_REPORT_NO_COLUMN, false); assertThat(externalIssues).hasSize(3); ExternalIssue first = externalIssues.get(0); @@ -153,7 +169,7 @@ public void issues_no_column_info() throws IOException { @Test public void issues_other_formats() throws IOException { - List externalIssues = executeSensorImporting(7, 9, "pylint_brackets.txt"); + List externalIssues = executeSensorImporting(7, 9, "pylint_brackets.txt", false); assertThat(externalIssues).hasSize(10); ExternalIssue first = externalIssues.get(0); assertThat(first.ruleKey()).hasToString("external_pylint:E1300"); @@ -164,7 +180,7 @@ public void issues_other_formats() throws IOException { assertThat(firstPrimaryLoc.message()) .isEqualTo("message"); - externalIssues = executeSensorImporting(7, 9, "pylint_names_in_brackets.txt"); + externalIssues = executeSensorImporting(7, 9, "pylint_names_in_brackets.txt", false); assertThat(externalIssues).hasSize(1); first = externalIssues.get(0); assertThat(first.ruleKey()).hasToString("external_pylint:C0111"); @@ -178,7 +194,7 @@ public void issues_other_formats() throws IOException { @Test public void no_issues_unknown_files() throws IOException { - List externalIssues = executeSensorImporting(7, 9, "pylint_report_unknown_files.txt"); + List externalIssues = executeSensorImporting(7, 9, "pylint_report_unknown_files.txt", false); assertThat(externalIssues).isEmpty(); assertThat(onlyOneLogElement(logTester.logs(LoggerLevel.WARN))).hasToString("Failed to resolve 1 file path(s) in Pylint report. " + "No issues imported related to file(s): pylint/unknown_file.py"); @@ -186,14 +202,22 @@ public void no_issues_unknown_files() throws IOException { @Test public void no_issues_with_invalid_report_path() throws IOException { - List externalIssues = executeSensorImporting(7, 9, "invalid-path.txt"); + List externalIssues = executeSensorImporting(7, 9, "invalid-path.txt", false); assertThat(externalIssues).isEmpty(); assertThat(onlyOneLogElement(logTester.logs(LoggerLevel.ERROR))) .startsWith("No issues information will be saved as the report file '") .contains("invalid-path.txt' can't be read."); } - private static List executeSensorImporting(int majorVersion, int minorVersion, @Nullable String fileName) throws IOException { + @Test + public void import_with_legacy_property() throws IOException { + List externalIssues = executeSensorImporting(7, 9, "pylint_brackets.txt", true); + assertThat(externalIssues).hasSize(10); + assertThat(onlyOneLogElement(logTester.logs())).hasToString("The use of 'sonar.python.pylint.reportPath' is deprecated." + + " Please use the 'sonar.python.pylint.reportPaths' property instead."); + } + + private static List executeSensorImporting(int majorVersion, int minorVersion, @Nullable String fileName, boolean useLegacyKey) throws IOException { Path baseDir = PROJECT_DIR.getParent(); SensorContextTester context = SensorContextTester.create(baseDir); try (Stream fileStream = Files.list(PROJECT_DIR)) { @@ -201,7 +225,11 @@ private static List executeSensorImporting(int majorVersion, int context.setRuntime(SonarRuntimeImpl.forSonarQube(Version.create(majorVersion, minorVersion), SonarQubeSide.SERVER, SonarEdition.DEVELOPER)); if (fileName != null) { String path = PROJECT_DIR.resolve(fileName).toAbsolutePath().toString(); - context.settings().setProperty("sonar.python.pylint.reportPaths", path); + if (useLegacyKey) { + context.settings().setProperty(LEGACY_PROPERTY, path); + } else { + context.settings().setProperty(DEFAULT_PROPERTY, path); + } } pylintSensor.execute(context); return new ArrayList<>(context.allExternalIssues()); From 4817dcd0c0f2ae5884b85eaa049ad220b388e471 Mon Sep 17 00:00:00 2001 From: Guillaume Dequenne Date: Tue, 14 Jul 2020 15:11:15 +0200 Subject: [PATCH 5/5] Remove old Pylint metadata --- .../sonar/plugins/python/profile-default.xml | 729 ---- .../sonar/plugins/python/pylint/convert.py | 112 - .../python/pylint/generate_rules_file.sh | 2 - .../python/pylint/remediation-cost.csv | 370 -- .../org/sonar/plugins/python/pylint/rules.xml | 3507 ----------------- .../plugins/python/pylint/rules_generated.xml | 2563 ------------ 6 files changed, 7283 deletions(-) delete mode 100644 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/profile-default.xml delete mode 100755 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/convert.py delete mode 100755 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/generate_rules_file.sh delete mode 100644 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/remediation-cost.csv delete mode 100644 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.xml delete mode 100644 sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules_generated.xml diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/profile-default.xml b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/profile-default.xml deleted file mode 100644 index 5ba37f1485..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/profile-default.xml +++ /dev/null @@ -1,729 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Sonar way - py - - - Pylint - E0001 - MAJOR - - - Pylint - E0011 - MAJOR - - - Pylint - E0012 - MAJOR - - - Pylint - E0100 - MAJOR - - - Pylint - E0101 - MAJOR - - - Pylint - E0102 - MAJOR - - - Pylint - E0103 - MAJOR - - - Pylint - E0104 - MAJOR - - - Pylint - E0105 - MAJOR - - - Pylint - E0106 - MAJOR - - - Pylint - E0107 - MAJOR - - - Pylint - E0202 - MAJOR - - - Pylint - E0203 - MAJOR - - - Pylint - E0211 - MAJOR - - - Pylint - E0213 - MAJOR - - - Pylint - E0221 - MAJOR - - - Pylint - E0222 - MAJOR - - - Pylint - E0501 - MAJOR - - - Pylint - E0502 - MAJOR - - - Pylint - E0503 - MAJOR - - - Pylint - E0601 - MAJOR - - - Pylint - E0602 - MAJOR - - - Pylint - E0611 - MAJOR - - - Pylint - E0701 - MAJOR - - - Pylint - E0702 - MAJOR - - - Pylint - E0710 - MAJOR - - - Pylint - E0711 - MAJOR - - - Pylint - E1001 - MAJOR - - - Pylint - E1002 - MAJOR - - - Pylint - E1003 - MAJOR - - - Pylint - E1101 - MAJOR - - - Pylint - E1102 - MAJOR - - - Pylint - E1103 - MAJOR - - - Pylint - E1111 - MAJOR - - - Pylint - E1120 - MAJOR - - - Pylint - E1121 - MAJOR - - - Pylint - E1122 - MAJOR - - - Pylint - E1123 - MAJOR - - - Pylint - E1124 - MAJOR - - - Pylint - E1300 - MAJOR - - - Pylint - E1301 - MAJOR - - - Pylint - E1302 - MAJOR - - - Pylint - E1303 - MAJOR - - - Pylint - E1304 - MAJOR - - - Pylint - E1305 - MAJOR - - Pylint - E1306 - MAJOR - - Pylint - F0001 - MAJOR - - - Pylint - F0002 - MAJOR - - - Pylint - F0003 - MAJOR - - - Pylint - F0004 - MAJOR - - - Pylint - F0010 - MAJOR - - - Pylint - F0202 - MAJOR - - - Pylint - F0220 - MAJOR - - - Pylint - F0321 - MAJOR - - - Pylint - F0401 - MAJOR - - - Pylint - I0001 - INFO - - - Pylint - I0010 - INFO - - - Pylint - I0011 - INFO - - - Pylint - I0012 - INFO - - - Pylint - I0013 - INFO - - - - Pylint - R0201 - MINOR - - - Pylint - R0401 - MINOR - - - Pylint - R0801 - MINOR - - - Pylint - R0901 - MINOR - - - Pylint - R0902 - MINOR - - - Pylint - R0903 - MINOR - - - Pylint - R0904 - MINOR - - - Pylint - R0911 - MINOR - - - Pylint - R0912 - MINOR - - - Pylint - R0913 - MINOR - - - Pylint - R0914 - MINOR - - - Pylint - R0915 - MINOR - - - Pylint - R0921 - MINOR - - - Pylint - R0922 - MINOR - - - Pylint - R0923 - MINOR - - - Pylint - W0101 - MINOR - - - Pylint - W0102 - MINOR - - - Pylint - W0104 - MINOR - - - Pylint - W0105 - MINOR - - - Pylint - W0106 - MINOR - - - Pylint - W0107 - MINOR - - - Pylint - W0108 - MINOR - - - Pylint - W0109 - MINOR - - - Pylint - W0122 - MINOR - - - Pylint - W0141 - MINOR - - - Pylint - W0142 - MINOR - - - Pylint - W0150 - MINOR - - - Pylint - W0199 - MINOR - - - Pylint - W0201 - MINOR - - - Pylint - W0211 - MINOR - - - Pylint - W0212 - MINOR - - - Pylint - W0221 - MINOR - - - Pylint - W0222 - MINOR - - - Pylint - W0223 - MINOR - - - Pylint - W0231 - MINOR - - - Pylint - W0232 - MINOR - - - Pylint - W0233 - MINOR - - - Pylint - W0301 - MINOR - - - Pylint - W0311 - MINOR - - - Pylint - W0312 - MINOR - - - Pylint - W0331 - MINOR - - - Pylint - W0332 - MINOR - - - Pylint - W0333 - MINOR - - - Pylint - W0401 - MINOR - - - Pylint - W0402 - MINOR - - - Pylint - W0403 - MINOR - - - Pylint - W0404 - MINOR - - - Pylint - W0406 - MINOR - - - Pylint - W0410 - MINOR - - - Pylint - W0511 - MINOR - - - Pylint - W0601 - MINOR - - - Pylint - W0602 - MINOR - - - Pylint - W0603 - MINOR - - - Pylint - W0604 - MINOR - - - Pylint - W0611 - MINOR - - - Pylint - W0612 - MINOR - - - Pylint - W0613 - MINOR - - - Pylint - W0614 - MINOR - - - Pylint - W0621 - MINOR - - - Pylint - W0622 - MINOR - - - Pylint - W0631 - MINOR - - - Pylint - W0701 - MINOR - - - Pylint - W0702 - MINOR - - - Pylint - W0703 - MINOR - - - Pylint - W0704 - MINOR - - - Pylint - W0710 - MINOR - - - Pylint - W1001 - MINOR - - - Pylint - W1111 - MINOR - - - Pylint - W1201 - MINOR - - - Pylint - W1300 - MINOR - - - Pylint - W1301 - MINOR - - - - - - Pylint - E1200 - MAJOR - - - Pylint - E1201 - MAJOR - - - Pylint - E1205 - MAJOR - - - Pylint - E1206 - MAJOR - - diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/convert.py b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/convert.py deleted file mode 100755 index a62a9480b5..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/convert.py +++ /dev/null @@ -1,112 +0,0 @@ -# Sonar Python Plugin -# Copyright (C) 2011 Waleri Enns -# Author(s) : Waleri Enns -# waleri.enns@gmail.com - -# 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 - - -# -# Reads pylint --list-msgs from the stdin and writes XML to the stdout. -# The latter is compatible with sonar rules.xml-schema. -# - -import sys -import re - -RULEID_PATTERN = ":[a-z _-]+ \(([A-Z][0-9]{4})\): ?\*?(.*?)\*?$" - -def parseNextRule(lines): - ruleid, rulename = grabIdAndName(lines) - ruledescr = grabDescr(lines) - if ruleid: - if not ruledescr: - raise Exception("Invalid input") - return Rule(ruleid, rulename, ruledescr) - return None - -def grabIdAndName(lines): - if not lines: - return "" - currline = lines.pop() - ruleid = rulename = "" - match = re.match(RULEID_PATTERN, currline) - if match: - ruleid, rulename = match.groups() - return ruleid, rulename - -def grabDescr(lines): - def partOfDescr(line): - return line[:2] == " " - - descr = "" - while lines: - currline = lines.pop() - if partOfDescr(currline): - descr += " " + currline.strip() - else: - lines.append(currline) - break - return descr.strip() - - -def header(): - return ('\n' - '\n') - - -def footer(): - return "\n" - - -class Rule: - def __init__(self, ruleid, rulename, ruledescr): - self.ruleid = ruleid - self.rulename = rulename - self.ruledescr = ruledescr - - def __lt__(self, other): - return self.ruleid < other.ruleid - - def toxml(self): - rid = self.ruleid - return ("\n" - "%s\n" - "\n" - "%s\n" - "\n" - "\n" - "\n" - "\n" - % (self.ruleid, self.rulename, self.ruleid, self.ruledescr)) - - -lines = sys.stdin.readlines() -lines.reverse() - -# parse line oriented input format -rules = [] -while(lines): - rule = parseNextRule(lines) - if rule: - rules.append(rule) -rules.sort() - -# generate rules-file as expected by sonar -outstream = sys.stdout -outstream.write(header()) -for rule in rules: - outstream.write(rule.toxml()) -outstream.write(footer()) diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/generate_rules_file.sh b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/generate_rules_file.sh deleted file mode 100755 index 890051dec6..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/generate_rules_file.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -pylint --list-msgs | python convert.py > rules_generated.xml diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/remediation-cost.csv b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/remediation-cost.csv deleted file mode 100644 index 7886c519da..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/remediation-cost.csv +++ /dev/null @@ -1,370 +0,0 @@ -C0102,1min -C0103,1min -C0111,5min -C0112,5min -C0113,1min -C0121,20min -C0122,1min -C0123,15min -C0200,20min -C0201,20min -C0202,10min -C0203,10min -C0204,20min -C0205,15min -C0301,1min -C0302,30min -C0303,1h -C0304,10min -C0305,1min -C0321,5min -C0322,1min -C0323,1min -C0324,1min -C0325,5min -C0326,1min -C0327,15min -C0328,1min -C0330,5min -C0401,1min -C0402,1min -C0403,1min -C0410,5min -C0411,5min -C0412,5min -C0413,5min -C0414,5min -C1001,20min -C1801,20min -E0001,5min -E0011,5min -E0012,5min -E0100,30min -E0101,5min -E0102,15min -E0103,15min -E0104,5min -E0105,10min -E0106,15min -E0107,3min -E0108,5min -E0109,10min -E0110,5min -E0111,10min -E0112,5min -E0113,5min -E0114,5min -E0116,15min -E0115,15min -E0117,5min -E0119,5min -E0202,15min -E0203,15min -E0211,10min -E0213,5min -E0221,20min -E0222,30min -E0235,10min -E0236,10min -E0237,10min -E0238,20min -E0239,20min -E0240,20min -E0241,20min -E0301,10min -E0302,20min -E0303,20min -E0401,10min -E0402,20min -E0501,5min -E0502,5min -E0503,10min -E0601,15min -E0602,20min -E0603,20min -E0604,3min -E0611,10min -E0632,20min -E0633,20min -E0701,30min -E0702,20min -E0703,5min -E0704,10min -E0710,15min -E0711,5min -E0712,20min -E1001,10min -E1002,10min -E1003,10min -E1004,10min -E1101,10min -E1102,3min -E1103,10min -E1111,20min -E1120,5min -E1121,5min -E1122,3min -E1123,3min -E1124,5min -E1125,5min -E1126,15min -E1127,15min -E1128,15min -E1129,15min -E1130,15min -E1131,15min -E1132,15min -E1133,15min -E1134,15min -E1135,15min -E1136,15min -E1137,15min -E1138,15min -E1139,15min -E1140,5min -E1200,10min -E1201,10min -E1205,10min -E1206,10min -E1300,3min -E1301,3min -E1302,5min -E1303,10min -E1304,10min -E1305,5min -E1306,5min -E1307,5min -E1310,10min -E1507,5min -E1601,5min -E1602,10min -E1603,10min -E1604,5min -E1605,5min -E1606,10min -E1607,5min -E1608,3min -E1609,10min -E1610,10min -E1700,15min -E1701,5min -F0001,15min -F0002,15min -F0003,15min -F0004,15min -F0010,15min -F0202,15min -F0220,15min -F0321,15min -F0401,15min -I0001,15min -I0010,15min -I0011,15min -I0012,15min -I0013,15min -I0020,15min -I0021,15min -I0022,15min -I0023,5min -I1101,15min -R0123,20min -R0124,1min -R0201,20min -R0202,15min -R0203,15min -R0205,1min -R0401,2h -R0801,15min -R0901,3h -R0902,2h -R0903,1h -R0904,2h -R0911,20min -R0912,15min -R0913,30min -R0914,20min -R0915,15min -R0916,20min -R0921,20min -R0922,45min -R0923,20min -R1701,20min -R1702,30min -R1703,10min -R1704,1h -R1705,20min -R1706,20min -R1707,1min -R1708,5min -R1709,20min -R1710,20min -R1711,1min -R1712,1min -R1713,1min -R1714,1min -R1715,1min -R1716,1min -R1717,15min -R1718,15min -R1719,1min -R1720,1min -W0101,15min -W0102,15min -W0104,15min -W0105,15min -W0106,15min -W0107,1min -W0108,5min -W0109,10min -W0110,15min -W0111,20min -W0120,20min -W0121,5min -W0122,30min -W0123,30min -W0124,30min -W0125,30min -W0141,15min -W0142,15min -W0143,1min -W0150,20min -W0199,5min -W0201,5min -W0211,10min -W0212,1h -W0221,15min -W0222,20min -W0223,1h -W0231,20min -W0232,20min -W0233,2h -W0234,15min -W0235,1min -W0301,1min -W0311,1min -W0312,1min -W0331,1min -W0332,1min -W0333,1min -W0401,1min -W0402,1d -W0403,15min -W0404,1min -W0406,15min -W0410,10min -W0511,1h -W0512,15min -W0601,15min -W0602,15min -W0603,1h -W0604,1min -W0611,1min -W0612,5min -W0613,15min -W0614,1min -W0621,15min -W0622,5min -W0623,20min -W0631,15min -W0632,15min -W0633,15min -W0640,20min -W0641,1min -W0642,5min -W0701,15min -W0702,15min -W0703,20min -W0704,15min -W0705,10min -W0706,1min -W0710,15min -W0711,15min -W0712,15min -W0715,5min -W0716,5min -W1001,20min -W1111,15min -W1113,5min -W1201,5min -W1202,5min -W1203,1min -W1300,5min -W1301,5min -W1302,5min -W1303,5min -W1304,5min -W1305,5min -W1306,5min -W1307,5min -W1308,1min -W1401,5min -W1402,15min -W1403,1min -W1501,15min -W1502,15min -W1503,15min -W1505,15min -W1506,15min -W1507,15min -W1508,5min -W1509,15min -W1601,15min -W1602,5min -W1603,5min -W1604,5min -W1605,5min -W1606,5min -W1607,5min -W1608,5min -W1609,5min -W1610,5min -W1611,5min -W1612,5min -W1613,5min -W1614,5min -W1615,5min -W1616,5min -W1617,5min -W1618,5min -W1619,5min -W1620,5min -W1621,5min -W1622,5min -W1623,5min -W1624,5min -W1625,5min -W1626,5min -W1627,5min -W1628,5min -W1629,5min -W1630,5min -W1632,5min -W1633,5min -W1634,5min -W1635,5min -W1636,5min -W1637,5min -W1638,5min -W1639,5min -W1640,5min -W1641,5min -W1642,5min -W1643,5min -W1644,5min -W1645,5min -W1646,5min -W1647,5min -W1649,5min -W1650,5min -W1651,5min -W1652,5min -W1653,5min -W1654,5min -W1655,5min -W1656,5min -W1657,5min -W1658,5min -W1659,5min -W1660,5min -W1661,5min -W1662,5min diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.xml b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.xml deleted file mode 100644 index 570de57723..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules.xml +++ /dev/null @@ -1,3507 +0,0 @@ - - - - C0102 - - C0102 - - Pylint can be customized to help enforce coding - guidelines that discourage or forbid use of certain names for - variables, functions, etc. These names are specified with the - bad-names option. This message is raised whenever a name is in the - list of names defined with the bad-names option.

- ]]> -
- MINOR -
- - C0103 - - C0103 - - This rule is deprecated, use {rule:python:S116}, {rule:python:S117}, {rule:python:S101}, - {rule:python:S100}, {rule:python:S1542}, {rule:python:S1578} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0111 - - C0111 - - This rule is deprecated, use {rule:python:S1720} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0112 - - C0112 - - This rule is deprecated, use {rule:python:S1720} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0113 - - C0113 - - - - MINOR - - - - C0121 - - C0121 - - - - MINOR - - - C0122 - - C0122 - - - - MINOR - - - C0123 - - C0123 - - - - MINOR - - - C0200 - - C0200 - - - - MINOR - - - C0201 - - C0201 - - - - MINOR - - - C0202 - - C0202 - - - - MINOR - - - C0203 - - C0203 - - - - MINOR - - - C0204 - - C0204 - - - - MINOR - - - C0205 - - C0205 - - - - MINOR - - - C0301 - - C0301 - - This rule is deprecated, use {rule:python:LineLength} instead.

]]> -
- MINOR - DEPRECATED -
- - C0302 - - C0302 - - This rule is deprecated, use {rule:python:S104} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0303 - - C0303 - - Added in Pylint 1.0.0.

-

This rule is deprecated, use {rule:python:S1131} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0304 - - C0304 - - While Python interpreters typically do not require line - end character(s) on the last line, other programs processing Python - source files may do, and it is simply good practice to have it.

-

Added in Pylint 1.0.0.

-

This rule is deprecated, use {rule:python:S113} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0305 - - C0305 - - - - MINOR - - - C0321 - - C0321 - - Used when more than one statement are found on the same line.

-

This rule is deprecated, use {rule:python:OneStatementPerLine} instead.

]]> -
- MINOR - DEPRECATED -
- - C0322 - - C0322 - - = | < | > | = | \+= |-= | \*= | /= | %) is not preceded by a space.]]> - - MINOR - - - C0323 - - C0323 - - = | < | > | = | \+= |-= | \*= | /= | %) is not followed by a space.]]> - - MINOR - - - C0324 - - C0324 - - - - MINOR - - - C0325 - - C0325 - - This rule was added in Pylint 1.1.0.

-

This rule is deprecated, use {rule:python:S1110} instead.

- ]]> -
- MINOR - DEPRECATED -
- - C0326 - - C0326 - - - - MINOR - - - C0327 - - C0327 - - - - MINOR - - - C0328 - - C0328 - - - - MINOR - - - C0330 - - C0330 - - Used when continued lines are badly indented.

-

This rule was added in Pylint 1.2.1.

]]> -
- MINOR -
- - C0401 - - C0401 - - - - MINOR - - - C0402 - - C0402 - - - - MINOR - - - C0403 - - C0403 - - - - MINOR - - - C0410 - - C0410 - - - - MINOR - - - C0411 - - C0411 - - - - MINOR - - - C0412 - - C0412 - - - - MINOR - - - C0413 - - C0413 - - - - MINOR - - - C1001 - - C1001 - - = 3.0. -

This rule was added in Pylint 1.0.0.

-

This rule is deprecated, use {rule:python:S1722} instead.

- ]]> -
- MINOR -
- - C0414 - - C0414 - - - - MINOR - - - C1801 - - C1801 - - - - MINOR - - - E0001 - - E0001 - - - - MAJOR - - - E0011 - - E0011 - - Note that options can be specified in the - configuration file and can be overridden on the command line.

- ]]> -
- MAJOR -
- - E0012 - - E0012 - - The option exists but its value is not valid. The options can be - specified in the Pylint configuration file and can be overridden in - the Pylint command line.

- ]]> -
- MAJOR -
- - E0100 - - E0100 - - The __init__() method is required to return nothing. Python 2.7 and 3.x - raise a TypeError when __init__() is called and executes a yield - statement. Pylint reports this error without depending on the actual - invocation.

-

This rule is deprecated, use {rule:python:S2734} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0101 - - E0101 - - The __init__() method is required to return - nothing. Python raises a TypeError when __init__() is - called and executes a return statement with a value other than - None. Pylint reports this error without depending on the actual - invocation.

-

This rule is deprecated, use {rule:python:S2734} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0102 - - E0102 - - - - MAJOR - - - E0103 - - E0103 - - This rule is deprecated, use {rule:python:S1716} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0104 - - E0104 - - This rule is deprecated, use {rule:python:S2711} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0105 - - E0105 - - This rule is deprecated, use {rule:python:S2711} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0106 - - E0106 - - = 3.3. -

This rule is deprecated, use {rule:python:S2712} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0107 - - E0107 - - This rule is deprecated, use {rule:python:PreIncrementDecrement} instead.

- ]]> -
- MAJOR - DEPRECATED -
- - E0108 - - E0108 - - This rule was added in Pylint 0.28.0.

]]> -
- MINOR -
- - E0109 - - E0109 - - - - MINOR - - - E0110 - - E0110 - - - - MINOR - - - E0111 - - E0111 - - This rule was added in Pylint 1.2.0.

]]> -
- MINOR -
- - E0112 - - E0112 - - - - MAJOR - - - E0113 - - E0113 - - - - MAJOR - - - E0114 - - E0114 - - - - MAJOR - - - E0115 - - E0115 - - - - MAJOR - - - E0116 - - E0116 - - - - MINOR - - - E0117 - - E0117 - - - - MAJOR - - - E0119 - - E0119 - - - - MINOR - - - E0202 - - E0202 - - - - MAJOR - - - E0203 - - E0203 - - - - MAJOR - - - E0211 - - E0211 - - - - MAJOR - - - E0213 - - E0213 - - - - MAJOR - - - E0221 - - E0221 - - - - MAJOR - - - E0222 - - E0222 - - - - MAJOR - - - E0235 - - E0235 - - This rule was added in Pylint 1.1.0.

-

This rule is deprecated, use {rule:python:S2733} instead.

- ]]> -
- MINOR - DEPRECATED -
- - E0236 - - E0236 - - This rule was added in Pylint 1.2.0.

]]> -
- MINOR -
- - E0237 - - E0237 - - - - MINOR - - - E0238 - - E0238 - - This rule was added in Pylint 1.2.0.

]]> -
- MINOR -
- - E0239 - - E0239 - - - - MINOR - - - E0240 - - E0240 - - - - MINOR - - - E0241 - - E0241 - - - - MINOR - - - E0301 - - E0301 - - - - MINOR - - - E0302 - - E0302 - - - - MINOR - - - E0303 - - E0303 - - - - MINOR - - - E0401 - - E0401 - - - - MINOR - - - E0402 - - E0402 - - - - MINOR - - - E0501 - - E0501 - - - - MAJOR - - - E0502 - - E0502 - - - - MAJOR - - - E0503 - - E0503 - - - - MAJOR - - - E0601 - - E0601 - - - - MAJOR - - - E0602 - - E0602 - - - - MAJOR - - - E0603 - - E0603 - - - - MINOR - - - E0604 - - E0604 - - This rule was added in Pylint 0.27.0.

]]> -
- MINOR -
- - E0611 - - E0611 - - - - MAJOR - - - E0632 - - E0632 - - - - MINOR - - - E0633 - - E0633 - - - - MINOR - - - E0701 - - E0701 - - - - MAJOR - - - E0702 - - E0702 - - - - MAJOR - - - E0703 - - E0703 - - - - MINOR - - - E0704 - - E0704 - - - - MAJOR - - - E0710 - - E0710 - - - - MAJOR - - - E0711 - - E0711 - - - - MAJOR - - - E0712 - - E0712 - - - - MINOR - - - E1001 - - E1001 - - = 3.0.]]> - - MAJOR - - - E1002 - - E1002 - - = 3.0.]]> - - MAJOR - - - E1003 - - E1003 - - - - MAJOR - - - E1004 - - E1004 - - = 3.0.]]> - - MINOR - - - E1101 - - E1101 - - - - MAJOR - - - E1102 - - E1102 - - - - MAJOR - - - E1103 - - E1103 - - - - MAJOR - - - E1111 - - E1111 - - - - MAJOR - - - E1120 - - E1120 - - - - MAJOR - - - E1121 - - E1121 - - - - MAJOR - - - E1122 - - E1122 - - - - MAJOR - - - E1123 - - E1123 - - - - MAJOR - - - E1124 - - E1124 - - - - MAJOR - - - E1125 - - E1125 - - - - MAJOR - - - E1126 - - E1126 - - - - MAJOR - - - E1127 - - E1127 - - - - MAJOR - - - E1128 - - E1128 - - - - MAJOR - - - E1129 - - E1129 - - - - MAJOR - - - E1130 - - E1130 - - - - MAJOR - - - E1131 - - E1131 - - - - MAJOR - - - E1132 - - E1132 - - - - MAJOR - - - E1133 - - E1133 - - - - MAJOR - - - E1134 - - E1134 - - - - MAJOR - - - E1135 - - E1135 - - - - MAJOR - - - E1136 - - E1136 - - - - MAJOR - - - E1137 - - E1137 - - - - MAJOR - - - E1138 - - E1138 - - - - MAJOR - - - E1139 - - E1139 - - - - MAJOR - - - E1140 - - E1140 - - - - MAJOR - - - E1200 - - E1200 - - - - MAJOR - - - E1201 - - E1201 - - - - MAJOR - - - E1205 - - E1205 - - - - MAJOR - - - E1206 - - E1206 - - - - MAJOR - - - E1300 - - E1300 - - - - MAJOR - - - E1301 - - E1301 - - - - MAJOR - - - E1302 - - E1302 - - - - MAJOR - - - E1303 - - E1303 - - - - MAJOR - - - E1304 - - E1304 - - - - MAJOR - - - E1305 - - E1305 - - - - MAJOR - - - E1306 - - E1306 - - - - MAJOR - - - E1307 - - E1307 - - - - MINOR - - - E1310 - - E1310 - - This rule was added in Pylint 0.28.0.

]]> -
- MINOR -
- - E1507 - - E1507 - - - - MINOR - - - E1601 - - E1601 - - - - MAJOR - - - E1602 - - E1602 - - - - MAJOR - - - E1603 - - E1603 - - - - MAJOR - - - E1604 - - E1604 - - - - MAJOR - - - E1605 - - E1605 - - - - MAJOR - - - E1606 - - E1606 - - = 3.0.]]> - - MAJOR - - - E1607 - operator]]> - E1607 - - " operator is used instead of "!=". This is removed in Python 3. This message can't be emitted when using Python >= 3.0.]]> - - MAJOR - - - E1608 - - E1608 - - = 3.0.]]> - - MAJOR - - - E1609 - - E1609 - - = 3.0.]]> - - MAJOR - - - E1610 - - E1610 - - = 3.0.]]> - - MAJOR - - - E1700 - - E1700 - - - - MAJOR - - - E1701 - - E1701 - - - - MINOR - - - F0001 - - F0001 - - - - MAJOR - - - F0002 - - F0002 - - - - MAJOR - - - F0003 - - F0003 - - - - MAJOR - - - F0004 - - F0004 - - - - MAJOR - - - F0010 - - F0010 - - - - MAJOR - - - F0202 - - F0202 - - - - MAJOR - - - F0220 - - F0220 - - - - MAJOR - - - F0321 - - F0321 - - - - MAJOR - - - F0401 - - F0401 - - - - MAJOR - - - I0001 - - I0001 - - - - INFO - - - I0010 - - I0010 - - - - INFO - - - I0011 - - I0011 - - - - INFO - - - I0012 - - I0012 - - - - INFO - - - I0013 - - I0013 - - - - INFO - - - I0020 - - I0020 - - - - INFO - - - I0021 - - I0021 - - - - INFO - - - I0022 - - I0022 - - = 0.26]]> - - INFO - - - I0023 - - I0023 - - - - INFO - - - I1101 - - I1101 - - - - INFO - - - R0123 - - R0123 - - - - MINOR - - - R0124 - - R0124 - - - - MINOR - - - R0201 - - R0201 - - This rule is deprecated, use {rule:python:S2325} instead.

- ]]> -
- MINOR - DEPRECATED -
- - R0202 - - R0202 - - - - MINOR - - - R0203 - - R0203 - - - - MINOR - - - R0205 - - R0205 - - - - MINOR - - - R0401 - - R0401 - - While cyclic imports terminate and execute without - surprises in most cases, the circular dependency often indicates a - design issue in the code base.

- ]]> -
- MINOR -
- - R0801 - - R0801 - - This rule is deprecated, use {rule:common-py:DuplicatedBlocks} instead.

- ]]> -
- MINOR - DEPRECATED -
- - R0901 - - R0901 - - - - MINOR - - - R0902 - - R0902 - - - - MINOR - - - R0903 - - R0903 - - - - MINOR - - - R0904 - - R0904 - - - - MINOR - - - R0911 - - R0911 - - This rule is deprecated, use {rule:python:S1142} instead.

- ]]> -
- MINOR - DEPRECATED -
- - R0912 - - R0912 - - This rule is deprecated, use {rule:python:FunctionComplexity} instead.

- ]]> -
- MINOR - DEPRECATED -
- - R0913 - - R0913 - - This rule is deprecated, use {rule:python:S107} instead.

- ]]> -
- MINOR - DEPRECATED -
- - R0914 - - R0914 - - - - MINOR - - - R0915 - - R0915 - - - - MINOR - - - R0916 - - R0916 - - - - MINOR - - - R0921 - - R0921 - - - - MINOR - - - R0922 - - R0922 - - - - MINOR - - - R0923 - - R0923 - - - - MINOR - - - R1701 - - R1701 - - - - MINOR - - - R1702 - - R1702 - - - - MINOR - - - R1703 - - R1703 - - - - MINOR - - - R1704 - - R1704 - - - - MINOR - - - R1705 - - R1705 - - - - MINOR - - - R1706 - - R1706 - - - - MINOR - - - R1707 - - R1707 - - - - MINOR - - - R1708 - - R1708 - - - - MINOR - - - R1709 - - R1709 - - - - MINOR - - - R1710 - - R1710 - - - - MINOR - - - R1711 - - R1711 - - - - MINOR - - - R1712 - - R1712 - - - - MINOR - - - R1713 - - R1713 - - - - MINOR - - - R1714 - - R1714 - - - - MINOR - - - R1715 - - R1715 - - - - MINOR - - - R1716 - - R1716 - - - - MINOR - - - R1717 - - R1717 - - - - MINOR - - - R1718 - - R1718 - - - - MINOR - - - R1719 - - R1719 - - - - MINOR - - - R1720 - - R1720 - - - - MINOR - - - W0101 - - W0101 - - This rule is deprecated, use {rule:python:S1763} instead.

- ]]> -
- MINOR - DEPRECATED -
- - W0102 - - W0102 - - - - MINOR - - - W0104 - - W0104 - - - - MINOR - - - W0105 - - W0105 - - - - MINOR - - - W0106 - - W0106 - - - - MINOR - - - W0107 - - W0107 - - This rule is deprecated, use {rule:python:S2772} instead.

- ]]> -
- MINOR - DEPRECATED -
- - W0108 - - W0108 - - - - MINOR - - - W0109 - - W0109 - - - - MINOR - - - W0110 - - W0110 - - = 3.0. -

This rule was added in Pylint 0.27.0.

]]> -
- MINOR -
- - W0111 - - W0111 - - - - MINOR - - - W0120 - - W0120 - - This rule was added in Pylint 0.28.0.

]]> -
- MINOR -
- - W0121 - - W0121 - - = 3.0. -

This rule was added in Pylint 1.0.0.

]]> -
- MINOR -
- - W0122 - - W0122 - - This rule is deprecated, use {rule:python:ExecStatementUsage} instead.

- ]]> -
- MINOR - DEPRECATED -
- - W0123 - - W0123 - - This rule was added in Pylint 1.2.0.

]]> -
- MINOR -
- - W0124 - - W0124 - - - - MINOR - - - W0125 - - W0125 - - - - MINOR - - - W0141 - - W0141 - - - - MINOR - - - W0142 - - W0142 - - - - MINOR - - - W0143 - - W0143 - - - - MINOR - - - W0150 - - W0150 - - - - MINOR - - - W0199 - - W0199 - - - - MINOR - - - W0201 - - W0201 - - - - MINOR - - - W0211 - - W0211 - - - - MINOR - - - W0212 - - W0212 - - - - MINOR - - - W0221 - - W0221 - - - - MINOR - - - W0222 - - W0222 - - - - MINOR - - - W0223 - - W0223 - - - - MINOR - - - W0231 - - W0231 - - - - MINOR - - - W0232 - - W0232 - - - - MINOR - - - W0233 - - W0233 - - - - MINOR - - - W0234 - - W0234 - - This rule was added in Pylint 1.1.0.

]]> -
- MINOR -
- - W0235 - - W0235 - - - - MINOR - - - W0301 - - W0301 - - - - MINOR - - - W0311 - - W0311 - - - - MINOR - - - W0312 - - W0312 - - As indentation is part of Python's syntax, - inconsistencies in its usage are usually considered a - major issue.

- ]]> -
- MAJOR -
- - W0331 - operator]]> - W0331 - - " operator is used instead of "!=". -

This rule is deprecated, use {rule:python:InequalityUsage} instead.

- ]]> -
- MINOR - DEPRECATED -
- - W0332 - - W0332 - - = 3.0.]]> - - MINOR - - - W0333 - - W0333 - - This rule is deprecated, use {rule:python:BackticksUsage} instead.

- ]]> -
- MINOR - DEPRECATED -
- - W0401 - - W0401 - - - - MINOR - - - W0402 - - W0402 - - - - MINOR - - - W0403 - - W0403 - - = 3.0.]]> - - MINOR - - - W0404 - - W0404 - - - - MINOR - - - W0406 - - W0406 - - - - MINOR - - - W0410 - - W0410 - - - - MINOR - - - W0511 - - W0511 - - - - MINOR - - - W0512 - - W0512 - - = 3.0. -

This rule was added in Pylint 1.0.0.

]]> -
- MINOR -
- - W0601 - - W0601 - - - - MINOR - - - W0602 - - W0602 - - - - MINOR - - - W0603 - - W0603 - - - - MINOR - - - W0604 - - W0604 - - - - MINOR - - - W0611 - - W0611 - - - - MINOR - - - W0612 - - W0612 - - - - MINOR - - - W0613 - - W0613 - - - - MINOR - - - W0614 - - W0614 - - - - MINOR - - - W0621 - - W0621 - - - - MINOR - - - W0622 - - W0622 - - - - MINOR - - - W0623 - - W0623 - - - - MINOR - - - W0631 - - W0631 - - - - MINOR - - - W0632 - - W0632 - - This rule was added in Pylint 1.1.0.

]]> -
- MINOR -
- - W0633 - - W0633 - - This rule was added in Pylint 1.1.0.

]]> -
- MINOR -
- - W0640 - - W0640 - - - - MINOR - - - W0641 - - W0641 - - - - MINOR - - - W0642 - - W0642 - - - - MINOR - - - W0701 - - W0701 - - - - MINOR - - - W0702 - - W0702 - - Catching exceptions should be as precise as - possible. The type of exceptions that can be raised should be known in - advance. Using catch-all-constructs hides potential - errors (including syntax ones), defeats the purpose of - knowing the type of error that occurred, and prohibits the use of - tailored responses.

- ]]> -
- MINOR -
- - W0703 - - W0703 - - Catching exceptions should be as precise as possible. The type of - exceptions that can be raised should be known in advance. Using a - catch-all Exception instance defeats the purpose of knowing the type - of error that occur-ed, and prohibits the use of tailored responses.

- ]]> -
- MINOR -
- - W0704 - - W0704 - - - - MINOR - - - W0705 - - W0705 - - - - MINOR - - - W0706 - - W0706 - - - - MINOR - - - W0710 - - W0710 - - = 3.0.]]> - - MINOR - - - W0711 - - W0711 - - - - MINOR - - - W0712 - - W0712 - - = 3.0. -

This rule was added in Pylint 1.0.0.

]]> -
- MINOR -
- - W0715 - - W0715 - - - - MINOR - - - W0716 - - W0716 - - - - MINOR - - - W1001 - - W1001 - - = 3.0.]]> - - MINOR - - - W1111 - - W1111 - - - - MINOR - - - W1113 - - W1113 - - - - MINOR - - - W1201 - - W1201 - - (format_string % (format_args...))". Such - calls should leave string interpolation to the logging method itself - and be written "logging.(format_string, - format_args...)" so that the program may avoid incurring the cost of - the interpolation in those cases in which no message will be - logged. For more, see http://www.python.org/dev/peps/pep-0282/.]]> - - MINOR - - - W1202 - - W1202 - - (format_string.format(format_args...))". Such calls should use % formatting instead, but leave interpolation to the logging function by passing the parameters as arguments.]]> - - MINOR - - - W1203 - - W1203 - - - - MINOR - - - W1300 - - W1300 - - - - MINOR - - - W1301 - - W1301 - - - - MINOR - - - W1302 - - W1302 - - - - MINOR - - - W1303 - - W1303 - - - - MINOR - - - W1304 - - W1304 - - - - MINOR - - - W1305 - - W1305 - - - - MINOR - - - W1306 - - W1306 - - - - MINOR - - - W1307 - - W1307 - - - - MINOR - - - W1308 - - W1308 - - - - MINOR - - - W1401 - - W1401 - - This rule was added in Pylint 0.26.0.

-

This rule is deprecated, use {rule:python:S1717} instead.

]]> -
- MINOR - DEPRECATED -
- - W1402 - - W1402 - - This rule was added in Pylint 0.26.0.

]]> -
- MINOR -
- - W1403 - - W1403 - - - - MINOR - - - W1501 - - W1501 - - - - MINOR - - - W1502 - - W1502 - - = 3.5.]]> - - MINOR - - - W1503 - - W1503 - - - - MINOR - - - W1505 - - W1505 - - - - MINOR - - - W1506 - - W1506 - - - - MINOR - - - W1507 - - W1507 - - - - MINOR - - - W1508 - - W1508 - - - - MINOR - - - W1509 - - W1509 - - - - MINOR - - - W1601 - - W1601 - - - - MINOR - - - W1602 - - W1602 - - - - MINOR - - - W1603 - - W1603 - - - - MINOR - - - W1604 - - W1604 - - - - MINOR - - - W1605 - - W1605 - - - - MINOR - - - W1606 - - W1606 - - - - MINOR - - - W1607 - - W1607 - - - - MINOR - - - W1608 - - W1608 - - - - MINOR - - - W1609 - - W1609 - - - - MINOR - - - W1610 - - W1610 - - - - MINOR - - - W1611 - - W1611 - - - - MINOR - - - W1612 - - W1612 - - - - MINOR - - - W1613 - - W1613 - - - - MINOR - - - W1614 - - W1614 - - - - MINOR - - - W1615 - - W1615 - - - - MINOR - - - W1616 - - W1616 - - - - MINOR - - - W1617 - - W1617 - - - - MINOR - - - W1618 - - W1618 - - - - MINOR - - - W1619 - - W1619 - - - - MINOR - - - W1620 - - W1620 - - - - MINOR - - - W1621 - - W1621 - - - - MINOR - - - W1622 - - W1622 - - - - MINOR - - - W1623 - - W1623 - - - - MINOR - - - W1624 - - W1624 - - - - MINOR - - - W1625 - - W1625 - - - - MINOR - - - W1626 - - W1626 - - - - MINOR - - - W1627 - - W1627 - - - - MINOR - - - W1628 - - W1628 - - - - MINOR - - - W1629 - - W1629 - - - - MINOR - - - W1630 - - W1630 - - - - MINOR - - - W1632 - - W1632 - - - - MINOR - - - W1633 - - W1633 - - - - MINOR - - - W1634 - - W1634 - - - - MINOR - - - W1635 - - W1635 - - - - MINOR - - - W1636 - - W1636 - - - - MINOR - - - W1637 - - W1637 - - - - MINOR - - - W1638 - - W1638 - - - - MINOR - - - W1639 - - W1639 - - - - MINOR - - - W1640 - - W1640 - - - - MINOR - - - W1641 - - W1641 - - - - MINOR - - - W1642 - - W1642 - - - - MINOR - - - W1643 - - W1643 - - - - MINOR - - - W1644 - - W1644 - - - - MINOR - - - W1645 - - W1645 - - - - MINOR - - - W1646 - - W1646 - - - - MINOR - - - W1647 - - W1647 - - - - MINOR - - - W1649 - - W1649 - - - - MINOR - - - W1650 - - W1650 - - - - MINOR - - - W1651 - - W1651 - - - - MINOR - - - W1652 - - W1652 - - - - MINOR - - - W1653 - - W1653 - - - - MINOR - - - W1654 - - W1654 - - - - MINOR - - - W1655 - - W1655 - - - - MINOR - - - W1656 - - W1656 - - - - MINOR - - - W1657 - - W1657 - - - - MINOR - - - W1658 - - W1658 - - - - MINOR - - - W1659 - - W1659 - - - - MAJOR - - - W1660 - - W1660 - - - - MINOR - - - W1661 - - W1661 - - - - MINOR - - - W1662 - - W1662 - - - - MINOR - -
diff --git a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules_generated.xml b/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules_generated.xml deleted file mode 100644 index 73fefc5593..0000000000 --- a/sonar-python-plugin/src/main/resources/org/sonar/plugins/python/pylint/rules_generated.xml +++ /dev/null @@ -1,2563 +0,0 @@ - - - -C0102 - -C0102 - - - - - -C0103 - -C0103 - - - - - -C0111 - -C0111 - - - - - -C0112 - -C0112 - - - - - -C0113 - -C0113 - - - - - -C0121 - -C0121 - - - - - -C0122 - -C0122 - - - - - -C0123 - -C0123 - - - - - -C0200 - -C0200 - - - - - -C0201 - -C0201 - - - - - -C0202 - -C0202 - - - - - -C0203 - -C0203 - - - - - -C0204 - -C0204 - - - - - -C0205 - -C0205 - - - - - -C0301 - -C0301 - - - - - -C0302 - -C0302 - - - - - -C0303 - -C0303 - - - - - -C0304 - -C0304 - - - - - -C0305 - -C0305 - - - - - -C0321 - -C0321 - - - - - -C0325 - -C0325 - - - - - -C0326 - -C0326 - - - - - -C0327 - -C0327 - - - - - -C0328 - -C0328 - - - - - -C0330 - -C0330 - - - - - -C0401 - -C0401 - - - - - -C0402 - -C0402 - - - - - -C0403 - -C0403 - - - - - -C0410 - -C0410 - - - - - -C0411 - -C0411 - - - - - -C0412 - -C0412 - - - - - -C0413 - -C0413 - - - - - -C0414 - -C0414 - - - - - -C1801 - -C1801 - - - - - -E0001 - -E0001 - - - - - -E0011 - -E0011 - - - - - -E0012 - -E0012 - - - - - -E0100 - -E0100 - - - - - -E0101 - -E0101 - - - - - -E0102 - -E0102 - - - - - -E0103 - -E0103 - - - - - -E0104 - -E0104 - - - - - -E0105 - -E0105 - - - - - -E0107 - -E0107 - - - - - -E0108 - -E0108 - - - - - -E0110 - -E0110 - - - - - -E0111 - -E0111 - - - - - -E0112 - -E0112 - - - - - -E0113 - -E0113 - - - - - -E0114 - -E0114 - - - - - -E0115 - -E0115 - - - - - -E0116 - -E0116 - - - - - -E0117 - -E0117 - - - - - -E0119 - -E0119 - - - - - -E0202 - -E0202 - - - - - -E0203 - -E0203 - - - - - -E0211 - -E0211 - - - - - -E0213 - -E0213 - - - - - -E0236 - -E0236 - - - - - -E0237 - -E0237 - - - - - -E0238 - -E0238 - - - - - -E0239 - -E0239 - - - - - -E0240 - -E0240 - - - - - -E0241 - -E0241 - - - - - -E0301 - -E0301 - - - - - -E0302 - -E0302 - - - - - -E0303 - -E0303 - - - - - -E0401 - -E0401 - - - - - -E0402 - -E0402 - - - - - -E0601 - -E0601 - - - - - -E0602 - -E0602 - - - - - -E0603 - -E0603 - - - - - -E0604 - -E0604 - - - - - -E0611 - -E0611 - - - - - -E0633 - -E0633 - - - - - -E0701 - -E0701 - - - - - -E0702 - -E0702 - - - - - -E0703 - -E0703 - - - - - -E0704 - -E0704 - - - - - -E0710 - -E0710 - - - - - -E0711 - -E0711 - - - - - -E0712 - -E0712 - - - - - -E1003 - -E1003 - - - - - -E1101 - -E1101 - - - - - -E1102 - -E1102 - - - - - -E1111 - -E1111 - - - - - -E1120 - -E1120 - - - - - -E1121 - -E1121 - - - - - -E1123 - -E1123 - - - - - -E1124 - -E1124 - - - - - -E1125 - -E1125 - - - - - -E1126 - -E1126 - - - - - -E1127 - -E1127 - - - - - -E1128 - -E1128 - - - - - -E1129 - -E1129 - - - - - -E1130 - -E1130 - - - - - -E1131 - -E1131 - - - - - -E1132 - -E1132 - - - - - -E1133 - -E1133 - - - - - -E1134 - -E1134 - - - - - -E1135 - -E1135 - - - - - -E1136 - -E1136 - - - - - -E1137 - -E1137 - - - - - -E1138 - -E1138 - - - - - -E1139 - -E1139 - - - - - -E1140 - -E1140 - - - - - -E1200 - -E1200 - - - - - -E1201 - -E1201 - - - - - -E1205 - -E1205 - - - - - -E1206 - -E1206 - - - - - -E1300 - -E1300 - - - - - -E1301 - -E1301 - - - - - -E1302 - -E1302 - - - - - -E1303 - -E1303 - - - - - -E1304 - -E1304 - - - - - -E1305 - -E1305 - - - - - -E1306 - -E1306 - - - - - -E1307 - -E1307 - - - - - -E1310 - -E1310 - - - - - -E1507 - -E1507 - - - - - -E1601 - -E1601 - - - - - -E1602 - -E1602 - - - - - -E1603 - -E1603 - - - - - -E1604 - -E1604 - - - - - -E1605 - -E1605 - - - - - -E1700 - -E1700 - - - - - -E1701 - -E1701 - - - - - -F0001 - -F0001 - - - - - -F0002 - -F0002 - - - - - -F0010 - -F0010 - - - - - -F0202 - -F0202 - - - - - -I0001 - -I0001 - - - - - -I0010 - -I0010 - - - - - -I0011 - -I0011 - - - - - -I0013 - -I0013 - - - - - -I0020 - -I0020 - - - - - -I0021 - -I0021 - - - - - -I0022 - -I0022 - -= 0.26]]> - - - -I0023 - -I0023 - - - - - -I1101 - -I1101 - - - - - -R0123 - -R0123 - - - - - -R0124 - -R0124 - - - - - -R0201 - -R0201 - - - - - -R0202 - -R0202 - - - - - -R0203 - -R0203 - - - - - -R0205 - -R0205 - - - - - -R0401 - -R0401 - - - - - -R0801 - -R0801 - - - - - -R0901 - -R0901 - - - - - -R0902 - -R0902 - - - - - -R0903 - -R0903 - - - - - -R0904 - -R0904 - - - - - -R0911 - -R0911 - - - - - -R0912 - -R0912 - - - - - -R0913 - -R0913 - - - - - -R0914 - -R0914 - - - - - -R0915 - -R0915 - - - - - -R0916 - -R0916 - - - - - -R1701 - -R1701 - - - - - -R1702 - -R1702 - - - - - -R1703 - -R1703 - - - - - -R1704 - -R1704 - - - - - -R1705 - -R1705 - - - - - -R1706 - -R1706 - - - - - -R1707 - -R1707 - - - - - -R1708 - -R1708 - - - - - -R1709 - -R1709 - - - - - -R1710 - -R1710 - - - - - -R1711 - -R1711 - - - - - -R1712 - -R1712 - - - - - -R1713 - -R1713 - - - - - -R1714 - -R1714 - - - - - -R1715 - -R1715 - - - - - -R1716 - -R1716 - - - - - -R1717 - -R1717 - - - - - -R1718 - -R1718 - - - - - -R1719 - -R1719 - - - - - -R1720 - -R1720 - - - - - -W0101 - -W0101 - - - - - -W0102 - -W0102 - - - - - -W0104 - -W0104 - - - - - -W0105 - -W0105 - - - - - -W0106 - -W0106 - - - - - -W0107 - -W0107 - - - - - -W0108 - -W0108 - - - - - -W0109 - -W0109 - - - - - -W0111 - -W0111 - - - - - -W0120 - -W0120 - - - - - -W0122 - -W0122 - - - - - -W0123 - -W0123 - - - - - -W0124 - -W0124 - - - - - -W0125 - -W0125 - - - - - -W0143 - -W0143 - - - - - -W0150 - -W0150 - - - - - -W0199 - -W0199 - - - - - -W0201 - -W0201 - - - - - -W0211 - -W0211 - - - - - -W0212 - -W0212 - - - - - -W0221 - -W0221 - - - - - -W0222 - -W0222 - - - - - -W0223 - -W0223 - - - - - -W0231 - -W0231 - - - - - -W0232 - -W0232 - - - - - -W0233 - -W0233 - - - - - -W0235 - -W0235 - - - - - -W0301 - -W0301 - - - - - -W0311 - -W0311 - - - - - -W0312 - -W0312 - - - - - -W0401 - -W0401 - - - - - -W0402 - -W0402 - - - - - -W0404 - -W0404 - - - - - -W0406 - -W0406 - - - - - -W0410 - -W0410 - - - - - -W0511 - -W0511 - - - - - -W0601 - -W0601 - - - - - -W0602 - -W0602 - - - - - -W0603 - -W0603 - - - - - -W0604 - -W0604 - - - - - -W0611 - -W0611 - - - - - -W0612 - -W0612 - - - - - -W0613 - -W0613 - - - - - -W0614 - -W0614 - - - - - -W0621 - -W0621 - - - - - -W0622 - -W0622 - - - - - -W0623 - -W0623 - - - - - -W0631 - -W0631 - - - - - -W0632 - -W0632 - - - - - -W0640 - -W0640 - - - - - -W0641 - -W0641 - - - - - -W0642 - -W0642 - - - - - -W0702 - -W0702 - - - - - -W0703 - -W0703 - - - - - -W0705 - -W0705 - - - - - -W0706 - -W0706 - - - - - -W0711 - -W0711 - - - - - -W0715 - -W0715 - - - - - -W0716 - -W0716 - - - - - -W1113 - -W1113 - - - - - -W1201 - -W1201 - -(format_string % (format_args...))". Such calls should leave string interpolation to the logging method itself and be written "logging.(format_string, format_args...)" so that the program may avoid incurring the cost of the interpolation in those cases in which no message will be logged. For more, see http://www.python.org/dev/peps/pep-0282/.]]> - - - -W1202 - -W1202 - -(format_string.format(format_args...))". Such calls should use % formatting instead, but leave interpolation to the logging function by passing the parameters as arguments.]]> - - - -W1203 - -W1203 - - - - - -W1300 - -W1300 - - - - - -W1301 - -W1301 - - - - - -W1302 - -W1302 - - - - - -W1303 - -W1303 - - - - - -W1304 - -W1304 - - - - - -W1305 - -W1305 - - - - - -W1306 - -W1306 - - - - - -W1307 - -W1307 - - - - - -W1308 - -W1308 - - - - - -W1401 - -W1401 - - - - - -W1402 - -W1402 - - - - - -W1403 - -W1403 - - - - - -W1501 - -W1501 - - - - - -W1503 - -W1503 - - - - - -W1505 - -W1505 - - - - - -W1506 - -W1506 - - - - - -W1507 - -W1507 - - - - - -W1508 - -W1508 - - - - - -W1509 - -W1509 - - - - - -W1601 - -W1601 - - - - - -W1602 - -W1602 - - - - - -W1603 - -W1603 - - - - - -W1604 - -W1604 - - - - - -W1605 - -W1605 - - - - - -W1606 - -W1606 - - - - - -W1607 - -W1607 - - - - - -W1608 - -W1608 - - - - - -W1609 - -W1609 - - - - - -W1610 - -W1610 - - - - - -W1611 - -W1611 - - - - - -W1612 - -W1612 - - - - - -W1613 - -W1613 - - - - - -W1614 - -W1614 - - - - - -W1615 - -W1615 - - - - - -W1616 - -W1616 - - - - - -W1617 - -W1617 - - - - - -W1618 - -W1618 - - - - - -W1619 - -W1619 - - - - - -W1620 - -W1620 - - - - - -W1621 - -W1621 - - - - - -W1622 - -W1622 - - - - - -W1623 - -W1623 - - - - - -W1624 - -W1624 - - - - - -W1625 - -W1625 - - - - - -W1626 - -W1626 - - - - - -W1627 - -W1627 - - - - - -W1628 - -W1628 - - - - - -W1629 - -W1629 - - - - - -W1630 - -W1630 - - - - - -W1632 - -W1632 - - - - - -W1633 - -W1633 - - - - - -W1634 - -W1634 - - - - - -W1635 - -W1635 - - - - - -W1636 - -W1636 - - - - - -W1637 - -W1637 - - - - - -W1638 - -W1638 - - - - - -W1639 - -W1639 - - - - - -W1640 - -W1640 - - - - - -W1641 - -W1641 - - - - - -W1642 - -W1642 - - - - - -W1643 - -W1643 - - - - - -W1644 - -W1644 - - - - - -W1645 - -W1645 - - - - - -W1646 - -W1646 - - - - - -W1647 - -W1647 - - - - - -W1649 - -W1649 - - - - - -W1650 - -W1650 - - - - - -W1651 - -W1651 - - - - - -W1652 - -W1652 - - - - - -W1653 - -W1653 - - - - - -W1654 - -W1654 - - - - - -W1655 - -W1655 - - - - - -W1656 - -W1656 - - - - - -W1657 - -W1657 - - - - - -W1658 - -W1658 - - - - - -W1659 - -W1659 - - - - - -W1660 - -W1660 - - - - - -W1661 - -W1661 - - - - - -W1662 - -W1662 - - - - -