Skip to content

fix(python): handle PEP 440 direct references and path dependencies in uv export#490

Open
a-oren wants to merge 3 commits into
guacsec:mainfrom
a-oren:TC-4538
Open

fix(python): handle PEP 440 direct references and path dependencies in uv export#490
a-oren wants to merge 3 commits into
guacsec:mainfrom
a-oren:TC-4538

Conversation

@a-oren
Copy link
Copy Markdown
Contributor

@a-oren a-oren commented May 21, 2026

Summary

  • Skip PEP 440 direct references (name @ url) and path dependencies (./, ../, /) in parseUvExport() with log.fine() warning instead of throwing IOException
  • Set currentKey = null after skipping to prevent # via comments from corrupting the dependency graph of other packages
  • Add test fixture with mixed direct refs and normal packages, plus 7 new unit tests covering skip behavior, graph integrity, and integration with provideStack()/provideComponent()

Implements TC-4538

Test plan

  • parseUvExport() skips PEP 440 direct references without throwing
  • parseUvExport() skips path dependencies (./, ../, /) without throwing
  • Packages following skipped lines are parsed correctly
  • # via comments after skipped packages do not corrupt the graph
  • provideStack() and provideComponent() succeed with direct refs in export output
  • Existing tests pass (27/27)
  • mvn spotless:check passes

🤖 Generated with Claude Code

Summary by Sourcery

Handle uv export entries that lack pinned versions by skipping specific dependency types and expand test coverage to ensure dependency graphs and SBOM generation remain correct.

Bug Fixes:

  • Skip PEP 440 direct reference entries in uv export parsing to avoid failures on unpinned dependencies.
  • Skip path-based dependency entries in uv export parsing to prevent IO exceptions on non-versioned packages.
  • Reset dependency tracking state after skipped entries so subsequent '# via' comments do not corrupt the dependency graph.

Tests:

  • Add unit tests verifying skipping of direct references and path dependencies in uv export output.
  • Add tests ensuring '# via' comments following skipped entries do not create incorrect parent-child relationships.
  • Add fixture-based tests validating correct parsing of mixed direct references, path dependencies, and normal packages.
  • Add integration-style tests confirming provideStack and provideComponent work when uv export contains direct references and path dependencies.

…n uv export

Skip PEP 440 direct references (name @ url) and path dependencies (./,
../, /) in parseUvExport() with a log.fine() warning instead of throwing
an IOException. These lines lack pinned versions needed for the dependency
graph. Set currentKey to null after skipping to prevent # via comments
from corrupting the graph of other packages.

Implements TC-4538

Signed-off-by: Adva Oren <aoren@redhat.com>
Assisted-by: Claude Code
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 21, 2026

Reviewer's Guide

Updates PythonUvProvider.parseUvExport to gracefully skip PEP 440 direct references and path-based dependencies while preserving dependency graph integrity, and adds focused tests and fixtures to validate skip behavior and integration with SBOM generation (provideStack/provideComponent).

Flow diagram for updated parseUvExport line handling

flowchart TD
  A[parseUvExport line loop] --> B[check package line]
  B --> C{trimmed contains @}
  C -- yes --> D[log.fine Skipping PEP 440 direct reference]
  D --> E[set currentKey null]
  E --> A
  C -- no --> F{trimmed startsWith ./, ../, or /}
  F -- yes --> G[log.fine Skipping path dependency]
  G --> H[set currentKey null]
  H --> A
  F -- no --> I{trimmed contains ==}
  I -- no --> J[throw IOException]
  I -- yes --> K[process pinned package]
  K --> A
Loading

File-Level Changes

Change Details Files
Skip PEP 440 direct references and path dependencies in parseUvExport without failing, while preventing via-block corruption.
  • Extend package-line parsing in parseUvExport to detect lines containing ' name @ url ' and log a fine-level message while skipping them.
  • Detect path-style dependencies starting with './', '../', or '/' and log a fine-level message while skipping them.
  • Reset currentKey to null whenever a direct reference or path dependency is skipped to ensure subsequent '# via' blocks do not attach children to the wrong parent.
  • Retain existing strict behavior for non-skipped package lines that lack a '==' pinned version, continuing to throw IOException in that case.
src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java
Add tests and fixtures ensuring skipped entries do not appear in graphs/SBOMs and that normal packages and via-relationships remain correct.
  • Add unit tests verifying that PEP 440 direct references are skipped without exceptions and do not appear in the dependency graph while subsequent packages are parsed normally.
  • Add unit tests verifying that relative and absolute path dependencies are skipped without exceptions and that following packages are parsed correctly.
  • Add tests ensuring '# via' comments following skipped entries do not create incorrect parent-child relationships or corrupt direct dependency sets.
  • Introduce a uv_export_direct_refs.txt fixture mixing direct references, path dependencies, and standard packages, and add tests that parse it to validate graph contents, versions, and directDeps.
  • Add integration-style tests for provideStack and provideComponent that feed the direct-refs fixture via system property, then assert that resulting CycloneDX SBOMs exclude skipped packages while including expected normal packages.
src/test/java/io/github/guacsec/trustifyda/providers/Python_Uv_Provider_Test.java
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv/uv_export_direct_refs.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The direct-reference and path-dependency detection is currently based on simple string checks (trimmed.contains(" @ "), startsWith("./", "../", "/")); consider extracting these into clearly named helper methods with more precise matching (e.g., regex aligned with uv/PEP 440 formats) so that future edge cases (Windows paths, file: schemes, ~/ paths) can be handled consistently in one place.
  • The path-dependency skip logic only covers ./, ../, and / prefixes; if uv can emit other local path forms (e.g., ~/local-package or Windows-style paths), it might be worth adding explicit handling (or tests confirming they cannot appear) to avoid unexpected IOExceptions for those cases.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The direct-reference and path-dependency detection is currently based on simple string checks (`trimmed.contains(" @ ")`, `startsWith("./", "../", "/")`); consider extracting these into clearly named helper methods with more precise matching (e.g., regex aligned with uv/PEP 440 formats) so that future edge cases (Windows paths, `file:` schemes, `~/` paths) can be handled consistently in one place.
- The path-dependency skip logic only covers `./`, `../`, and `/` prefixes; if uv can emit other local path forms (e.g., `~/local-package` or Windows-style paths), it might be worth adding explicit handling (or tests confirming they cannot appear) to avoid unexpected IOExceptions for those cases.

## Individual Comments

### Comment 1
<location path="src/test/java/io/github/guacsec/trustifyda/providers/Python_Uv_Provider_Test.java" line_range="419-416" />
<code_context>
+  /** Verifies that path dependencies (./local-package) are skipped without throwing. */
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test to ensure `# via` comments after skipped path dependencies do not corrupt the graph

Please add a similar test case for path dependencies. For instance, cover a sequence like: normal package → path dependency + `# via` → normal package, and assert that `currentKey` is reset correctly for the path dep and that the `# via` block is not attached to the wrong package.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

// anyio after the skipped line is still parsed correctly
assertThat(data.graph()).containsKey("anyio");
assertThat(data.graph().get("anyio").version()).isEqualTo("3.6.2");
assertThat(data.directDeps()).contains("anyio");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add a test to ensure # via comments after skipped path dependencies do not corrupt the graph

Please add a similar test case for path dependencies. For instance, cover a sequence like: normal package → path dependency + # via → normal package, and assert that currentKey is reset correctly for the path dep and that the # via block is not attached to the wrong package.

a-oren added 2 commits May 21, 2026 12:00
…, add ~/Windows path support

Address code review feedback:
- Extract isDirectReference() and isPathDependency() helper methods for
  clearer naming and a single place to extend edge case handling
- Add ~/home-relative and Windows drive path (C:\) detection
- Add test for # via graph corruption after skipped path dependencies

Implements TC-4538

Signed-off-by: Adva Oren <aoren@redhat.com>
Assisted-by: Claude Code
@a-oren a-oren requested a review from ruromero May 21, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants