Skip to content

Commit b254927

Browse files
joke1196sonartech
authored andcommitted
SONARPY-4937 Migrate S6890 to TypeV2
GitOrigin-RevId: 7ed12753021cd08b28d6566c9cf6c9a51ed3d574
1 parent 44e1916 commit b254927

4 files changed

Lines changed: 38 additions & 15 deletions

File tree

‎python-checks/src/main/java/org/sonar/python/checks/PytzUsageCheck.java‎

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@
2323
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2424
import org.sonar.plugins.python.api.PythonVersionUtils;
2525
import org.sonar.plugins.python.api.SubscriptionContext;
26-
import org.sonar.plugins.python.api.symbols.Symbol;
2726
import org.sonar.plugins.python.api.tree.AliasedName;
2827
import org.sonar.plugins.python.api.tree.DottedName;
2928
import org.sonar.plugins.python.api.tree.ImportFrom;
3029
import org.sonar.plugins.python.api.tree.ImportName;
3130
import org.sonar.plugins.python.api.tree.Tree;
31+
import org.sonar.plugins.python.api.types.v2.matchers.TypeMatcher;
32+
import org.sonar.plugins.python.api.types.v2.matchers.TypeMatchers;
3233
import org.sonar.python.tree.TreeUtils;
3334

3435
@Rule(key = "S6890")
3536
public class PytzUsageCheck extends PythonSubscriptionCheck {
3637
private static final String MESSAGE = "Don't use `pytz` module with Python 3.9 and later.";
38+
private static final TypeMatcher PYTZ_PREFIX_MATCHER = TypeMatchers.withFQNPrefix("pytz.");
3739

3840
@Override
3941
public void initialize(Context context) {
@@ -50,10 +52,19 @@ private static void checkImport(SubscriptionContext context) {
5052
return;
5153
}
5254

55+
if (context.syntaxNode() instanceof ImportFrom importFrom) {
56+
if (importFrom.dottedPrefixForModule().isEmpty()) {
57+
Optional.ofNullable(importFrom.module())
58+
// V1 associated the imported symbol with the alias rather than the imported name.
59+
// Preserve that behavior: an aliased "from pytz import ..." import is not reported.
60+
.ifPresent(module -> importFrom.importedNames().stream()
61+
.filter(name -> name.alias() == null && (isPytzModule(module) || isPytzImportedName(name)))
62+
.forEach(name -> context.addIssue(module, MESSAGE)));
63+
}
64+
return;
65+
}
66+
5367
Stream.of(
54-
Optional.of(context.syntaxNode())
55-
.flatMap(TreeUtils.toOptionalInstanceOfMapper(ImportFrom.class))
56-
.map(ImportFrom::importedNames),
5768
Optional.of(context.syntaxNode())
5869
.flatMap(TreeUtils.toOptionalInstanceOfMapper(ImportName.class))
5970
.map(ImportName::modules))
@@ -65,18 +76,17 @@ private static void checkImport(SubscriptionContext context) {
6576
.filter(list -> !list.isEmpty())
6677
.map(names -> names.get(0))
6778
.filter(name -> "pytz".equals(name.name())
68-
|| Optional.ofNullable(name.symbol())
69-
.map(Symbol::fullyQualifiedName)
70-
.filter(fqn -> fqn.startsWith("pytz")).isPresent())
71-
.forEach(name -> raiseIssue(context, name));
79+
|| PYTZ_PREFIX_MATCHER.isTrueFor(name, context))
80+
.forEach(name -> context.addIssue(name, MESSAGE));
7281
}
7382

74-
private static void raiseIssue(SubscriptionContext context, Tree tree) {
75-
if (context.syntaxNode().is(Tree.Kind.IMPORT_FROM)) {
76-
ImportFrom importFrom = (ImportFrom) context.syntaxNode();
77-
Optional.ofNullable(importFrom.module()).ifPresent(module -> context.addIssue(module, MESSAGE));
78-
} else {
79-
context.addIssue(tree, MESSAGE);
80-
}
83+
private static boolean isPytzModule(DottedName module) {
84+
return !module.names().isEmpty()
85+
&& "pytz".equals(module.names().get(0).name());
86+
}
87+
88+
private static boolean isPytzImportedName(AliasedName name) {
89+
return !name.dottedName().names().isEmpty()
90+
&& "pytz".equals(name.dottedName().names().get(0).name());
8191
}
8292
}

‎python-checks/src/test/java/org/sonar/python/checks/PytzUsageCheckTest.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,11 @@ void test_explicit_python_3_9() {
5050
ProjectPythonVersion.setCurrentVersions(EnumSet.of(PythonVersionUtils.Version.V_39));
5151
PythonCheckVerifier.verify("src/test/resources/checks/pytzUsage.py", new PytzUsageCheck());
5252
}
53+
54+
@Test
55+
void test_multiple_imports_raise_an_issue_for_each_imported_name() {
56+
assertThat(PythonCheckVerifier.issues("src/test/resources/checks/pytzUsageMultipleImports.py", new PytzUsageCheck()))
57+
.hasSize(2)
58+
.allSatisfy(issue -> assertThat(issue.primaryLocation().startLine()).isEqualTo(1));
59+
}
5360
}

‎python-checks/src/test/resources/checks/pytzUsage.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ def on_import():
55
#^^^^
66
from pytz import timezone # Noncompliant {{Don't use `pytz` module with Python 3.9 and later.}}
77
#^^^^
8+
from pytz import timezone as pytz_timezone
9+
from pytz.tzinfo import BaseTzInfo # Noncompliant {{Don't use `pytz` module with Python 3.9 and later.}}
10+
#^^^^^^^^^^^
11+
from pip._vendor import pytz # Noncompliant {{Don't use `pytz` module with Python 3.9 and later.}}
12+
#^^^^^^^^^^^
813
import pytz as p # Noncompliant {{Don't use `pytz` module with Python 3.9 and later.}}
914
#^^^^
1015
from something.different.pytz import a_function
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from pytz import timezone, utc

0 commit comments

Comments
 (0)