Skip to content

Auto-update requirements.txt after Python package install/uninstall#14605

Open
bricestacey wants to merge 9 commits into
mainfrom
14343/auto-update-requirements
Open

Auto-update requirements.txt after Python package install/uninstall#14605
bricestacey wants to merge 9 commits into
mainfrom
14343/auto-update-requirements

Conversation

@bricestacey

Copy link
Copy Markdown
Contributor

Fixes #14343

Summary

After #14550 made a workspace-root requirements.txt the source of truth that pip/uv package operations resolve against (via -r <file> on the command line), the file itself was never written. Installing a package left it undeclared, so the environment drifted from what the project declares, and uninstalling a declared package didn't stick -- the next operation's -r requirements.txt reinstalled it.

This PR keeps the file's declared set in sync after explicit package operations, mirroring R's packages.r.renvAutoSnapshot:

  • Install an undeclared package: append its bare name (never a version; the file declares intent, the environment is the lock).
  • Uninstall a declared package: remove its entry (full span, including \-continuations and --hash lines).
  • Update / Update All: never touch the file (the -r resolve already governs versions).

The sync only runs when a workspace-root requirements.txt already exists (it is never created), acts only on the top-level requested package (never the transitive closure, unlike pip freeze), and is confirmed against the kernel's installed set before editing. It is decoupled from the operation: a write failure warns but never fails the install/uninstall. The uv project workflow (uv add/uv sync) is untouched since it already keeps pyproject.toml in sync; only the uv pip environment branch syncs.

Gated by a new setting packages.python.autoUpdateRequirements (boolean, default true).

Release Notes

New Features

Bug Fixes

  • N/A

Validation Steps

@:packages-pane

  1. In a workspace with a requirements.txt containing requests==2.28.2, using a Python environment:
    uv venv && uv pip install -r requirements.txt
    
  2. Packages pane -> Install pandas -> requirements.txt gains a bare pandas line; the requests pin is unchanged.
  3. Packages pane -> right-click requests -> Uninstall -> the requests==2.28.2 line is removed.
  4. Update a package (or Update All) -> requirements.txt is unchanged.
  5. Set packages.python.autoUpdateRequirements to false, install another package -> the file is not modified.
  6. Make requirements.txt read-only, install a package -> a warning appears and the package still installs.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:packages-pane

readme  valid tags

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

PETE's assessment 🧪

Verdict: 🟢 Adequate -- The PR adds unit tests at all three layers (pure editor, sync orchestration, and both pip/uv manager integrations) that directly exercise the new install-appends / uninstall-removes / setting-gated / project-untouched behavior.

What changed

  • New requirementsEditor.ts: pure, I/O-free parse/append/remove of a single top-level requirements.txt entry (handles \-continuations, --hash lines, PEP 503 normalization).
  • New requirementsSync.ts: confirm-against-installed-set then edit, with read/write failures warned-but-swallowed; gated by new packages.python.autoUpdateRequirements setting.
  • pipPackageManager.ts / uvPackageManager.ts: call the sync after install (env branch) and uninstall; the uv project workflow is intentionally untouched.
  • One-line config registration in positronPackages.contribution.ts.

Tests in this PR

  • Unit (Vitest/Mocha) ✅ (added requirementsEditor.unit.test.ts, requirementsSync.unit.test.ts; extended pipPackageManager.unit.test.ts, uvPackageManager.unit.test.ts)
  • Extension host ✅ (these are ext-host Mocha -- the correct runner for extensions/positron-python/ code)
  • E2E (Playwright) ✅ (not warranted -- file-content sync is I/O logic; existing @:packages-pane test covers the install/uninstall UI flow)

Existing coverage

  • test/e2e/tests/packages-pane/packages-pane.test.ts covers the Packages-pane install/uninstall UI flow (already tagged @:packages-pane, present in the PR body). It doesn't assert requirements.txt contents, but that behavior is covered at the unit level where it's cheaper and more reliable.

Suggested additions

None. Coverage maps cleanly to the changed behavior: editor edge cases (continuation/hash/PEP503/no-op), sync's confirm-then-write and failure-swallowing, and both managers' install-appends / uninstall-removes / setting-disabled / project-workflow-untouched paths.


PETE (Positron Extreme Test Experiment) - LLM-based test-coverage advisor, in pilot. Triggers on PR open and on /recheck-tests comments. Wrong verdict? Comment /recheck-tests (or /rePETE) on this PR to re-run. Please share feedback on how PETE performed here.

@bricestacey

Copy link
Copy Markdown
Contributor Author

This screenshot demonstrates that after installing cowsay it appears in the requirements.txt and then after uninstalling it, it's removed.

Screenshot 2026-07-01 at 10 34 05 AM

@bricestacey bricestacey requested a review from austin3dickey July 1, 2026 14:46
return content;
}
const needsNewline = content.length > 0 && !content.endsWith('\n');
return `${content}${needsNewline ? '\n' : ''}${name}\n`;

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.

This write-back is great, but I think I'm just a little uncomfortable with doing it by default without user opt-in. I think we could solve this a few different ways:

  • default the setting to false
  • have a confirmation prompt or at least an info notification the first time we do this
  • show a diff view that they can accept (we do this with the "import vs code settings" gesture for example)
  • add comments where Positron changed the file, like requests # added by Positron or # requests removed by Positron

@cindyytong cindyytong Jul 1, 2026

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.

My vote would be to:

  1. Default false
  2. First time a workspace has a requirements.txt show a notification
    "Positron can keep requirements.txt in sync with installed packages. [Enable] [Not now]".
  3. If enabled
    add comments where Positron changed the file, like requests # added by Positron or # requests removed by Positron

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should we punt this issue to the next release then?

Alternatively, we could default to false so it's opt in and then follow-up with the improvements next release.

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.

I think I'm cool with defaulting to false for 07 and doing the rest as followups.

Make requirements.txt auto-update opt-in rather than on by default, so
it never modifies a user's requirements.txt without them enabling it.

See #14343
Integrates #14627 (python.packageManager.useRequirementsFile opt-out) and
#14608. Resolved conflicts in the pip/uv managers and uv test: kept both the
new useRequirementsFile gate in _getWorkspaceRequirementsPath and the
requirements.txt writeback.

Because writeback is gated on _getWorkspaceRequirementsPath() (which now
returns undefined when useRequirementsFile is disabled), auto-update is
skipped whenever requirements.txt is not the source of truth, even if
packages.python.autoUpdateRequirements is enabled. Added a pip test pinning
this interaction.

Pre-commit hook bypassed: it flagged extensions/copilot/*positronAssistant*
files that are byte-identical to origin/main (unmodified upstream content
swept in by the merge), not part of this change.
Relocate the requirements.txt source-of-truth opt-out from the
positron-python extension (python.packageManager.useRequirementsFile,
added in #14627) to Positron core as packages.python.useRequirementsFile,
so it sits in the same namespace as packages.python.autoUpdateRequirements
and appears under Features > Packages. The extension reads the core setting
via the same configuration API.

See #14343
@juliasilge

juliasilge commented Jul 2, 2026

Copy link
Copy Markdown
Member

After chatting with Brice, we're going to wait on this updating feature until 2026.08 (after Brice is back from OOO). One could argue it isn't too terrible what we let people do here; if you add a package via pip, pip itself won't update the requirements.txt for you or anything like that.

I also vote for Cindy's suggestion in #14605 (comment).

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.

packages pane: python package operations should update the project's declarative dependency files

4 participants