Skip to content

feat(pypi): add pyproject.toml support to IntelliJ plugin#244

Open
ruromero wants to merge 7 commits intoredhat-developer:mainfrom
ruromero:TC-3853
Open

feat(pypi): add pyproject.toml support to IntelliJ plugin#244
ruromero wants to merge 7 commits intoredhat-developer:mainfrom
ruromero:TC-3853

Conversation

@ruromero
Copy link
Copy Markdown
Collaborator

@ruromero ruromero commented Mar 24, 2026

Summary

  • Add component analysis support for pyproject.toml manifest files in the IntelliJ Dependency Analytics plugin
  • Parse PEP 621 [project.dependencies] and Poetry [tool.poetry.dependencies] sections for vulnerability scanning
  • Register new PyprojectCAAnnotator, PyprojectCAInspection, and PyprojectCAIntentionAction for TOML language
  • Update SaAction, SaUtils, CAAnnotator, SAIntentionAction, and ApiService to recognize pyproject.toml
  • Update README.md to document pyproject.toml support including ignore patterns and dev dependency exclusion
  • Add 35 unit tests for PEP 508 utilities, dependency extraction, intention actions, and manifest recognition (TC-4172)

Implements TC-3853

Test plan

  • Verify pyproject.toml appears in supported manifest files list
  • Open a pyproject.toml with PEP 621 dependencies and verify inline diagnostics appear
  • Open a pyproject.toml with Poetry dependencies and verify inline diagnostics appear
  • Verify quick-fix intention action is available for vulnerable dependencies
  • Verify RHDA Report (Stack Analysis) is available via right-click on pyproject.toml
  • Verify trustify-da-ignore comment works to suppress diagnostics
  • Build succeeds with ./gradlew build
  • 35 unit tests pass (./gradlew test --tests "org.jboss.tools.intellij.componentanalysis.pypi.*")

🤖 Generated with Claude Code

Copy link
Copy Markdown
Collaborator Author

@ruromero ruromero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification Report for TC-3853

Check Result Details
Scope Containment WARN All 9 task-listed files present. plugin.xml is out-of-scope per file lists but required by Implementation Notes for annotator/inspection registration
Diff Size PASS 514 additions, 10 deletions, 10 files — proportionate to 3 new files + 7 modifications
Commit Traceability PASS Single commit references TC-3853 in body ("Implements TC-3853"), follows Conventional Commits, includes Assisted-by: Claude Code trailer
Sensitive Patterns PASS No passwords, secrets, API keys, or private keys detected
CI Status WARN build-linux, build-macos, main, SonarCloud pass; verify and build-windows pending
Acceptance Criteria PASS 6 of 6 criteria met (see details below)
Verification Commands N/A No verification commands specified in task

Acceptance Criteria Details

# Criterion Result Evidence
1 Opening pyproject.toml triggers component analysis with inline diagnostics PASS PyprojectCAAnnotator extends CAAnnotator, checks filename, registered in plugin.xml
2 Vulnerable packages in [project.dependencies] are highlighted PASS parsePep621Dependencies() parses PEP 508 strings from project table's dependencies array
3 Vulnerable packages in [tool.poetry.dependencies] are highlighted PASS parsePoetryDependencies() extracts name/version from poetry table key-value pairs
4 Quick-fix intention action available for recommended versions PASS PyprojectCAIntentionAction extends CAIntentionAction, handles both PEP 621 and Poetry formats
5 Stack analysis (RHDA Report) available via right-click PASS SaAction.java includes pyproject.toml in supported filenames; SaUtils.java maps it to python
6 README.md updated to list pyproject.toml as supported PASS Multiple references added: prerequisites, component analysis, exhortignore, dev dependency sections

Overall: WARN

Two CI checks (verify, build-windows) are still pending. One out-of-scope file (plugin.xml) is included but is justified by the Implementation Notes requirement to register the annotator and inspection. All acceptance criteria are satisfied. No blocking issues found.


This comment was AI-generated by sdlc-workflow/verify-pr v0.4.3.

@ruromero
Copy link
Copy Markdown
Collaborator Author

/review

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Mar 24, 2026

PR Reviewer Guide 🔍

(Review updated until commit a1c1727)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

TC-3853 - Partially compliant

Compliant requirements:

  • Add pyproject.toml to supported manifest filename lists across stack analysis and component analysis flows
  • Implement TOML-based component analysis support for pyproject.toml using IntelliJ TOML PSI
  • New PyprojectCAAnnotator extracting dependencies from PEP 621 [project.dependencies], [project.optional-dependencies], and Poetry [tool.poetry.dependencies]
  • New PyprojectCAIntentionAction quick-fix for pyproject.toml
  • New PyprojectCAInspection
  • Register the new annotator + inspection in plugin.xml
  • Update README.md to document pyproject.toml support (ignore patterns and dependency scope notes)
  • ApiService applies Python-specific settings for pyproject.toml alongside requirements.txt

Non-compliant requirements:

  • Add/Update tests for pyproject.toml recognition, parsing, quick-fix availability, and ApiService settings

Requires further human verification:

  • Opening pyproject.toml triggers component analysis with inline diagnostics in IntelliJ
  • Vulnerable packages in PEP 621 and Poetry sections are highlighted correctly
  • Quick-fix intention is offered at the right PSI elements and produces a valid pyproject.toml for both PEP 621 and Poetry formats
  • Stack analysis (RHDA report) action is available via right-click on pyproject.toml and produces a valid report
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Performance

Ignore-handling does multiple full-PSI scans and per-comment full iterations (collectElementsOfType for comments/key-values/literals, then line-matching loops), which can become costly on larger TOML files. Consider building a single line->element index (or using PSI sibling navigation from the comment) to avoid O(n^2) patterns and repeated tree traversals.

private Set<String> getIgnoredDependencies(PsiFile file) {
    Set<String> ignoredDeps = new HashSet<>();
    Collection<PsiComment> comments = PsiTreeUtil.collectElementsOfType(file, PsiComment.class);
    for (PsiComment comment : comments) {
        String commentText = comment.getText();
        if (commentText.contains(TRUSTIFY_DA_IGNORE) || commentText.contains(EXHORT_IGNORE)) {
            String dependencyName = findAssociatedDependency(file, comment);
            if (dependencyName != null) {
                ignoredDeps.add(dependencyName.toLowerCase());
            }
        }
    }
    return ignoredDeps;
}

private String findAssociatedDependency(PsiFile file, PsiComment comment) {
    // Check if comment is on same line as a TomlKeyValue
    TomlKeyValue keyValue = findKeyValueOnSameLine(file, comment);
    if (keyValue != null) {
        String keyName = keyValue.getKey().getText();
        // If in [tool.poetry.dependencies], the key IS the dependency name
        if (isInPoetryDependencies(keyValue)) {
            return normalizeKeyName(keyName);
        }
        // If key is "dependencies" in [project], check if on same line as an array element
        // For PEP 621 array entries, the ignore comment is typically on the same line as the string literal
    }

    // Check array elements — PEP 621 format where comment is on the same line as a literal in the array
    TomlLiteral literal = findLiteralOnSameLine(file, comment);
    if (literal != null) {
        String text = unquote(literal.getText());
        String name = extractPep508Name(text);
        if (name != null) {
            return name;
        }
    }

    return null;
}

private boolean isInPoetryDependencies(TomlKeyValue keyValue) {
    TomlTable parentTable = PsiTreeUtil.getParentOfType(keyValue, TomlTable.class);
    if (parentTable != null) {
        String tablePath = getTablePath(parentTable.getHeader());
        return "tool.poetry.dependencies".equals(tablePath);
    }
    return false;
}

private TomlKeyValue findKeyValueOnSameLine(PsiFile file, PsiComment comment) {
    int commentLine = getLineNumber(file, comment);
    Collection<TomlKeyValue> allKeyValues = PsiTreeUtil.collectElementsOfType(file, TomlKeyValue.class);
    for (TomlKeyValue keyValue : allKeyValues) {
        int keyValueLine = getLineNumber(file, keyValue);
        if (commentLine == keyValueLine) {
            return keyValue;
        }
    }
    return null;
}

private TomlLiteral findLiteralOnSameLine(PsiFile file, PsiComment comment) {
    int commentLine = getLineNumber(file, comment);
    Collection<TomlLiteral> literals = PsiTreeUtil.collectElementsOfType(file, TomlLiteral.class);
    for (TomlLiteral literal : literals) {
        int literalLine = getLineNumber(file, literal);
        if (commentLine == literalLine) {
            return literal;
        }
    }
    return null;
}
Behavior Change

The quick-fix rewrites Poetry version constraints to an exact pin ("==<version>"), potentially changing semantics from caret/tilde/range constraints and breaking user intent. Confirm this is desired for Poetry workflows, and consider preserving the original operator (or using Poetry-appropriate constraint updates) if not.

protected void updateVersion(@NotNull Project project, Editor editor, PsiFile file, String version) {
    if (version == null) {
        return;
    }

    if (element instanceof TomlLiteral literal) {
        // PEP 621: string literal in dependencies array like "anyio==3.6.2"
        String depString = PyprojectCAAnnotator.unquote(literal.getText());
        String name = PyprojectCAAnnotator.extractPep508Name(depString);
        if (name != null) {
            String extras = PyprojectCAAnnotator.extractPep508Extras(depString);
            String markers = PyprojectCAAnnotator.extractPep508Markers(depString);
            StringBuilder newDep = new StringBuilder(name);
            if (extras != null) {
                newDep.append(extras);
            }
            newDep.append("==").append(version);
            if (markers != null) {
                newDep.append(" ").append(markers);
            }
            replaceVersionLiteral(project, file, literal, newDep.toString());
        }
    } else if (element instanceof TomlKeyValue keyValue) {
        // Poetry: key-value pair like anyio = "^3.6.2"
        TomlLiteral valueLiteral = findVersionLiteral(keyValue);
        if (valueLiteral != null) {
            replaceVersionLiteral(project, file, valueLiteral, "==" + version);
        }
    }
Annotator Scope

Both Cargo and pyproject annotators are registered for language="TOML". While each annotator now guards by filename, IntelliJ will still invoke them for every TOML file, which may add overhead. Consider narrowing by pattern/scope if supported, or ensuring getDependencies remains minimal/fast for non-target TOML files.

<!-- Cargo.toml support for Rust projects (requires TOML bundle plugin) -->
<localInspection language="TOML" shortName="CargoCAInspection"
                 displayName="Red Hat Dependency Analytics component analysis"
                 groupName="Rust" enabledByDefault="true" level="ERROR"
                 implementationClass="org.jboss.tools.intellij.componentanalysis.cargo.CargoCAInspection"/>

<externalAnnotator language="TOML"
                   implementationClass="org.jboss.tools.intellij.componentanalysis.cargo.CargoCAAnnotator"/>

<!-- pyproject.toml support for Python projects (requires TOML bundle plugin) -->
<localInspection language="TOML" shortName="PyprojectCAInspection"
                 displayName="Red Hat Dependency Analytics component analysis"
                 groupName="Python" enabledByDefault="true" level="ERROR"
                 implementationClass="org.jboss.tools.intellij.componentanalysis.pypi.PyprojectCAInspection"/>

<externalAnnotator language="TOML"
                   implementationClass="org.jboss.tools.intellij.componentanalysis.pypi.PyprojectCAAnnotator"/>

@ruromero
Copy link
Copy Markdown
Collaborator Author

/review

@qodo-code-review
Copy link
Copy Markdown

Persistent review updated to latest commit d420e28

@ruromero ruromero marked this pull request as draft March 26, 2026 14:35
@ruromero ruromero requested a review from soul2zimate March 26, 2026 14:35
@ruromero
Copy link
Copy Markdown
Collaborator Author

@soul2zimate it is missing the license support introduced here #243

The license quick fix for pyproject.toml files can be introduced after your PR is merged.

@soul2zimate
Copy link
Copy Markdown
Contributor

@ruromero roger! Let's hold any feature PR for the moment, I just want to include bug fix PR to address issue reported by QE at this stage.

Copy link
Copy Markdown
Contributor

@soul2zimate soul2zimate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In plugin.xml, there is already localInspection and externalAnnotator for TOML which supports Cargo.toml. This PR defines again for pyproject.toml. Does it cause conflict and false detection?

Comment thread README.md
@ruromero
Copy link
Copy Markdown
Collaborator Author

In plugin.xml, there is already localInspection and externalAnnotator for TOML which supports Cargo.toml. This PR defines again for pyproject.toml. Does it cause conflict and false detection?

The annotators target different files so there is no conflict

@soul2zimate
Copy link
Copy Markdown
Contributor

In plugin.xml, there is already localInspection and externalAnnotator for TOML which supports Cargo.toml. This PR defines again for pyproject.toml. Does it cause conflict and false detection?

The annotators target different files so there is no conflict

But IntelliJ still invokes both PyprojectCAAnnotator and CargoCAAnnotator on every .toml file. The language="TOML" registration means IntelliJ doesn't know they target different files. It calls both, and each one has to run far enough to determine the file is irrelevant and bail out.

I suggest to add a filename guard for CargoCAAnnotator as same as you did for PyprojectCAAnnotator

    @Override
    protected Map<Dependency, List<PsiElement>> getDependencies(PsiFile file) {
        if (!"pyproject.toml".equals(file.getName())) {
            return Map.of();
        }

@ruromero
Copy link
Copy Markdown
Collaborator Author

ruromero commented Apr 1, 2026

I suggest to add a filename guard for CargoCAAnnotator as same as you did for PyprojectCAAnnotator

Done. Thanks for the suggestion.

@ruromero ruromero marked this pull request as ready for review April 14, 2026 15:46
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Add pyproject.toml support for Python dependency analysis in IntelliJ plugin

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add pyproject.toml support for Python dependency analysis
  - Parse PEP 621 [project.dependencies] and [project.optional-dependencies]
  - Parse Poetry [tool.poetry.dependencies] format
• Create PyprojectCAAnnotator, PyprojectCAInspection, and PyprojectCAIntentionAction classes
• Update existing classes to recognize pyproject.toml as supported manifest
• Add Cargo.toml validation and pyproject.toml documentation
Diagram
flowchart LR
  A["pyproject.toml"] -->|"Parse PEP 621 & Poetry"| B["PyprojectCAAnnotator"]
  B -->|"Extract dependencies"| C["Dependency Map"]
  C -->|"Highlight vulnerabilities"| D["PyprojectCAInspection"]
  D -->|"Quick fix available"| E["PyprojectCAIntentionAction"]
  F["SaAction/SaUtils"] -->|"Register manifest"| A
  G["ApiService"] -->|"Apply Python settings"| A
  H["CAAnnotator/SAIntentionAction"] -->|"Map to python"| A
Loading

Grey Divider

File Changes

1. src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java ✨ Enhancement +399/-0

New annotator for pyproject.toml dependency analysis

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java


2. src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAInspection.java ✨ Enhancement +39/-0

New inspection for pyproject.toml vulnerabilities

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAInspection.java


3. src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java ✨ Enhancement +117/-0

New quick-fix action for pyproject.toml dependencies

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java


View more (8)
4. src/main/java/org/jboss/tools/intellij/componentanalysis/CAAnnotator.java ✨ Enhancement +1/-1

Map pyproject.toml to python package manager

src/main/java/org/jboss/tools/intellij/componentanalysis/CAAnnotator.java


5. src/main/java/org/jboss/tools/intellij/componentanalysis/SAIntentionAction.java ✨ Enhancement +2/-1

Add pyproject.toml availability check

src/main/java/org/jboss/tools/intellij/componentanalysis/SAIntentionAction.java


6. src/main/java/org/jboss/tools/intellij/componentanalysis/cargo/CargoCAAnnotator.java 🐞 Bug fix +5/-1

Add Cargo.toml validation and newline fix

src/main/java/org/jboss/tools/intellij/componentanalysis/cargo/CargoCAAnnotator.java


7. src/main/java/org/jboss/tools/intellij/exhort/ApiService.java ✨ Enhancement +3/-3

Apply Python settings for pyproject.toml analysis

src/main/java/org/jboss/tools/intellij/exhort/ApiService.java


8. src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java ✨ Enhancement +2/-1

Register pyproject.toml as supported manifest

src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java


9. src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java ✨ Enhancement +3/-2

Map pyproject.toml to python package manager

src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java


10. src/main/resources/META-INF/plugin.xml ⚙️ Configuration changes +9/-0

Register PyprojectCAAnnotator and PyprojectCAInspection

src/main/resources/META-INF/plugin.xml


11. README.md 📝 Documentation +21/-2

Document pyproject.toml support and ignore patterns

README.md


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 14, 2026

Code Review by Qodo

🐞 Bugs (3)   📘 Rule violations (0)   📎 Requirement gaps (4)
🐞\ ≡ Correctness (2) ⚙ Maintainability (1)
📎\ ☼ Reliability (4)

Grey Divider


Action required

1. Missing pyproject.toml recognition tests 📎
Description
The PR updates SaAction/SaUtils to recognize pyproject.toml, but no automated tests were
added/updated to verify this manifest gating behavior. This violates the checklist requirement to
add tests for pyproject.toml recognition in these entry points.
Code

src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java[R40-44]

            "requirements.txt",
            "build.gradle",
            "build.gradle.kts",
-            "Cargo.toml"
+            "Cargo.toml",
+            "pyproject.toml"
Evidence
PR Compliance ID 1 requires automated tests verifying pyproject.toml recognition in SaAction and
SaUtils. The diff shows new pyproject.toml support added to both code paths, but no
corresponding test changes/additions are present in the PR.

pyproject.toml is recognized as a supported manifest across plugin entry points
src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java[40-44]
src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[27-31]
src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[61-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pyproject.toml` support was added to stack-analysis manifest gating, but the PR does not include automated tests to verify recognition in `SaAction` and `SaUtils` as required.

## Issue Context
The compliance checklist requires tests to prevent regressions where UI actions/SA routing stop triggering for `pyproject.toml`.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java[40-44]
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[27-31]
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[61-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Pyproject dependency parsing untested 📎
Description
The PR adds new dependency extraction logic for PEP 621 [project.dependencies] and Poetry
[tool.poetry.dependencies], but no automated tests were added to validate correct extraction. This
violates the checklist requirements for tested parsing of both formats.
Code

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[R116-187]

+    private void parsePep621Dependencies(TomlTable projectTable, Set<String> ignoredDeps,
+                                          Map<Dependency, List<PsiElement>> resultMap) {
+        List<TomlKeyValue> keyValues = PsiTreeUtil.getChildrenOfTypeAsList(projectTable, TomlKeyValue.class);
+        for (TomlKeyValue kv : keyValues) {
+            if (!"dependencies".equals(kv.getKey().getText())) {
+                continue;
+            }
+            TomlValue value = kv.getValue();
+            if (!(value instanceof TomlArray array)) {
+                continue;
+            }
+            for (TomlValue element : array.getElements()) {
+                if (!(element instanceof TomlLiteral literal)) {
+                    continue;
+                }
+                String depString = unquote(literal.getText());
+                String name = extractPep508Name(depString);
+                if (name == null || ignoredDeps.contains(name.toLowerCase())) {
+                    continue;
+                }
+                String version = extractPep508Version(depString);
+                Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
+                resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(literal);
+            }
+        }
+    }
+
+    /**
+     * Parses PEP 621 [project.optional-dependencies] table.
+     * Format: dev = ["pytest>=7.0", "black"], security = ["certifi>=2023.7"]
+     */
+    private void parseOptionalDependencies(TomlTable optDepsTable, Set<String> ignoredDeps,
+                                            Map<Dependency, List<PsiElement>> resultMap) {
+        List<TomlKeyValue> groups = PsiTreeUtil.getChildrenOfTypeAsList(optDepsTable, TomlKeyValue.class);
+        for (TomlKeyValue group : groups) {
+            TomlValue value = group.getValue();
+            if (!(value instanceof TomlArray array)) {
+                continue;
+            }
+            for (TomlValue element : array.getElements()) {
+                if (!(element instanceof TomlLiteral literal)) {
+                    continue;
+                }
+                String depString = unquote(literal.getText());
+                String name = extractPep508Name(depString);
+                if (name == null || ignoredDeps.contains(name.toLowerCase())) {
+                    continue;
+                }
+                String version = extractPep508Version(depString);
+                Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
+                resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(literal);
+            }
+        }
+    }
+
+    /**
+     * Parses Poetry [tool.poetry.dependencies] table.
+     * Format: anyio = "^3.6.2" or anyio = {version = "^3.6.2", optional = true}
+     */
+    private void parsePoetryDependencies(TomlTable poetryTable, Set<String> ignoredDeps,
+                                          Map<Dependency, List<PsiElement>> resultMap) {
+        List<TomlKeyValue> keyValues = PsiTreeUtil.getChildrenOfTypeAsList(poetryTable, TomlKeyValue.class);
+        for (TomlKeyValue kv : keyValues) {
+            String name = normalizeKeyName(kv.getKey().getText());
+            if ("python".equalsIgnoreCase(name) || ignoredDeps.contains(name.toLowerCase())) {
+                continue;
+            }
+            String version = extractPoetryVersion(kv.getValue());
+            Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
+            resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(kv);
+        }
+    }
Evidence
PR Compliance IDs 4 and 5 require automated tests that cover dependency extraction for both PEP 621
arrays and Poetry tables. The PR introduces the parsing implementations but does not add/modify any
tests to assert correct extraction behavior.

Dependency extraction from PEP 621 [project.dependencies] is implemented and tested
Dependency extraction from Poetry [tool.poetry.dependencies] is implemented and tested
src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[116-187]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Dependency extraction for `pyproject.toml` (PEP 621 and Poetry) is implemented but lacks automated tests.

## Issue Context
The checklist requires tests verifying that `[project.dependencies]` (PEP 508 strings) and `[tool.poetry.dependencies]` (name->version mappings) are correctly extracted from TOML PSI.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[116-187]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Pyproject quick-fix untested 📎
Description
A new PyprojectCAIntentionAction is introduced to apply recommended versions, but no automated
tests were added to verify its availability and correct version application for pyproject.toml.
This violates the checklist requirement for intention action tests.
Code

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java[R37-66]

+    @Override
+    protected void updateVersion(@NotNull Project project, Editor editor, PsiFile file, String version) {
+        if (version == null) {
+            return;
+        }
+
+        if (element instanceof TomlLiteral literal) {
+            // PEP 621: string literal in dependencies array like "anyio==3.6.2"
+            String depString = PyprojectCAAnnotator.unquote(literal.getText());
+            String name = PyprojectCAAnnotator.extractPep508Name(depString);
+            if (name != null) {
+                String extras = PyprojectCAAnnotator.extractPep508Extras(depString);
+                String markers = PyprojectCAAnnotator.extractPep508Markers(depString);
+                StringBuilder newDep = new StringBuilder(name);
+                if (extras != null) {
+                    newDep.append(extras);
+                }
+                newDep.append("==").append(version);
+                if (markers != null) {
+                    newDep.append(" ").append(markers);
+                }
+                replaceVersionLiteral(project, file, literal, newDep.toString());
+            }
+        } else if (element instanceof TomlKeyValue keyValue) {
+            // Poetry: key-value pair like anyio = "^3.6.2"
+            TomlLiteral valueLiteral = findVersionLiteral(keyValue);
+            if (valueLiteral != null) {
+                replaceVersionLiteral(project, file, valueLiteral, "==" + version);
+            }
+        }
Evidence
PR Compliance ID 7 requires automated tests confirming the intention action is available for
pyproject.toml and applies recommended versions. The PR adds the new intention action and wires it
from the annotator, but no tests were added/updated to cover this behavior.

Quick-fix intention action is available for pyproject.toml and can apply recommended versions
src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[93-96]
src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java[37-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PyprojectCAIntentionAction` was added but lacks tests verifying (1) it is offered for `pyproject.toml` and (2) it correctly updates versions for both PEP 621 literals and Poetry key/value entries.

## Issue Context
The repo already uses IntelliJ `BasePlatformTestCase` fixtures for intention-action tests (e.g., cargo). Add analogous tests for pyproject.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[93-96]
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java[37-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. ApiService pyproject tests missing 📎
Description
ApiService is updated to treat pyproject.toml like requirements.txt for component analysis
submission and Python-specific request properties, but no automated tests were added to verify these
settings are applied. This violates the checklist requirement for ApiService tests covering
Python-specific behavior for pyproject.toml.
Code

src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[R259-268]

+        if ("requirements.txt".equals(manifestName) || "pyproject.toml".equals(manifestName)) {
            if (settings.pythonMatchManifestVersions) {
                System.setProperty("MATCH_MANIFEST_VERSIONS", "true");
            } else {
                System.setProperty("MATCH_MANIFEST_VERSIONS","false");
            }
        }
-        if (!"go.mod".equals(manifestName) && !"requirements.txt".equals(manifestName)) {
+        if (!"go.mod".equals(manifestName) && !"requirements.txt".equals(manifestName) && !"pyproject.toml".equals(manifestName)) {
            System.clearProperty("MATCH_MANIFEST_VERSIONS");
        }
Evidence
PR Compliance ID 9 requires automated tests verifying ApiService applies Python-specific settings
for pyproject.toml the same as requirements.txt. The diff adds pyproject.toml to the relevant
manifest checks, but no tests were added/updated to assert the resulting system-property behavior.

ApiService applies Python-specific settings for pyproject.toml the same as requirements.txt
src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[101-106]
src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[259-268]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ApiService` now includes `pyproject.toml` in Python-specific manifest handling, but there are no automated tests to confirm the correct request properties/system properties are set/cleared.

## Issue Context
Checklist requires parity with `requirements.txt` behavior (e.g., `MATCH_MANIFEST_VERSIONS` and Python virtualenv-related settings handling) for `pyproject.toml`.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[101-106]
- src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[259-268]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Poetry ignore comment misbind 🐞
Description
PyprojectCAAnnotator.findAssociatedDependency() can associate a trustify-da-ignore comment on a
Poetry inline-table dependency line with an inner key (e.g., version) instead of the dependency
name, so the dependency is not actually ignored and diagnostics keep appearing.
Code

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[R218-263]

+    private String findAssociatedDependency(PsiFile file, PsiComment comment) {
+        // Check if comment is on same line as a TomlKeyValue
+        TomlKeyValue keyValue = findKeyValueOnSameLine(file, comment);
+        if (keyValue != null) {
+            String keyName = keyValue.getKey().getText();
+            // If in [tool.poetry.dependencies], the key IS the dependency name
+            if (isInPoetryDependencies(keyValue)) {
+                return normalizeKeyName(keyName);
+            }
+            // If key is "dependencies" in [project], check if on same line as an array element
+            // For PEP 621 array entries, the ignore comment is typically on the same line as the string literal
+        }
+
+        // Check array elements — PEP 621 format where comment is on the same line as a literal in the array
+        TomlLiteral literal = findLiteralOnSameLine(file, comment);
+        if (literal != null) {
+            String text = unquote(literal.getText());
+            String name = extractPep508Name(text);
+            if (name != null) {
+                return name;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean isInPoetryDependencies(TomlKeyValue keyValue) {
+        TomlTable parentTable = PsiTreeUtil.getParentOfType(keyValue, TomlTable.class);
+        if (parentTable != null) {
+            String tablePath = getTablePath(parentTable.getHeader());
+            return "tool.poetry.dependencies".equals(tablePath);
+        }
+        return false;
+    }
+
+    private TomlKeyValue findKeyValueOnSameLine(PsiFile file, PsiComment comment) {
+        int commentLine = getLineNumber(file, comment);
+        Collection<TomlKeyValue> allKeyValues = PsiTreeUtil.collectElementsOfType(file, TomlKeyValue.class);
+        for (TomlKeyValue keyValue : allKeyValues) {
+            int keyValueLine = getLineNumber(file, keyValue);
+            if (commentLine == keyValueLine) {
+                return keyValue;
+            }
+        }
+        return null;
+    }
Evidence
Pyproject parsing explicitly supports Poetry inline-table dependencies, which introduce multiple
TomlKeyValue nodes on the same line (outer dependency entry plus inner version=..., etc.). The
ignore resolver returns the first TomlKeyValue found on that line without restricting to the
top-level dependency entry, then uses that key text as the dependency name for ignore matching.

src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[171-199]
src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[218-263]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`trustify-da-ignore` lookup in `PyprojectCAAnnotator` can return the wrong TOML key on Poetry inline-table dependency lines because it scans all `TomlKeyValue` elements on the same line and returns the first match.

### Issue Context
Poetry inline-table deps (supported by this PR) place nested key/values (`version`, `optional`, etc.) on the same line as the outer dependency key, so the ignore comment must be associated with the *outer* dependency key, not inner entries.

### Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[218-263]

### Suggested implementation direction
- In `findAssociatedDependency`, when handling Poetry, prefer the `TomlKeyValue` whose **direct parent is the `TomlTable` entry list** (i.e., the dependency entry), not a nested inline-table key.
- Alternatively, derive the associated dependency by walking PSI from the comment to the nearest enclosing/top-level `TomlKeyValue` on that line, then verify it’s in `[tool.poetry.dependencies]` before returning its key name.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. Pyproject exclude fix missing 🐞
Description
ExcludeManifestIntentionAction does not consider pyproject.toml a supported manifest, so the
“Exclude this manifest from component analysis” quick-fix won’t be offered for pyproject.toml even
after adding pyproject support.
Code

src/main/resources/META-INF/plugin.xml[R578-585]

+        <!-- pyproject.toml support for Python projects (requires TOML bundle plugin) -->
+        <localInspection language="TOML" shortName="PyprojectCAInspection"
+                         displayName="Red Hat Dependency Analytics component analysis"
+                         groupName="Python" enabledByDefault="true" level="ERROR"
+                         implementationClass="org.jboss.tools.intellij.componentanalysis.pypi.PyprojectCAInspection"/>
+
+        <externalAnnotator language="TOML"
+                           implementationClass="org.jboss.tools.intellij.componentanalysis.pypi.PyprojectCAAnnotator"/>
Evidence
Component-analysis annotations always register ExcludeManifestIntentionAction, but that
intention’s isAvailable() hard-codes a manifest allowlist that omits pyproject.toml, so IntelliJ
will hide the action for pyproject files.

src/main/java/org/jboss/tools/intellij/componentanalysis/CAAnnotator.java[170-190]
src/main/java/org/jboss/tools/intellij/componentanalysis/ExcludeManifestIntentionAction.java[41-54]
src/main/resources/META-INF/plugin.xml[574-586]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ExcludeManifestIntentionAction.isAvailable()` does not recognize `pyproject.toml`, preventing users from excluding pyproject manifests via the provided quick-fix.

### Issue Context
With this PR, `pyproject.toml` gains component-analysis diagnostics. The annotator always attaches the exclude-manifest intention, but its availability filter blocks it for pyproject.

### Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/ExcludeManifestIntentionAction.java[41-54]

### Suggested implementation direction
- Add `|| "pyproject.toml".equals(fileName)` to the allowlist in `isAvailable()` (keeping behavior consistent with other supported manifests).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

7. Docs contradict optional deps 🐞
Description
README states dev/test dependencies are ignored, but also states [project.optional-dependencies]
are analyzed; since optional dependency groups commonly include dev/test extras, the current
behavior/documentation is ambiguous and should be clarified (or filtered).
Code

README.md[R336-338]

- **Excluding developmental or test dependencies**
  <br >Red Hat Dependency Analytics does not analyze dependencies marked as `dev` or `test`, these dependencies are
  ignored.
Evidence
The README first claims dev/test dependencies are ignored, then immediately states that pyproject
optional-dependencies are analyzed, which can include dev/test groups; this creates unclear
expectations for users configuring pyproject dependencies.

README.md[336-386]
src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[143-169]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
README guidance is internally inconsistent about whether dev/test dependencies are ignored when using `pyproject.toml`, while the implementation analyzes all `[project.optional-dependencies]` groups.

### Issue Context
Optional dependency groups are frequently used for dev/test extras (by convention), so users may reasonably expect them to be excluded based on the earlier README statement.

### Fix Focus Areas
- README.md[336-386]

### Suggested implementation direction
- Either update documentation to explicitly state that PEP 621 optional-dependencies are analyzed even if the group is named `dev`/`test`, or
- Change implementation to exclude optional-dependency groups named `dev`/`test` (and then document that behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines 40 to +44
"requirements.txt",
"build.gradle",
"build.gradle.kts",
"Cargo.toml"
"Cargo.toml",
"pyproject.toml"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Missing pyproject.toml recognition tests 📎 Requirement gap ☼ Reliability

The PR updates SaAction/SaUtils to recognize pyproject.toml, but no automated tests were
added/updated to verify this manifest gating behavior. This violates the checklist requirement to
add tests for pyproject.toml recognition in these entry points.
Agent Prompt
## Issue description
`pyproject.toml` support was added to stack-analysis manifest gating, but the PR does not include automated tests to verify recognition in `SaAction` and `SaUtils` as required.

## Issue Context
The compliance checklist requires tests to prevent regressions where UI actions/SA routing stop triggering for `pyproject.toml`.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaAction.java[40-44]
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[27-31]
- src/main/java/org/jboss/tools/intellij/stackanalysis/SaUtils.java[61-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sdlc-workflow/verify-pr] Classified as code change request — the Jira task Test Requirements explicitly require "Test that pyproject.toml is recognized as a supported manifest in SaAction and SaUtils." Investigation confirms no tests exist for SaUtils/SaAction (pre-existing gap across all ecosystems). Combined sub-task created for all missing pyproject.toml tests.

Comment on lines +116 to +187
private void parsePep621Dependencies(TomlTable projectTable, Set<String> ignoredDeps,
Map<Dependency, List<PsiElement>> resultMap) {
List<TomlKeyValue> keyValues = PsiTreeUtil.getChildrenOfTypeAsList(projectTable, TomlKeyValue.class);
for (TomlKeyValue kv : keyValues) {
if (!"dependencies".equals(kv.getKey().getText())) {
continue;
}
TomlValue value = kv.getValue();
if (!(value instanceof TomlArray array)) {
continue;
}
for (TomlValue element : array.getElements()) {
if (!(element instanceof TomlLiteral literal)) {
continue;
}
String depString = unquote(literal.getText());
String name = extractPep508Name(depString);
if (name == null || ignoredDeps.contains(name.toLowerCase())) {
continue;
}
String version = extractPep508Version(depString);
Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(literal);
}
}
}

/**
* Parses PEP 621 [project.optional-dependencies] table.
* Format: dev = ["pytest>=7.0", "black"], security = ["certifi>=2023.7"]
*/
private void parseOptionalDependencies(TomlTable optDepsTable, Set<String> ignoredDeps,
Map<Dependency, List<PsiElement>> resultMap) {
List<TomlKeyValue> groups = PsiTreeUtil.getChildrenOfTypeAsList(optDepsTable, TomlKeyValue.class);
for (TomlKeyValue group : groups) {
TomlValue value = group.getValue();
if (!(value instanceof TomlArray array)) {
continue;
}
for (TomlValue element : array.getElements()) {
if (!(element instanceof TomlLiteral literal)) {
continue;
}
String depString = unquote(literal.getText());
String name = extractPep508Name(depString);
if (name == null || ignoredDeps.contains(name.toLowerCase())) {
continue;
}
String version = extractPep508Version(depString);
Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(literal);
}
}
}

/**
* Parses Poetry [tool.poetry.dependencies] table.
* Format: anyio = "^3.6.2" or anyio = {version = "^3.6.2", optional = true}
*/
private void parsePoetryDependencies(TomlTable poetryTable, Set<String> ignoredDeps,
Map<Dependency, List<PsiElement>> resultMap) {
List<TomlKeyValue> keyValues = PsiTreeUtil.getChildrenOfTypeAsList(poetryTable, TomlKeyValue.class);
for (TomlKeyValue kv : keyValues) {
String name = normalizeKeyName(kv.getKey().getText());
if ("python".equalsIgnoreCase(name) || ignoredDeps.contains(name.toLowerCase())) {
continue;
}
String version = extractPoetryVersion(kv.getValue());
Dependency dp = new Dependency(PYPI, null, name.toLowerCase(), version);
resultMap.computeIfAbsent(dp, k -> new LinkedList<>()).add(kv);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Pyproject dependency parsing untested 📎 Requirement gap ☼ Reliability

The PR adds new dependency extraction logic for PEP 621 [project.dependencies] and Poetry
[tool.poetry.dependencies], but no automated tests were added to validate correct extraction. This
violates the checklist requirements for tested parsing of both formats.
Agent Prompt
## Issue description
Dependency extraction for `pyproject.toml` (PEP 621 and Poetry) is implemented but lacks automated tests.

## Issue Context
The checklist requires tests verifying that `[project.dependencies]` (PEP 508 strings) and `[tool.poetry.dependencies]` (name->version mappings) are correctly extracted from TOML PSI.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[116-187]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sdlc-workflow/verify-pr] Classified as code change request — the Jira task Test Requirements explicitly require "Test that PyprojectCAAnnotator extracts dependencies from PEP 621 format" and Poetry format. No tests exist. Combined sub-task created for all missing pyproject.toml tests.

Comment on lines +37 to +66
@Override
protected void updateVersion(@NotNull Project project, Editor editor, PsiFile file, String version) {
if (version == null) {
return;
}

if (element instanceof TomlLiteral literal) {
// PEP 621: string literal in dependencies array like "anyio==3.6.2"
String depString = PyprojectCAAnnotator.unquote(literal.getText());
String name = PyprojectCAAnnotator.extractPep508Name(depString);
if (name != null) {
String extras = PyprojectCAAnnotator.extractPep508Extras(depString);
String markers = PyprojectCAAnnotator.extractPep508Markers(depString);
StringBuilder newDep = new StringBuilder(name);
if (extras != null) {
newDep.append(extras);
}
newDep.append("==").append(version);
if (markers != null) {
newDep.append(" ").append(markers);
}
replaceVersionLiteral(project, file, literal, newDep.toString());
}
} else if (element instanceof TomlKeyValue keyValue) {
// Poetry: key-value pair like anyio = "^3.6.2"
TomlLiteral valueLiteral = findVersionLiteral(keyValue);
if (valueLiteral != null) {
replaceVersionLiteral(project, file, valueLiteral, "==" + version);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Pyproject quick-fix untested 📎 Requirement gap ☼ Reliability

A new PyprojectCAIntentionAction is introduced to apply recommended versions, but no automated
tests were added to verify its availability and correct version application for pyproject.toml.
This violates the checklist requirement for intention action tests.
Agent Prompt
## Issue description
`PyprojectCAIntentionAction` was added but lacks tests verifying (1) it is offered for `pyproject.toml` and (2) it correctly updates versions for both PEP 621 literals and Poetry key/value entries.

## Issue Context
The repo already uses IntelliJ `BasePlatformTestCase` fixtures for intention-action tests (e.g., cargo). Add analogous tests for pyproject.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[93-96]
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAIntentionAction.java[37-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sdlc-workflow/verify-pr] Classified as code change request — the Jira task Test Requirements explicitly require "Test that PyprojectCAIntentionAction is available for pyproject.toml files." No tests exist. Combined sub-task created for all missing pyproject.toml tests.

Comment on lines +259 to 268
if ("requirements.txt".equals(manifestName) || "pyproject.toml".equals(manifestName)) {
if (settings.pythonMatchManifestVersions) {
System.setProperty("MATCH_MANIFEST_VERSIONS", "true");
} else {
System.setProperty("MATCH_MANIFEST_VERSIONS","false");
}
}
if (!"go.mod".equals(manifestName) && !"requirements.txt".equals(manifestName)) {
if (!"go.mod".equals(manifestName) && !"requirements.txt".equals(manifestName) && !"pyproject.toml".equals(manifestName)) {
System.clearProperty("MATCH_MANIFEST_VERSIONS");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Apiservice pyproject tests missing 📎 Requirement gap ☼ Reliability

ApiService is updated to treat pyproject.toml like requirements.txt for component analysis
submission and Python-specific request properties, but no automated tests were added to verify these
settings are applied. This violates the checklist requirement for ApiService tests covering
Python-specific behavior for pyproject.toml.
Agent Prompt
## Issue description
`ApiService` now includes `pyproject.toml` in Python-specific manifest handling, but there are no automated tests to confirm the correct request properties/system properties are set/cleared.

## Issue Context
Checklist requires parity with `requirements.txt` behavior (e.g., `MATCH_MANIFEST_VERSIONS` and Python virtualenv-related settings handling) for `pyproject.toml`.

## Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[101-106]
- src/main/java/org/jboss/tools/intellij/exhort/ApiService.java[259-268]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sdlc-workflow/verify-pr] Classified as code change request — the Jira task Test Requirements explicitly require "Test that ApiService applies Python-specific settings for pyproject.toml." No tests exist. Combined sub-task created for all missing pyproject.toml tests.

Comment on lines +218 to +263
private String findAssociatedDependency(PsiFile file, PsiComment comment) {
// Check if comment is on same line as a TomlKeyValue
TomlKeyValue keyValue = findKeyValueOnSameLine(file, comment);
if (keyValue != null) {
String keyName = keyValue.getKey().getText();
// If in [tool.poetry.dependencies], the key IS the dependency name
if (isInPoetryDependencies(keyValue)) {
return normalizeKeyName(keyName);
}
// If key is "dependencies" in [project], check if on same line as an array element
// For PEP 621 array entries, the ignore comment is typically on the same line as the string literal
}

// Check array elements — PEP 621 format where comment is on the same line as a literal in the array
TomlLiteral literal = findLiteralOnSameLine(file, comment);
if (literal != null) {
String text = unquote(literal.getText());
String name = extractPep508Name(text);
if (name != null) {
return name;
}
}

return null;
}

private boolean isInPoetryDependencies(TomlKeyValue keyValue) {
TomlTable parentTable = PsiTreeUtil.getParentOfType(keyValue, TomlTable.class);
if (parentTable != null) {
String tablePath = getTablePath(parentTable.getHeader());
return "tool.poetry.dependencies".equals(tablePath);
}
return false;
}

private TomlKeyValue findKeyValueOnSameLine(PsiFile file, PsiComment comment) {
int commentLine = getLineNumber(file, comment);
Collection<TomlKeyValue> allKeyValues = PsiTreeUtil.collectElementsOfType(file, TomlKeyValue.class);
for (TomlKeyValue keyValue : allKeyValues) {
int keyValueLine = getLineNumber(file, keyValue);
if (commentLine == keyValueLine) {
return keyValue;
}
}
return null;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Poetry ignore comment misbind 🐞 Bug ≡ Correctness

PyprojectCAAnnotator.findAssociatedDependency() can associate a trustify-da-ignore comment on a
Poetry inline-table dependency line with an inner key (e.g., version) instead of the dependency
name, so the dependency is not actually ignored and diagnostics keep appearing.
Agent Prompt
### Issue description
`trustify-da-ignore` lookup in `PyprojectCAAnnotator` can return the wrong TOML key on Poetry inline-table dependency lines because it scans all `TomlKeyValue` elements on the same line and returns the first match.

### Issue Context
Poetry inline-table deps (supported by this PR) place nested key/values (`version`, `optional`, etc.) on the same line as the outer dependency key, so the ignore comment must be associated with the *outer* dependency key, not inner entries.

### Fix Focus Areas
- src/main/java/org/jboss/tools/intellij/componentanalysis/pypi/PyprojectCAAnnotator.java[218-263]

### Suggested implementation direction
- In `findAssociatedDependency`, when handling Poetry, prefer the `TomlKeyValue` whose **direct parent is the `TomlTable` entry list** (i.e., the dependency entry), not a nested inline-table key.
- Alternatively, derive the associated dependency by walking PSI from the comment to the nearest enclosing/top-level `TomlKeyValue` on that line, then verify it’s in `[tool.poetry.dependencies]` before returning its key name.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sdlc-workflow/verify-pr] Classified as code change request — investigation confirms a variant of this bug exists for multi-line Poetry inline-table dependencies where the trustify-da-ignore comment is placed on an inner key-value line. The single-line case works correctly due to PSI depth-first traversal order. Sub-task created for the multi-line case fix.

ruromero and others added 4 commits April 20, 2026 23:47
Add component analysis support for pyproject.toml manifest files,
enabling inline vulnerability diagnostics for Python projects using
PEP 621 ([project.dependencies]) and Poetry
([tool.poetry.dependencies]) formats.

New classes:
- PyprojectCAAnnotator: extracts dependencies from TOML PSI structure
- PyprojectCAIntentionAction: quick-fix for version updates
- PyprojectCAInspection: inspection registration

Modified files:
- SaAction, SaUtils: recognize pyproject.toml as supported manifest
- CAAnnotator, SAIntentionAction: add pyproject.toml to python mapping
- ApiService: apply Python-specific settings for pyproject.toml
- plugin.xml: register annotator and inspection for TOML language
- README.md: document pyproject.toml support

Implements TC-3853

Assisted-by: Claude Code
- Fix PEP 621 annotation targeting: use individual TomlLiteral elements
  instead of parent TomlKeyValue so each dependency gets its own highlight
- Fix quick-fix replacement: preserve package name in PEP 508 strings
  (e.g. "anyio==3.6.2" → "anyio==4.0.0" instead of just "==4.0.0")
- Add [project.optional-dependencies] parsing for PEP 621 optional groups
- Update README to reflect optional-dependencies are now analyzed

Implements TC-3853

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assisted-by: Claude Code
…on extraction

The version extraction regex was matching across the ';' environment
marker boundary, and the quick fix was dropping extras and markers
when rewriting the dependency string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
@ruromero
Copy link
Copy Markdown
Collaborator Author

/review

@qodo-code-review
Copy link
Copy Markdown

Persistent review updated to latest commit a1c1727

@ruromero
Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-3853 (commit a1c1727)

Check Result Details
Review Feedback WARN 8 threads classified: 2 code change requests addressed by commits, 1 question, 5 outstanding code change requests → 2 sub-tasks created (TC-4172, TC-4173)
Root-Cause Investigation DONE implement-task phase gap: Test Requirements section not fulfilled → TC-4174
Scope Containment WARN All 9 task-listed files present. 2 out-of-scope files: CargoCAAnnotator.java (related Cargo fix), plugin.xml (required by Implementation Notes for registration)
Diff Size PASS 612 additions, 10 deletions, 11 files — proportionate to 3 new files + 7 modifications + README
Commit Traceability WARN 2 of 5 commits reference TC-3853. Missing references: fix(pypi): preserve PEP 508..., chore: fail fast..., fix: implement license abstract methods
Sensitive Patterns PASS No passwords, secrets, API keys, or private keys detected
CI Status PASS All 6 checks pass: SonarCloud, build-linux, build-macos, build-windows, main, verify
Acceptance Criteria PASS 6 of 6 criteria met
Test Quality N/A No test files in PR diff
Verification Commands N/A No verification commands specified in task

Overall: WARN

Summary of issues requiring attention:

  1. Missing tests — All 5 Test Requirements from the task description are unimplemented. Sub-task TC-4172 created.
  2. Poetry ignore comment misbindfindAssociatedDependency() incorrectly resolves inner key names for multi-line inline-table dependencies. Sub-task TC-4173 created.
  3. Commit traceability — 3 of 5 commits lack the TC-3853 Jira reference.
  4. Out-of-scope fileCargoCAAnnotator.java modified but not listed in the task.

This comment was AI-generated by sdlc-workflow/verify-pr v0.6.1.

Add 35 tests covering PEP 508 utility methods, PEP 621 and Poetry
dependency extraction, optional dependencies, intention action version
updates, availability checks, and SaAction/SaUtils manifest recognition.

Implements TC-4172

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assisted-by: Claude Code
…e tables

When a trustify-da-ignore comment was placed on an inner key of a
multi-line Poetry inline table, findAssociatedDependency() returned
the inner key name (e.g., "version") instead of the outer dependency
name. Added resolvePoetryDependencyEntry() to walk up from inner
TomlKeyValue through TomlInlineTable to the actual dependency entry.

Resolves: TC-4173

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants