astral-sh / uv

An extremely fast Python package and project manager, written in Rust.
https://docs.astral.sh/uv
Apache License 2.0
26.41k stars 768 forks source link

Constraint flag for `uv add` #6518

Open yehoshuadimarsky opened 2 months ago

yehoshuadimarsky commented 2 months ago

Hi, can we instead add a new flag -c / --constraint to uv add, so that you can pass in constraint files directly? I think currently you can do this by adding a constraint to a requirements.txt file, then running uv add --requirements requirements.txt, but this would embed it directly in the command.

My specific use case is using Apache Airflow which only supports pip (not Poetry etc) because it basically requires constraint files, see https://github.com/apache/airflow/blob/main/README.md#installing-from-pypi

That way, I can do (copying from their example):

uv add 'apache-airflow==2.10.0' \
 --constraint "https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt"

Thanks.

yehoshuadimarsky commented 2 months ago

https://github.com/astral-sh/uv/issues/6275#issuecomment-2307069060

You need to add that constraint file to tool.uv.constraint_dependencies? It needs to be tracked in the project. uv add --constraint could perhaps do that for you, but it's going to apply to all of your dependencies.

That's fine, in fact that's probably the best behavior, that the constraint applies to all dependencies. But in line with my Airflow use case, it'd be great if we can specify a URL for tool.uv.constraint_dependencies, not just a file that we have to download and save

yehoshuadimarsky commented 2 months ago

cc @potiuk

zanieb commented 2 months ago

Can you not specify a URL in constraint_dependencies? I presumed that'd just work.

yehoshuadimarsky commented 2 months ago

Sorry I don't think tool.uv.constraint_dependencies is a valid section yet (if it is, apologies for my mistake). I'm just saying that if/when it becomes a valid section, it'd be great if it could specify not only dependencies, but a URL that points to a valid constraint file (like Airflow's). If this is already assumed/included, please disregard.

zanieb commented 2 months ago

Sorry — constraint-dependencies. We're missing documentation for this one for some reason.

yehoshuadimarsky commented 2 months ago

How? I tried running uv lock after adding this to pyproject.toml,

[tool.uv]
constraint-dependencies = ["https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt"]

but got

warning: Failed to parse `pyproject.toml` during settings discovery:
  TOML parse error at line 16, column 27
     |
  16 | constraint-dependencies = ["https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt"]
     |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ https://...`).
  https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: Failed to parse: `pyproject.toml`
  Caused by: TOML parse error at line 16, column 27
   |
16 | constraint-dependencies = ["https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt"]
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ https://...`).
https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zanieb commented 2 months ago

Ah sorry I misunderstood — yes this requires the constraints to be unpacked into a list. I'm not sure it makes sense to use a remote file here? We'd need to download it on every uv operation. Could we just unpack it into this setting on uv add?

yehoshuadimarsky commented 2 months ago

I'm not sure it makes sense to use a remote file here? We'd need to download it on every uv operation.

I get the overhead hit, but is it any different from uv pip install -c https://raw.githubusercontent.com/apache/airflow/constraints-2.10.0/constraints-3.8.txt apache-airflow, which works and is valid? I think it's acceptable to allow a URL here

Could we just unpack it into this setting on uv add?

Yes, that could be a good fallback, if you decide not to support saving the URL itself in pyproject.toml. I guess would be good to at least auto-generate a comment saying "constraints packages were obtained from the user supplied URL <URL>" or something like that, so they can trace it back to the source

zanieb commented 2 months ago

Yes, it's different because you uv pip install once, but with the project interface we check if the environment is up-to-date on every invocation of uv run so we can't have dynamic metadata like this (and, if we cache it, people complain when it's not checked).

yehoshuadimarsky commented 2 months ago

I see, that makes sense. So can we at least add the flag --constraint to uv add that would

  1. read the URL
  2. populate tool.uv.constraint-dependencies, along with a comment indicating where it came from
  3. then proceed as normal?

I guess there are some outstanding questions about what the expected behavior should be in these scenarios:

zanieb commented 2 months ago

Yeah that sounds nice. We should probably support it for local files first. I'm a little hesitant on automatically tracking the URL but maybe it'd be okay.

I think they'd be combined as we would normally combine constraint files when they overlap (which I think means we'd raise an error if it results in unsolvable dependencies).

torran-g commented 1 month ago

FWIW I have exactly the same use case as @yehoshuadimarsky and I'd be very pleased to see his suggestion implemented.

I'd add that running uv run or uv lock would need to adhere to the remote constraint file for all of the other dependencies in the tree that aren't specified in the pyproject.toml file; so that our local environment matches the constrained Airflow environment.

Currently, I am specifing a minimal list of dependencies in pyproject.toml without specifying versions for the packages which are included in Airflow's constraints file, then using the uv pip compile command with the --constraint flag specified to generate a compatible requirements.txt file. I can then reference the requirements.txt file when using the uv pip sync and uv pip install commands to install them in a local environment. But I'd prefer it if uv managed the package and local environment itself, and uv.lock adhered to the constraints, so I can use uv run to run my test suite.

The specific commands I'm using currently.

```shell > uv pip compile pyproject.toml --constraint https://raw.githubusercontent.com/apache/airflow/constraints-2.9.2/constraints-3.11.txt --no-strip-extras --emit-index-url --all-extras --output-file requirements.txt -q > uv pip sync requirements.txt > uv pip install --requirement requirements.txt ```

A separate, but related, feature request, would be an option to write the constraint file used to the top of the requirements.txt file. As described in point 3 of these MWAA docs.