feat: migrate from Poetry to UV (#1176)#1297
feat: migrate from Poetry to UV (#1176)#1297ritwiktiwari wants to merge 4 commits intosparckles:mainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 📝 WalkthroughWalkthroughReplace Poetry with UV for dependency/lockfile management across CI, nox, and release scripts; dev tooling calls updated to use uv (ruff via uv); black/isort removed from dev dependency groups and workflows. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant CI as CI (GitHub Actions)
participant UV as UV Action
participant Nox as Nox
participant Build as Build/Test
Dev->>CI: push PR
CI->>UV: setup-uv@v7 (provide uv CLI)
CI->>CI: uv sync --locked --only-group dev --no-install-project
CI->>Nox: run nox sessions (use uv-exported requirements)
Nox->>Build: build with maturin & run pytest
Build->>CI: report test results / artifacts
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @.github/workflows/lint-pr.yml:
- Around line 23-29: The workflow currently installs ruff with "uv pip install
ruff" which bypasses the lockfile; change the CI steps to install pinned dev
dependencies from uv.lock by adding ruff to dev deps locally (via "uv add --dev
ruff") and in the workflow replace the install step with "uv sync --locked" and
replace execution lines to use the locked runner flags (e.g., use "uv run
--locked ruff check ." and "uv run --locked ruff format --check ."); update the
step names that currently reference "Install dependencies with uv" and "Run
linters" to use these locked commands so CI uses deterministic ruff versions.
In `@noxfile.py`:
- Around line 8-19: The uv export invocation in the nox session currently runs
session.run("uv", "export", "--group", "test", "--group", "dev", "--no-hashes",
"--output-file", "requirements.txt") which allows uv to re-lock the project;
update that call to include the "--frozen" flag so uv errors if uv.lock would be
modified (preventing dependency drift in tests). Locate the session.run call
that invokes "uv", "export" and add the "--frozen" argument to its parameter
list (preserving the existing flags like "--no-hashes" and "--output-file").
🧹 Nitpick comments (1)
noxfile.py (1)
46-48: Make lint session non-mutating withruff format --check.Currently this will reformat files in-place. Using
--checkexits with a non-zero status code if any files would need formatting, without modifying them—aligning with CI behavior.♻️ Suggested change
- session.run("ruff", "format", ".") + session.run("ruff", "format", "--check", ".")
.github/workflows/lint-pr.yml
Outdated
| - name: Install dependencies with uv | ||
| run: | | ||
| uv pip install ruff isort black | ||
| uv pip install ruff | ||
| - name: Run linters | ||
| run: | | ||
| uv run ruff check . | ||
| uv run isort --check-only --diff . | ||
| uv run ruff format --check . |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
UV package manager uv.lock file deterministic installs CI best practices
💡 Result:
Deterministic installs with uv.lock
- Commit
uv.lockand treat it as the source of truth for CI/prod installs. - In CI, prevent uv from re-locking (which can hide problems) by using:
uv sync --lockedto validate thatuv.lockmatches the project metadata and error if it doesn’t. [1]uv sync --frozento install strictly fromuv.lockwithout checking whether it’s up-to-date (useful when CI doesn’t have all workspacepyproject.tomlfiles available). [1][2]
- Optionally add an explicit lockfile check step:
uv lock --check(equivalent to “locked” validation). [1]
CI workflow best practices
- Install from the lockfile, then run tests using locked mode (so CI fails if someone changed
pyproject.tomlbut didn’t updateuv.lock):uv sync --lockeduv run --locked pytest(same idea applies to other commands). [1]
- Prefer
uv syncin CI (notuv runalone) if you want a clean, deterministic environment:uv syncperforms an “exact” sync by default (removes packages not in the lockfile). [1]
Monorepos / partial checkouts / Docker
- If your CI/Docker step only has the root
pyproject.toml+uv.lock(not all workspace members), use--frozenfor that step, then switch to--lockedonce all workspace files are present. [2]
Speeding up CI: cache correctly
- Cache uv’s package artifacts, and run
uv cache prune --ciat the end of the job to keep only what’s worth caching in CI (notably, keep wheels built from source; drop most prebuilt wheels). [3] - If you use GitHub Actions, consider an action that installs uv and manages its cache keys (including support for keying off
uv.lock). [5]
Prevent “lockfile drift”
- Add a pre-push hook (e.g., via pre-commit) to run
uv sync --lockedso developers catch an out-of-dateuv.lockbefore CI does. [4]
Sources: uv locking/syncing semantics and flags [1], Docker/workspace guidance for --frozen vs --locked [2], CI cache pruning recommendation [3], pre-commit hook pattern [4], GitHub Actions cache tooling [5].
🌐 Web query:
UV "uv pip install" vs "uv add" lock file behavior
💡 Result:
-
uv add <pkg>(project workflow)- Adds the dependency to
pyproject.tomland (re)resolves the project, creating/updatinguv.lock. - Intended for “this is a real project dependency” and to keep the manifest + lock in sync.
Sources: Astral docs on lockfile being created/modified byuv add/uv lock/uv sync[1][2], Real Python overview [3].
- Adds the dependency to
-
uv pip install <pkg>(pip-compatible workflow)- Installs into the current environment like pip, but does not manage your project’s
pyproject.tomloruv.lock(it’s not the project lock workflow). - For reproducible installs in the pip workflow, uv expects you to use lock inputs like compiled
requirements.txt/pylock.toml(and for “exact” environments, preferuv pip syncoveruv pip install).
Sources: Astral docs on pip compile/install/sync behavior [4], Astral docs distinguishinguv.lockas project lock managed by project commands [1][2].
- Installs into the current environment like pip, but does not manage your project’s
Rule of thumb: use uv add when you want the dependency recorded + locked for the project; use uv pip install for ad-hoc / pip-style installs (and use uv pip compile + uv pip sync if you want a locked, exactly-synced environment in the pip workflow) [4].
Sources
[1] Astral uv docs — Projects layout / lockfile (uv.lock created/updated by uv sync, uv run, explicitly by uv lock) https://docs.astral.sh/uv/concepts/projects/layout/
[2] Astral uv docs — Resolution (lockfile created/modified by uv lock, uv sync, uv add) https://docs.astral.sh/uv/concepts/resolution/
[3] Real Python — uv add creates/updates uv.lock and keeps it updated on add/remove/upgrade https://realpython.com/uv-vs-pip/
[4] Astral uv docs — Pip interface: uv pip compile, uv pip install, and why uv pip sync is needed for exact, reproducible envs https://docs.astral.sh/uv/pip/compile/
🌐 Web query:
UV "uv run" command how it resolves dependencies system-installed packages
💡 Result:
uv run resolves/imports packages from uv-managed environments, not from your OS “system-installed” site-packages.
- In a project,
uv run …executes inside the project virtual environment (typically.venv) and first ensures that environment is up-to-date with the project’s declared dependencies before running your command. [1] - System site-packages are not part of that environment, so packages installed via your system package manager (apt/dnf/pacman/homebrew, etc.) generally won’t be visible to
uv run. If a dependency (e.g.,numpy) isn’t in the uv environment, you must add/install it into the uv environment. (This “no mixing system packages with uv run env” behavior is also called out by users becauseuv runhas no--system-site-packagesequivalent.) [3] - If you use
uv run --with …, uv creates an additional ephemeral “layer” environment on top of the project environment containing the requested extra packages/versions (and they may intentionally conflict with the project’s pinned deps). This still layers on top of uv-managed envs—not system site-packages. [2] - By default,
uv runmakes the minimum necessary changes to satisfy requirements;--exactmakes it remove extraneous packages for an exact sync. [2]
If what you want is “use system Python + its site-packages”, that’s a different mode: --system exists on pip/tool install/run commands, but uv run itself is designed around running in the uv/project environment rather than inheriting system site-packages. [2]
Sources: [1] [2] [3]
Pin ruff via uv.lock for deterministic linting in CI.
uv pip install ruff bypasses the lockfile and pulls the latest release each run, causing non-deterministic formatting rules and unexpected CI failures. Instead, add ruff to your dev dependencies in pyproject.toml (via uv add --dev ruff) and use uv sync --locked in CI to install from the lockfile, then run uv run --locked ruff check .. This ensures consistent tool versions across all CI runs.
🤖 Prompt for AI Agents
In @.github/workflows/lint-pr.yml around lines 23 - 29, The workflow currently
installs ruff with "uv pip install ruff" which bypasses the lockfile; change the
CI steps to install pinned dev dependencies from uv.lock by adding ruff to dev
deps locally (via "uv add --dev ruff") and in the workflow replace the install
step with "uv sync --locked" and replace execution lines to use the locked
runner flags (e.g., use "uv run --locked ruff check ." and "uv run --locked ruff
format --check ."); update the step names that currently reference "Install
dependencies with uv" and "Run linters" to use these locked commands so CI uses
deterministic ruff versions.
|
@sansyrox can I get a review? |
|
Thanks for the PR @ritwiktiwari :D Can you resolve the conflict and address the reviews by @CodeRabbit ? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1509 🏁 Script executed: Length of output: 1025 🏁 Script executed: Length of output: 2191
Summary for
|
5fb08e0 to
0472e42
Compare
4dcafe4 to
b6b1b25
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
.github/workflows/lint-pr.yml (1)
14-14:astral-sh/setup-uv@v3is several major versions behind — update tov7.The official astral uv documentation for GitHub Actions recommends
astral-sh/setup-uv@v7, and v3 was released approximately 11 months ago with v7 being the current major version. Using a stale major version misses accumulated bug fixes and feature improvements.♻️ Proposed fix
- uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v7🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/lint-pr.yml at line 14, The workflow step currently references the outdated action version "astral-sh/setup-uv@v3"; update that usage to "astral-sh/setup-uv@v7" in the lint-pr.yml workflow so the job uses the current major release (and re-run CI to verify no breaking changes); locate the step containing the string "astral-sh/setup-uv@v3" and replace it with "astral-sh/setup-uv@v7".noxfile.py (1)
46-49:pip install ruffin the lint session bypasses the lockfile.The CI workflow (
lint-pr.yml) now uses the lockfile-installed ruff viauv run --locked, but the nox lint session installs the latest ruff unconditionally — meaning local nox runs may use a different ruff version than CI and potentially produce different results.Consider aligning the nox lint session with the CI approach:
♻️ Proposed refactor
`@nox.session`(python=["3.11"]) def lint(session): - session.run("pip", "install", "ruff") - session.run("ruff", "check", ".") - session.run("ruff", "format", "--check", ".") + session.run("pip", "install", "uv") + session.run( + "uv", "sync", "--frozen", "--only-group", "dev", "--no-install-project", + external=True, + ) + session.run("uv", "run", "--frozen", "ruff", "check", ".", external=True) + session.run("uv", "run", "--frozen", "ruff", "format", "--check", ".", external=True)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@noxfile.py` around lines 46 - 49, The lint(session) nox task currently installs ruff via pip which bypasses the lockfile; replace the unconditional session.run("pip", "install", "ruff") and direct "ruff" invocations by invoking the lockfile-managed runner used in CI (e.g., session.run("uv", "run", "--locked", "ruff", ...)) so the same ruff version is used locally and in CI; update the three calls that use "ruff" (the install line and the two session.run("ruff", ...)) to use session.run("uv", "run", "--locked", "ruff", <args>) instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In @.github/workflows/lint-pr.yml:
- Around line 20-26: The lint workflow change is correct; keep the
lockfile-pinned install and non-mutating format check as implemented: retain the
commands `uv sync --locked --only-group dev --no-install-project` and the `uv
run --locked ruff check .` plus `uv run --locked ruff format --check .` steps so
dependencies are installed from uv.lock and formatting remains a check-only
operation.
In `@noxfile.py`:
- Around line 8-21: The nox session currently installs uv and runs "uv export
--frozen --group test --group dev --no-hashes --output-file requirements.txt";
keep the --frozen flag to ensure the uv.lock cannot be silently modified (so
test dependencies remain consistent) and leave the session.run invocations (the
calls to session.run("pip", "install", "uv"/"maturin") and session.run("uv",
"export", ...)) as-is—no code changes required other than ensuring the --frozen
flag remains present.
---
Nitpick comments:
In @.github/workflows/lint-pr.yml:
- Line 14: The workflow step currently references the outdated action version
"astral-sh/setup-uv@v3"; update that usage to "astral-sh/setup-uv@v7" in the
lint-pr.yml workflow so the job uses the current major release (and re-run CI to
verify no breaking changes); locate the step containing the string
"astral-sh/setup-uv@v3" and replace it with "astral-sh/setup-uv@v7".
In `@noxfile.py`:
- Around line 46-49: The lint(session) nox task currently installs ruff via pip
which bypasses the lockfile; replace the unconditional session.run("pip",
"install", "ruff") and direct "ruff" invocations by invoking the
lockfile-managed runner used in CI (e.g., session.run("uv", "run", "--locked",
"ruff", ...)) so the same ruff version is used locally and in CI; update the
three calls that use "ruff" (the install line and the two session.run("ruff",
...)) to use session.run("uv", "run", "--locked", "ruff", <args>) instead.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
noxfile.py (1)
22-23:⚠️ Potential issue | 🟡 Minor
pip install -e .before the maturin wheel build causes redundant Rust compilation.
pip install -e .on a maturin project uses PEP 660 editable installs (supported since v0.12.0), which invokescargoto compile the Rust extension. That compiled artifact is then immediately discarded whenmaturin build(line 40) compiles the extension again andpip install --no-index --find-links=dist/ robyn(line 41) replaces it with the release wheel.Nothing between line 23 and line 41 requires an installed
robyn, so the editable install is pure overhead — roughly doubling Rust compile time per matrix entry.🔧 Proposed fix
session.run("pip", "install", "-r", "requirements.txt") - session.run("pip", "install", "-e", ".") - args = [🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@noxfile.py` around lines 22 - 23, Remove the redundant editable install that triggers a Cargo build: delete or comment out the session.run("pip", "install", "-e", ".") call so the Rust extension is only built by maturin build and then installed via pip install --no-index --find-links=dist/ robyn; alternatively, if an editable install is truly needed, move session.run("pip", "install", "-e", ".") to after the maturin build / pip install --no-index --find-links=dist/ robyn sequence to avoid double compilation.
🧹 Nitpick comments (1)
noxfile.py (1)
8-8: Consider pinning theuvversion for reproducible CI.
pip install uvwithout a version specifier will pick up the latest uv on every fresh session, meaning a breaking release could silently change--frozen/--only-groupsemantics between runs. Pin to a minimum known-good version (e.g.uv>=0.5,<1) or use a fixed pin.The same applies to Line 47 in the
lintsession.♻️ Proposed change
- session.run("pip", "install", "uv") + session.run("pip", "install", "uv>=0.5,<1")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@noxfile.py` at line 8, The pip install call uses an unpinned package ("session.run(\"pip\", \"install\", \"uv\")"), which makes CI non-reproducible; change the install invocation(s) to pin a safe version (for example "uv>=0.5,<1" or a fixed exact version) so the same release is used every run, and update both occurrences (the current session.run("pip", "install", "uv") and the identical call in the lint session) to use the pinned specifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@noxfile.py`:
- Around line 22-23: Remove the redundant editable install that triggers a Cargo
build: delete or comment out the session.run("pip", "install", "-e", ".") call
so the Rust extension is only built by maturin build and then installed via pip
install --no-index --find-links=dist/ robyn; alternatively, if an editable
install is truly needed, move session.run("pip", "install", "-e", ".") to after
the maturin build / pip install --no-index --find-links=dist/ robyn sequence to
avoid double compilation.
---
Duplicate comments:
In `@noxfile.py`:
- Around line 10-21: The command invocation in session.run now includes the
correct "--frozen" flag for "uv export", which enforces lockfile consistency; no
code change is required—keep the call in the noxfile (the session.run that runs
"uv", "export", "--frozen", "--group", "test", "--group", "dev", "--no-hashes",
"--output-file", "requirements.txt") as-is since it will error on uv.lock drift.
---
Nitpick comments:
In `@noxfile.py`:
- Line 8: The pip install call uses an unpinned package ("session.run(\"pip\",
\"install\", \"uv\")"), which makes CI non-reproducible; change the install
invocation(s) to pin a safe version (for example "uv>=0.5,<1" or a fixed exact
version) so the same release is used every run, and update both occurrences (the
current session.run("pip", "install", "uv") and the identical call in the lint
session) to use the pinned specifier.
|
@sansyrox I've addressed all comments |
Description
This PR fixes #1176
Summary
This PR migrates the project from Poetry to UV for dependency management:
pyproject.toml(tool.poetry.* configurations)noxfile.pyto useuv exportinstead ofpoetry exportscripts/release.shto useuv lockinstead ofpoetry lockpoetry.lockand generateduv.lockrequires-python = ">=3.10"to project metadatablackandisortdependencies in favor ofrufffor both linting and formattingnoxfile.pylint session to useruff checkandruff format.github/workflows/lint-pr.ymlto use onlyrufffor linting and formattingAll GitHub workflows were already using UV, so no changes were needed there.
PR Checklist
Please ensure that:
Pre-Commit Instructions:
Summary by CodeRabbit