Skip to content

Commit ae9833f

Browse files
authored
Merge branch 'main' into GCI103-python
2 parents 0ca4d02 + 062eee2 commit ae9833f

File tree

8 files changed

+270
-0
lines changed

8 files changed

+270
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- [#76](https://github.com/green-code-initiative/creedengo-python/pull/76) Add rule GCI 103 Dictionary Items Unused. A rule specifying that dictionary iteration should consider the pertinence of the element used.
13+
- [#79](https://github.com/green-code-initiative/creedengo-python/pull/79) Add rule GCI106 Avoid SQRT in a loop
1314
- [#71](https://github.com/green-code-initiative/creedengo-python/pull/71) Add rule GCI96 Require Usecols Argument in Pandas Read Functions
1415
- [#72](https://github.com/green-code-initiative/creedengo-python/pull/72) Add rule GCI97 Optimize square computation (scalar vs vectorized method)
1516

src/it/java/org/greencodeinitiative/creedengo/python/integration/tests/GCIRulesIT.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,4 +320,18 @@ void testGCI103(){
320320

321321
}
322322

323+
@Test
324+
void testGCI106() {
325+
String filePath = "src/avoidSqrtInLoop.py";
326+
String ruleId = "creedengo-python:GCI106";
327+
String ruleMsg = "Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.";
328+
int[] startLines = new int[]{
329+
7, 11, 16, 21, 45, 52, 60
330+
};
331+
int[] endLines = new int[]{
332+
7, 11, 16, 21, 45, 52, 60
333+
};
334+
checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_5MIN);
335+
}
336+
323337
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import math
2+
import numpy as np
3+
4+
results = []
5+
numbers = [1, 2, 3, 4, 5]
6+
for n in numbers:
7+
results.append(math.sqrt(n)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
8+
9+
results = []
10+
for x in [9, 16, 25]:
11+
results.append(np.sqrt(x)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
12+
13+
results = []
14+
i = 0
15+
while i < 3:
16+
results.append(np.sqrt(i)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
17+
i += 1
18+
19+
results = []
20+
for n in range(10):
21+
val = math.sqrt(n) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
22+
results.append(val)
23+
24+
class DataProcessor:
25+
numbers = np.array([1, 4, 9])
26+
sqrt_values = np.sqrt(numbers)
27+
28+
x = math.sqrt(25)
29+
30+
class Analyzer:
31+
def __init__(self, data):
32+
self.results = np.sqrt(np.array(data))
33+
34+
intermediate = []
35+
for n in range(3):
36+
intermediate.append(n)
37+
final = np.sqrt(np.array(intermediate))
38+
39+
results = np.sqrt([x for x in range(5)])
40+
41+
def non_compliant_math_sqrt_in_for_loop():
42+
numbers = [1, 2, 3, 4, 5]
43+
results = []
44+
for num in numbers:
45+
results.append(math.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
46+
return results
47+
48+
def non_compliant_numpy_scalar_sqrt_in_for_loop():
49+
numbers = [1, 2, 3, 4, 5]
50+
results = []
51+
for num in numbers:
52+
results.append(np.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
53+
return results
54+
55+
def non_compliant_math_sqrt_in_while_loop():
56+
numbers = [1, 2, 3, 4, 5]
57+
results = []
58+
i = 0
59+
while i < len(numbers):
60+
results.append(math.sqrt(numbers[i])) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
61+
i += 1
62+
return results
63+
64+
def compliant_numpy_vectorized_sqrt():
65+
numbers = np.array([1, 2, 3, 4, 5])
66+
results = np.sqrt(numbers)
67+
return results
68+
69+
def compliant_list_comprehension_with_vectorized_operation():
70+
numbers = [1, 2, 3, 4, 5]
71+
return np.sqrt(np.array(numbers))
72+
73+
def compliant_math_sqrt_outside_loop():
74+
value = 16
75+
return math.sqrt(value)

src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRe
4343
AvoidMultipleIfElseStatementCheck.class,
4444
PandasRequireUsecolsArgument.class,
4545
OptimizeSquareComputation.class,
46+
AvoidSqrtInLoop.class,
4647
DictionaryItemsUnused.class
4748
);
4849

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
3+
* Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.greencodeinitiative.creedengo.python.checks;
19+
20+
import org.sonar.check.Rule;
21+
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
22+
import org.sonar.plugins.python.api.SubscriptionContext;
23+
import org.sonar.plugins.python.api.tree.CallExpression;
24+
import org.sonar.plugins.python.api.tree.Expression;
25+
import org.sonar.plugins.python.api.tree.QualifiedExpression;
26+
import org.sonar.plugins.python.api.tree.Tree;
27+
28+
@Rule(key = "GCI106")
29+
public class AvoidSqrtInLoop extends PythonSubscriptionCheck {
30+
31+
public static final String DESCRIPTION = "Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.";
32+
33+
@Override
34+
public void initialize(Context context) {
35+
context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::checkCallExpression);
36+
}
37+
38+
private void checkCallExpression(SubscriptionContext context) {
39+
CallExpression callExpression = (CallExpression) context.syntaxNode();
40+
41+
if (isSqrtCall(callExpression) && hasLoopParent(callExpression)) {
42+
context.addIssue(callExpression, DESCRIPTION);
43+
}
44+
}
45+
46+
private boolean isSqrtCall(CallExpression callExpression) {
47+
Expression callee = callExpression.callee();
48+
49+
// Check for direct calls to math.sqrt
50+
if (callee.is(Tree.Kind.QUALIFIED_EXPR)) {
51+
QualifiedExpression qualifiedExpression = (QualifiedExpression) callee;
52+
String methodName = qualifiedExpression.name().name();
53+
54+
if ("sqrt".equals(methodName)) {
55+
Expression qualifier = qualifiedExpression.qualifier();
56+
if (qualifier != null && qualifier.is(Tree.Kind.NAME)) {
57+
String qualifierName = qualifier.firstToken().value();
58+
return "math".equals(qualifierName) || "np".equals(qualifierName) || "numpy".equals(qualifierName);
59+
}
60+
}
61+
}
62+
63+
return false;
64+
}
65+
66+
private boolean hasLoopParent(Tree tree) {
67+
for (Tree parent = tree.parent(); parent != null; parent = parent.parent()) {
68+
Tree.Kind kind = parent.getKind();
69+
if (kind == Tree.Kind.FOR_STMT || kind == Tree.Kind.WHILE_STMT) {
70+
return true;
71+
}
72+
}
73+
return false;
74+
}
75+
}

src/main/resources/org/greencodeinitiative/creedengo/python/creedengo_way_profile.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"GCI96",
1414
"GCI97",
1515
"GCI103",
16+
"GCI106",
1617
"GCI203",
1718
"GCI404"
1819
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
3+
* Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.greencodeinitiative.creedengo.python.checks;
19+
20+
import org.junit.Test;
21+
import org.sonar.python.checks.utils.PythonCheckVerifier;
22+
23+
public class AvoidSqrtInLoopTest {
24+
@Test
25+
public void test() {
26+
PythonCheckVerifier.verify("src/test/resources/checks/avoidSqrtInLoop.py", new AvoidSqrtInLoop());
27+
}
28+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import math
2+
import numpy as np
3+
4+
results = []
5+
numbers = [1, 2, 3, 4, 5]
6+
for n in numbers:
7+
results.append(math.sqrt(n)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
8+
9+
results = []
10+
for x in [9, 16, 25]:
11+
results.append(np.sqrt(x)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
12+
13+
results = []
14+
i = 0
15+
while i < 3:
16+
results.append(np.sqrt(i)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
17+
i += 1
18+
19+
results = []
20+
for n in range(10):
21+
val = math.sqrt(n) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
22+
results.append(val)
23+
24+
class DataProcessor:
25+
numbers = np.array([1, 4, 9])
26+
sqrt_values = np.sqrt(numbers)
27+
28+
x = math.sqrt(25)
29+
30+
class Analyzer:
31+
def __init__(self, data):
32+
self.results = np.sqrt(np.array(data))
33+
34+
intermediate = []
35+
for n in range(3):
36+
intermediate.append(n)
37+
final = np.sqrt(np.array(intermediate))
38+
39+
results = np.sqrt([x for x in range(5)])
40+
41+
def non_compliant_math_sqrt_in_for_loop():
42+
numbers = [1, 2, 3, 4, 5]
43+
results = []
44+
for num in numbers:
45+
results.append(math.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
46+
return results
47+
48+
def non_compliant_numpy_scalar_sqrt_in_for_loop():
49+
numbers = [1, 2, 3, 4, 5]
50+
results = []
51+
for num in numbers:
52+
results.append(np.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
53+
return results
54+
55+
def non_compliant_math_sqrt_in_while_loop():
56+
numbers = [1, 2, 3, 4, 5]
57+
results = []
58+
i = 0
59+
while i < len(numbers):
60+
results.append(math.sqrt(numbers[i])) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
61+
i += 1
62+
return results
63+
64+
def compliant_numpy_vectorized_sqrt():
65+
numbers = np.array([1, 2, 3, 4, 5])
66+
results = np.sqrt(numbers)
67+
return results
68+
69+
def compliant_list_comprehension_with_vectorized_operation():
70+
numbers = [1, 2, 3, 4, 5]
71+
return np.sqrt(np.array(numbers))
72+
73+
def compliant_math_sqrt_outside_loop():
74+
value = 16
75+
return math.sqrt(value)

0 commit comments

Comments
 (0)