astral-sh/uv

pip compile --no-deps fails with extras

Closed this issue · 9 comments

It looks like the --no-deps mode somehow fails with pip-compile when extras are specified.

$ echo 'flask[dotenv]' | uv pip compile - --no-deps
thread 'main' panicked at crates/uv-resolver/src/resolution.rs:136:29:
Every package should have metadata: NameVersion(PackageName("flask"), "3.0.2")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

vs

$ echo 'flask[dotenv]' | uv pip compile -
# This file was autogenerated by uv via the following command:
#    uv pip compile -
blinker==1.7.0
    # via flask
click==8.1.7
    # via flask
flask==3.0.2
itsdangerous==2.1.2
    # via flask
jinja2==3.1.3
    # via flask
markupsafe==2.1.5
    # via
    #   jinja2
    #   werkzeug
python-dotenv==1.0.1
    # via flask
werkzeug==3.0.1
    # via flask

Happens with uv 0.1.6 and earlier.

Fixing now.

Should be fixed in v0.1.7 (out now).

Is this fixed? I think the final result should be flask[dotenv]==3.0.2, at least that's what pip-compile tells me:

~$ echo 'flask[dotenv]' | pip-compile - --output-file test.in
WARNING: --strip-extras is becoming the default in version 8.0.0. To silence this warning, either use --strip-extras to opt into the new default or use --no-strip-extras to retain the existing behavior.
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --output-file=test.in -
#
blinker==1.7.0
    # via flask
click==8.1.7
    # via flask
flask[dotenv]==3.0.2
    # via -r -
itsdangerous==2.1.2
    # via flask
jinja2==3.1.3
    # via flask
markupsafe==2.1.5
    # via
    #   jinja2
    #   werkzeug
python-dotenv==1.0.1
    # via flask
werkzeug==3.0.1
    # via flask

with uv (0.1.10):

~ ❯ echo 'flask[dotenv]' | uv pip compile -
Resolved 8 packages in 8ms
# This file was autogenerated by uv via the following command:
#    uv pip compile -
blinker==1.7.0
    # via flask
click==8.1.7
    # via flask
flask==3.0.2
itsdangerous==2.1.2
    # via flask
jinja2==3.1.3
    # via flask
markupsafe==2.1.5
    # via
    #   jinja2
    #   werkzeug
python-dotenv==1.0.1
    # via flask
werkzeug==3.0.1
    # via flask

I think this is the cause of astral-sh/rye#745, and a related JAX issue I filed with rye.

Oh, I see - python-dotenv is included in both places, which is why uv pip install flask[dotenv] works correctly, but rye add flask[dotenv] breaks because it tries to desugar the feature into flask==3.0.2 and python-dotenv==1.0.1, and rye only matches against the first.

I'm not familiar enough with the implications and uv's goals to say this isn't what uv wants, but I would personally prefer if uv pip compile did not expand flask[dotenv] to flask + python-dotenv, at least from rye's perspective (for example if I type rye add flask[dotenv] I want flask[dotenv]==3.0.2 in my pyproject.toml. Maybe there's a higher level API rye could be using instead (I think currently spawning a subprocess)? Let me know if you want me to create a new issue for visibility/clarity.

I believe what uv is doing is correct. It's right to omit the [dotenv] in the output file, since it doesn't have any meaning (the "extras" are already included by way of including the dependencies themselves). You can see with the --strip-extras is becoming the default in version 8.0.0. warning that pip-tools is moving to that default soon (IIUC).

It seems like the issue here is around rye add with extras, which it's using to lock the correct version of a single dependency. I think rye either needs to re-add the extra with some manual parsing, or we'd need a flag or another workflow for it.

I tried rye add "flask[dotenv]" and it added the "right" dependency:

--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,6 +30,9 @@ classifiers = [
   "Topic :: Software Development :: Libraries",
 ]
 readme = "README.md"
+dependencies = [
+    "flask[dotenv]>=3.0.2",
+]

Huh, I have a different result (maybe it's a zsh issue?):

~/proj/test-proj main ?5 ❯ rye add "flask[dotenv]"
Initializing new virtualenv in /home/singhrac/proj/test-proj/.venv
Python version: cpython@3.12.1
Added flask>=3.0.2 as regular dependency
Reusing already existing virtualenv
Generating production lockfile: /home/singhrac/proj/test-proj/requirements.lock
Generating dev lockfile: /home/singhrac/proj/test-proj/requirements-dev.lock
Installing dependencies
   Built file:///home/singhrac/proj/test-proj                                                                                                                  Built 1 editable in 202ms
Resolved 1 package in 1ms
Downloaded 1 package in 35ms
Installed 8 packages in 7ms
 + blinker==1.7.0
 + click==8.1.7
 + flask==3.0.2
 + itsdangerous==2.1.2
 + jinja2==3.1.3
 + markupsafe==2.1.5
 + test-proj==0.1.0 (from file:///home/singhrac/proj/test-proj)
 + werkzeug==3.0.1
Done!
~/proj/test-proj main ?7 ❯ cat pyproject.toml
[project]
name = "test-proj"
version = "0.1.0"
description = "Add your description here"
authors = [
    { name = "Rachit Singh", email = "rachitsingh@outlook.com" }
]
dependencies = [
    "flask>=3.0.2",
]
readme = "README.md"
requires-python = ">= 3.8"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.rye]
managed = true
dev-dependencies = []

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/test_proj"]

Do you have rye configured to use uv by default (i.e. rye config --set-bool behavior.use-uv=true)? I think it works that way when that flag is off (when it uses pip-compile + its semantics, which as you pointed out is going to be deprecated).

Thanks, I had to set the Rye config! It's a Rye issue. I'll open one there.

@rachtsingh - Tracking as astral-sh/rye#745 (existing issue in Rye).