Skip to content

Commit f5bd3d5

Browse files
guillaume-dequennesonartech
authored andcommitted
SONARPY-3948 Agentic AI Quality Profiles for Python (#990)
GitOrigin-RevId: 7a5b5a8e74e4442c60394341f3f77a1a5e0231af
1 parent 1b51ea1 commit f5bd3d5

File tree

11 files changed

+516
-5
lines changed

11 files changed

+516
-5
lines changed

python-checks/src/main/java/org/sonar/python/checks/OpenSourceCheckList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public class OpenSourceCheckList {
111111

112112
public static final String RESOURCE_FOLDER = "org/sonar/l10n/py/rules/python";
113113
public static final String SONAR_WAY_PROFILE_LOCATION = RESOURCE_FOLDER + "/Sonar_way_profile.json";
114+
public static final String AI_QUALITY_PROFILE_LOCATION = RESOURCE_FOLDER + "/AI_quality_profile.json";
114115

115116
public Stream<Class<?>> getChecks() {
116117
return Stream.of(
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
{
2+
"name": "AI quality profile",
3+
"ruleKeys": [
4+
"S100",
5+
"S101",
6+
"S107",
7+
"S108",
8+
"S112",
9+
"S116",
10+
"S117",
11+
"S125",
12+
"S905",
13+
"S930",
14+
"S935",
15+
"S1045",
16+
"S1066",
17+
"S1134",
18+
"S1135",
19+
"S1143",
20+
"S1144",
21+
"S1172",
22+
"S1186",
23+
"S1192",
24+
"S1226",
25+
"S1244",
26+
"S1313",
27+
"S1481",
28+
"S1515",
29+
"S1542",
30+
"S1607",
31+
"S1656",
32+
"S1700",
33+
"S1716",
34+
"S1751",
35+
"S1763",
36+
"S1764",
37+
"S1854",
38+
"S1862",
39+
"S1871",
40+
"S2053",
41+
"S2068",
42+
"S2077",
43+
"S2092",
44+
"S2115",
45+
"S2159",
46+
"S2190",
47+
"S2201",
48+
"S2208",
49+
"S2245",
50+
"S2257",
51+
"S2275",
52+
"S2612",
53+
"S2638",
54+
"S2710",
55+
"S2711",
56+
"S2734",
57+
"S2737",
58+
"S2755",
59+
"S2757",
60+
"S2761",
61+
"S2772",
62+
"S2823",
63+
"S2836",
64+
"S2876",
65+
"S3329",
66+
"S3330",
67+
"S3358",
68+
"S3403",
69+
"S3457",
70+
"S3516",
71+
"S3626",
72+
"S3699",
73+
"S3752",
74+
"S3776",
75+
"S3923",
76+
"S3981",
77+
"S3984",
78+
"S3985",
79+
"S4143",
80+
"S4144",
81+
"S4423",
82+
"S4426",
83+
"S4433",
84+
"S4487",
85+
"S4502",
86+
"S4507",
87+
"S4790",
88+
"S4828",
89+
"S4830",
90+
"S5042",
91+
"S5122",
92+
"S5247",
93+
"S5332",
94+
"S5344",
95+
"S5361",
96+
"S5443",
97+
"S5445",
98+
"S5527",
99+
"S5542",
100+
"S5547",
101+
"S5549",
102+
"S5603",
103+
"S5607",
104+
"S5632",
105+
"S5642",
106+
"S5644",
107+
"S5655",
108+
"S5659",
109+
"S5704",
110+
"S5706",
111+
"S5707",
112+
"S5708",
113+
"S5709",
114+
"S5712",
115+
"S5714",
116+
"S5717",
117+
"S5719",
118+
"S5720",
119+
"S5722",
120+
"S5727",
121+
"S5747",
122+
"S5754",
123+
"S5756",
124+
"S5780",
125+
"S5781",
126+
"S5795",
127+
"S5796",
128+
"S5797",
129+
"S5799",
130+
"S5806",
131+
"S5807",
132+
"S5828",
133+
"S5842",
134+
"S5843",
135+
"S5845",
136+
"S5850",
137+
"S5852",
138+
"S5855",
139+
"S5860",
140+
"S5864",
141+
"S5868",
142+
"S5869",
143+
"S5886",
144+
"S5890",
145+
"S5899",
146+
"S5905",
147+
"S5914",
148+
"S5915",
149+
"S5918",
150+
"S5994",
151+
"S5996",
152+
"S6001",
153+
"S6002",
154+
"S6019",
155+
"S6035",
156+
"S6243",
157+
"S6246",
158+
"S6249",
159+
"S6252",
160+
"S6262",
161+
"S6265",
162+
"S6270",
163+
"S6275",
164+
"S6281",
165+
"S6302",
166+
"S6303",
167+
"S6304",
168+
"S6308",
169+
"S6317",
170+
"S6319",
171+
"S6321",
172+
"S6323",
173+
"S6326",
174+
"S6327",
175+
"S6328",
176+
"S6329",
177+
"S6330",
178+
"S6331",
179+
"S6332",
180+
"S6333",
181+
"S6353",
182+
"S6377",
183+
"S6395",
184+
"S6396",
185+
"S6397",
186+
"S6418",
187+
"S6437",
188+
"S6463",
189+
"S6468",
190+
"S6537",
191+
"S6552",
192+
"S6553",
193+
"S6556",
194+
"S6559",
195+
"S6560",
196+
"S6659",
197+
"S6660",
198+
"S6662",
199+
"S6663",
200+
"S6709",
201+
"S6711",
202+
"S6714",
203+
"S6725",
204+
"S6727",
205+
"S6729",
206+
"S6730",
207+
"S6734",
208+
"S6741",
209+
"S6742",
210+
"S6779",
211+
"S6781",
212+
"S6785",
213+
"S6786",
214+
"S6792",
215+
"S6794",
216+
"S6795",
217+
"S6796",
218+
"S6799",
219+
"S6882",
220+
"S6887",
221+
"S6890",
222+
"S6894",
223+
"S6900",
224+
"S6903",
225+
"S6908",
226+
"S6911",
227+
"S6918",
228+
"S6919",
229+
"S6925",
230+
"S6928",
231+
"S6929",
232+
"S6971",
233+
"S6972",
234+
"S6973",
235+
"S6974",
236+
"S6978",
237+
"S6979",
238+
"S6982",
239+
"S6984",
240+
"S6985",
241+
"S7483",
242+
"S7484",
243+
"S7487",
244+
"S7488",
245+
"S7489",
246+
"S7490",
247+
"S7491",
248+
"S7492",
249+
"S7493",
250+
"S7494",
251+
"S7496",
252+
"S7497",
253+
"S7498",
254+
"S7499",
255+
"S7500",
256+
"S7501",
257+
"S7502",
258+
"S7503",
259+
"S7505",
260+
"S7506",
261+
"S7507",
262+
"S7508",
263+
"S7510",
264+
"S7514",
265+
"S7515",
266+
"S7516",
267+
"S7517",
268+
"S7608",
269+
"S7609",
270+
"S7613",
271+
"S7614",
272+
"S7617",
273+
"S7618",
274+
"S7619",
275+
"S7620",
276+
"S7621",
277+
"S7622",
278+
"S7625",
279+
"S7931",
280+
"S7942",
281+
"S7943",
282+
"S8371",
283+
"S8375",
284+
"S8385",
285+
"S8389",
286+
"S8392",
287+
"S8396",
288+
"S8400",
289+
"S8401",
290+
"S8405",
291+
"S8409",
292+
"S8410",
293+
"S8411",
294+
"S8413",
295+
"S8414",
296+
"S8415"
297+
]
298+
}

python-checks/src/test/java/org/sonar/python/checks/OpenSourceCheckListTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void test() {
8282
void validate_sqKey_field_in_json() throws IOException {
8383
try (Stream<Path> fileStream = Files.find(METADATA_DIR, 1, (path, attr) -> path.toString().endsWith(".json"))) {
8484
List<Path> jsonList = fileStream
85-
.filter(path -> !path.toString().endsWith("Sonar_way_profile.json"))
85+
.filter(path -> !path.toString().endsWith("_profile.json"))
8686
.sorted()
8787
.toList();
8888

@@ -108,7 +108,7 @@ void test_no_deprecated_rule_in_default_profile() throws IOException, ParseExcep
108108
List<String> keysInDefaultProfile = getKeysInDefaultProfile(sonarWayProfilePath);
109109

110110
Set<String> deprecatedKeys = jsonList.stream()
111-
.filter(path -> !path.toString().endsWith("Sonar_way_profile.json"))
111+
.filter(path -> !path.toString().endsWith("_profile.json"))
112112
.filter(path1 -> {
113113
try {
114114
return isDeprecated(path1);
@@ -135,7 +135,7 @@ void test_locally_deprecated_rules_stay_deprecated() throws IOException {
135135
List<String> locallyDeprecatedRules = Arrays.asList("S1523", "S4721");
136136
try (Stream<Path> fileStream = Files.find(METADATA_DIR, 1, (path, attr) -> path.toString().endsWith(".json"))) {
137137
Set<String> deprecatedKeys = fileStream
138-
.filter(path -> !path.toString().endsWith("Sonar_way_profile.json"))
138+
.filter(path -> !path.toString().endsWith("_profile.json"))
139139
.filter(path1 -> {
140140
try {
141141
return isDeprecated(path1);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2025 SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.plugins.python;
18+
19+
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
20+
import org.sonar.plugins.python.editions.RepositoryInfoProvider;
21+
import org.sonar.plugins.python.editions.RepositoryInfoProvider.RepositoryInfo;
22+
import org.sonarsource.analyzer.commons.BuiltInQualityProfileJsonLoader;
23+
24+
public class AIQualityProfile implements BuiltInQualityProfilesDefinition {
25+
26+
static final String PROFILE_NAME = "AI quality profile";
27+
28+
private final RepositoryInfoProvider[] editionMetadataProviders;
29+
30+
public AIQualityProfile(RepositoryInfoProvider[] editionMetadataProviders) {
31+
this.editionMetadataProviders = editionMetadataProviders;
32+
}
33+
34+
@Override
35+
public void define(Context context) {
36+
NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile(PROFILE_NAME, Python.KEY);
37+
38+
for (RepositoryInfoProvider repositoryInfoProvider : editionMetadataProviders) {
39+
registerRulesForEdition(repositoryInfoProvider, profile);
40+
}
41+
42+
profile.done();
43+
}
44+
45+
private static void registerRulesForEdition(RepositoryInfoProvider repositoryInfoProvider, NewBuiltInQualityProfile profile) {
46+
RepositoryInfo repositoryInfo = repositoryInfoProvider.getAIProfileInfo();
47+
BuiltInQualityProfileJsonLoader.load(profile, repositoryInfo.repositoryKey(), repositoryInfo.profileLocation());
48+
profile.activeRules().removeIf(rule -> repositoryInfo.disabledRules().contains(rule.ruleKey()));
49+
}
50+
}

0 commit comments

Comments
 (0)