Skip to content

Commit 2ef9e5c

Browse files
SONARPY-2371 Move the custom rule examples to sonar-python repository (#2183)
Co-authored-by: Maksim Grebeniuk <[email protected]>
1 parent f5d2868 commit 2ef9e5c

19 files changed

+416
-0
lines changed

docs/LICENSE.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
MIT No Attribution
2+
3+
Copyright 2023, SonarSource SA
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so.
11+
12+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18+
SOFTWARE.

docs/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>org.sonarsource.python</groupId>
7+
<artifactId>python</artifactId>
8+
<version>4.25-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>docs</artifactId>
12+
<packaging>pom</packaging>
13+
14+
<name>SonarQube Python :: Documentation</name>
15+
16+
<modules>
17+
<module>python-custom-rule-examples</module>
18+
</modules>
19+
20+
</project>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>org.sonarsource.python</groupId>
8+
<artifactId>docs</artifactId>
9+
<version>4.25-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>python-custom-rule-examples</artifactId>
13+
<packaging>sonar-plugin</packaging>
14+
15+
<name>SonarQube Python :: Documentation :: Custom Rules Example</name>
16+
<description>Python custom rule examples for SonarQube</description>
17+
18+
<properties>
19+
<sonar.python.version>3.15.0.9787</sonar.python.version>
20+
</properties>
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.sonarsource.sonarqube</groupId>
24+
<artifactId>sonar-plugin-api</artifactId>
25+
<version>7.9</version>
26+
<scope>provided</scope>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.sonarsource.python</groupId>
30+
<artifactId>sonar-python-plugin</artifactId>
31+
<type>sonar-plugin</type>
32+
<version>${sonar.python.version}</version>
33+
<scope>provided</scope>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.sonarsource.python</groupId>
37+
<artifactId>python-checks-testkit</artifactId>
38+
<version>${sonar.python.version}</version>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>junit</groupId>
43+
<artifactId>junit</artifactId>
44+
<version>4.13.1</version>
45+
<scope>test</scope>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.mockito</groupId>
49+
<artifactId>mockito-core</artifactId>
50+
<scope>test</scope>
51+
</dependency>
52+
</dependencies>
53+
54+
<build>
55+
<plugins>
56+
<plugin>
57+
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
58+
<artifactId>sonar-packaging-maven-plugin</artifactId>
59+
<version>1.21.0.505</version>
60+
<extensions>true</extensions>
61+
<configuration>
62+
<pluginClass>org.sonar.samples.python.CustomPythonRulesPlugin</pluginClass>
63+
<requirePlugins>python:${sonar.python.version}</requirePlugins>
64+
<sonarLintSupported>true</sonarLintSupported>
65+
<skipDependenciesPackaging>true</skipDependenciesPackaging>
66+
</configuration>
67+
</plugin>
68+
69+
<plugin>
70+
<artifactId>maven-compiler-plugin</artifactId>
71+
<version>3.7.0</version>
72+
<configuration>
73+
<source>1.8</source>
74+
<target>1.8</target>
75+
</configuration>
76+
</plugin>
77+
<plugin>
78+
<groupId>com.mycila</groupId>
79+
<artifactId>license-maven-plugin</artifactId>
80+
<configuration>
81+
<licenseSets>
82+
<licenseSet>
83+
<header>${project.basedir}/src/main/resources/license-header.txt</header>
84+
</licenseSet>
85+
</licenseSets>
86+
</configuration>
87+
</plugin>
88+
</plugins>
89+
</build>
90+
91+
</project>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
package org.sonar.samples.python;
6+
7+
import java.io.ByteArrayOutputStream;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.net.URL;
11+
import java.nio.charset.StandardCharsets;
12+
import java.util.Arrays;
13+
import java.util.HashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
import org.sonar.api.server.rule.RulesDefinition;
17+
import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader;
18+
import org.sonar.plugins.python.api.PythonCustomRuleRepository;
19+
import org.sonar.samples.python.checks.CustomPythonSubscriptionCheck;
20+
import org.sonar.samples.python.checks.CustomPythonVisitorCheck;
21+
22+
public class CustomPythonRuleRepository implements RulesDefinition, PythonCustomRuleRepository {
23+
24+
@Override
25+
public void define(Context context) {
26+
NewRepository repository = context.createRepository(repositoryKey(), "py").setName("My custom repo");
27+
new RulesDefinitionAnnotationLoader().load(repository, checkClasses().toArray(new Class[] {}));
28+
Map<String, String> remediationCosts = new HashMap<>();
29+
remediationCosts.put(CustomPythonVisitorCheck.RULE_KEY, "5min");
30+
remediationCosts.put(CustomPythonSubscriptionCheck.RULE_KEY, "10min");
31+
repository.rules().forEach(rule -> rule.setDebtRemediationFunction(
32+
rule.debtRemediationFunctions().constantPerIssue(remediationCosts.get(rule.key()))));
33+
34+
// Optionally override html description from annotation with content from html files
35+
repository.rules().forEach(rule -> rule.setHtmlDescription(loadResource("/org/sonar/l10n/python/rules/python/" + rule.key() + ".html")));
36+
repository.done();
37+
}
38+
39+
@Override
40+
public String repositoryKey() {
41+
return "python-custom-rules";
42+
}
43+
44+
@Override
45+
public List<Class> checkClasses() {
46+
return Arrays.asList(CustomPythonVisitorCheck.class, CustomPythonSubscriptionCheck.class);
47+
}
48+
49+
String loadResource(String path) {
50+
URL resource = getClass().getResource(path);
51+
if (resource == null) {
52+
throw new IllegalStateException("Resource not found: " + path);
53+
}
54+
return readResource(resource);
55+
}
56+
57+
static String readResource(URL resource) {
58+
ByteArrayOutputStream result = new ByteArrayOutputStream();
59+
try (InputStream in = resource.openStream()) {
60+
byte[] buffer = new byte[1024];
61+
for (int len = in.read(buffer); len != -1; len = in.read(buffer)) {
62+
result.write(buffer, 0, len);
63+
}
64+
return new String(result.toByteArray(), StandardCharsets.UTF_8);
65+
} catch (IOException e) {
66+
throw new IllegalStateException("Failed to read resource: " + resource, e);
67+
}
68+
}
69+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
package org.sonar.samples.python;
6+
7+
import org.sonar.api.Plugin;
8+
9+
public class CustomPythonRulesPlugin implements Plugin {
10+
11+
@Override
12+
public void define(Context context) {
13+
context.addExtension(CustomPythonRuleRepository.class);
14+
}
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
package org.sonar.samples.python.checks;
6+
7+
import org.sonar.check.Priority;
8+
import org.sonar.check.Rule;
9+
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
10+
import org.sonar.plugins.python.api.tree.ForStatement;
11+
import org.sonar.plugins.python.api.tree.Tree;
12+
13+
@Rule(
14+
key = CustomPythonSubscriptionCheck.RULE_KEY,
15+
priority = Priority.MINOR,
16+
name = "Python subscription visitor check",
17+
description = "desc")
18+
public class CustomPythonSubscriptionCheck extends PythonSubscriptionCheck {
19+
20+
public static final String RULE_KEY = "subscription";
21+
22+
@Override
23+
public void initialize(Context context) {
24+
context.registerSyntaxNodeConsumer(Tree.Kind.FOR_STMT, ctx -> ctx.addIssue(((ForStatement) ctx.syntaxNode()).forKeyword(), "For statement."));
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
package org.sonar.samples.python.checks;
6+
7+
import org.sonar.check.Priority;
8+
import org.sonar.check.Rule;
9+
import org.sonar.plugins.python.api.PythonVisitorCheck;
10+
import org.sonar.plugins.python.api.tree.FunctionDef;
11+
12+
@Rule(
13+
key = CustomPythonVisitorCheck.RULE_KEY,
14+
priority = Priority.MINOR,
15+
name = "Python visitor check",
16+
description = "desc")
17+
public class CustomPythonVisitorCheck extends PythonVisitorCheck {
18+
19+
public static final String RULE_KEY = "visitor";
20+
21+
@Override
22+
public void visitFunctionDef(FunctionDef pyFunctionDefTree) {
23+
addIssue(pyFunctionDefTree.name(), "Function def.");
24+
super.visitFunctionDef(pyFunctionDefTree);
25+
}
26+
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
@ParametersAreNonnullByDefault
6+
package org.sonar.samples.python.checks;
7+
8+
import javax.annotation.ParametersAreNonnullByDefault;
9+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright (C) 2011-2024 SonarSource SA - mailto:info AT sonarsource DOT com
3+
* This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.
4+
*/
5+
@ParametersAreNonnullByDefault
6+
package org.sonar.samples.python;
7+
8+
import javax.annotation.ParametersAreNonnullByDefault;
9+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Copyright (C) ${license.years} ${license.owner} - ${license.mailto}
2+
This code is released under [MIT No Attribution](https://opensource.org/licenses/MIT-0) license.

0 commit comments

Comments
 (0)