Add uv support: requirements-to-uv, uv-to-requirements, auto-detection in existing commands#5
Conversation
…n in existing commands Co-Authored-By: 刘奕聪 <github@m.mrlyc.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Pull request overview
Adds first-class support for the uv package manager alongside Poetry, including new conversion commands and auto-detection of the active package manager based on lockfiles.
Changes:
- Introduces
Uvpackage-manager adapter with a Poetry-compatible interface. - Extends core dependency management/export/extraction to accept either Poetry or uv.
- Updates CLI to add
requirements-to-uv/uv-to-requirementsand to auto-detect the package manager for existing private-package commands; updates and adds tests.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
versifier/uv.py |
Adds Uv implementation for add/export/sync/run/lock behaviors. |
versifier/core.py |
Generalizes dependency operations to accept Poetry or Uv via a union type. |
versifier/__main__.py |
Adds --uv-path, uv commands, and lockfile-based package-manager auto-detection for relevant commands. |
tests/test_uv.py |
Adds unit tests for Uv behavior (mocking subprocess calls). |
tests/test_main.py |
Updates CLI/context tests for uv support and auto-detection scenarios; adds uv command tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def export_requirements( | ||
| self, | ||
| include_dev_requirements: bool = False, | ||
| extra_requirements: Optional[Iterable[str]] = None, | ||
| with_credentials: bool = False, | ||
| ) -> RequirementsFile: | ||
| with TemporaryDirectory() as td: | ||
| requirement_path = os.path.join(td, "requirements.txt") | ||
|
|
||
| commands = [ | ||
| self.uv_path, | ||
| "export", | ||
| "--no-hashes", | ||
| f"--output-file={requirement_path}", | ||
| ] | ||
|
|
||
| if not include_dev_requirements: | ||
| commands.append("--no-dev") | ||
|
|
||
| if extra_requirements: | ||
| commands.extend(f"--extra={i}" for i in extra_requirements) | ||
|
|
||
| check_call(commands) | ||
| rf = RequirementsFile.from_file(requirement_path) | ||
|
|
||
| return rf |
There was a problem hiding this comment.
with_credentials parameter is accepted but never used. This matters because PackageExtractor calls export_requirements(..., with_credentials=True) for private package extraction; silently ignoring it can lead to exporting requirements without embedded auth data and break installs against private indices. Consider either implementing an equivalent behavior for uv, or explicitly warning/raising when with_credentials=True so callers don’t get a false sense of security.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
There was a problem hiding this comment.
Fixed: export_requirements() now logs a warning when with_credentials=True since uv export has no equivalent flag. Added a test to verify the warning is emitted.
| def install( | ||
| self, include_dev_requirements: bool = False, extra_requirements: Optional[Iterable[str]] = None | ||
| ) -> None: | ||
| commands = [self.uv_path, "sync"] | ||
|
|
||
| if extra_requirements: | ||
| commands.extend(f"--extra={i}" for i in extra_requirements) | ||
|
|
||
| check_call(commands) |
There was a problem hiding this comment.
include_dev_requirements is currently unused in install(), so callers can’t control whether dev dependencies are installed (and the parameter is misleading). Either honor the flag (e.g., pass the appropriate uv sync option) or remove/rename the parameter and adjust the shared interface accordingly.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
There was a problem hiding this comment.
Fixed: install() now passes --no-dev when include_dev_requirements=False (the default). Added test_install_with_dev to verify both paths.
tests/test_main.py
Outdated
| with pytest.raises(Exception): | ||
| ctx.package_manager |
There was a problem hiding this comment.
This test asserts a very broad exception type. Since Context.package_manager raises a specific click.UsageError, the test should assert that exact exception (and ideally the message) to avoid masking unrelated failures.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
There was a problem hiding this comment.
Fixed: Now asserts click.UsageError with message match "No uv.lock or poetry.lock found" instead of broad Exception.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ements in install(), use specific click.UsageError in test Co-Authored-By: 刘奕聪 <github@m.mrlyc.com>
….devin.ai/proxy/github.com/MrLYC/versifier into devin/1770481980-add-uv-support
Co-authored-by: MrLYC <6391488+MrLYC@users.noreply.github.com>
…tion Co-authored-by: MrLYC <6391488+MrLYC@users.noreply.github.com>
Co-authored-by: MrLYC <6391488+MrLYC@users.noreply.github.com>
Co-authored-by: MrLYC <6391488+MrLYC@users.noreply.github.com>
Honor include_dev_requirements flag in Uv.install()
Assert specific click.UsageError in package manager auto-detection test
…0481980-add-uv-support
Add uv support with auto-detection for package manager commands
Summary
Adds uv as a supported package manager alongside Poetry. New CLI commands (
requirements-to-uv,uv-to-requirements) mirror the existing Poetry commands. Existing commands (extract-private-packages,obfuscate-private-packages) now auto-detect whether the project uses uv or Poetry by checking foruv.lock/poetry.lock.New files:
versifier/uv.py—Uvclass with same interface asPoetry(add_packages,export_requirements,install,run_command,init_if_needed)tests/test_uv.py— unit tests for theUvclassModified files:
versifier/core.py—PackageManager = Union[Poetry, Uv]type alias;DependencyManager,DependencyExporter,PackageExtractornow accept eitherversifier/__main__.py—Contextgainsuv_path,uvproperty,package_managerauto-detection property; new--uv-pathCLI option; newrequirements-to-uvanduv-to-requirementscommands;extract-private-packagesandobfuscate-private-packagesswitched fromctx.poetry→ctx.package_managertests/test_main.py— existing tests updated for newuv_pathfield; new tests for uv commands and auto-detectionUpdates since last revision
Addressed review feedback:
Uv.install()now honorsinclude_dev_requirements— passes--no-devtouv syncwheninclude_dev_requirements=False(the default)Uv.export_requirements()warns onwith_credentials=True— logs a warning thatuv exportdoes not support embedding credentials, instead of silently ignoring the parametertest_context_package_manager_nonenow assertsclick.UsageErrorwith message match instead of broadExceptionReview & Testing Checklist for Human
extract-private-packagesandobfuscate-private-packagesnow raiseUsageErrorif neitheruv.locknorpoetry.lockexists. Previously they would always use Poetry (even without a lock file). Verify this is acceptable behavior.poetryacceptsUvinstances —core.pydataclasses keep the field namepoetry: PackageManagerfor backward compat, so callers writeDependencyManager(poetry=uv_instance). Confirm this naming tradeoff is acceptable vs. a rename.UvandPoetryshare the same duck-typed interface but there is no shared base class orProtocolenforcing it. A future method addition to one could silently break the other.Recommended test plan:
versifier uv-to-requirements -o /tmp/req.txtand verify outputversifier extract-private-packages -P <pkg>and verify it still worksversifier extract-private-packages -P fooand confirm the error messageNotes
stub.py)uvCLILink to Devin run: https://app.devin.ai/sessions/2b89e39464164fe085c3208ff2b164d7
Requested by: @MrLYC