astral-sh / uv

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

Make it possible to lock dependencies in a script #6318

Open blin opened 2 months ago

blin commented 2 months ago

Uv's script support is amazing for creating self-contained scripts that can be written once and then executed by anyone with uv months later, as long as the upper boundaries for dependencies were specified.

Being able to lock the dependencies for a script would be a huge boon in script preservation, all the same arguments that apply to uv lock and pip-tools compile apply here.

Preserving original requirement specifications would be beneficial, so after running uv add --script example.py 'requests<3' 'rich' and uv lock --script example.py the example.py will contain something like (which is basically the result of extracting dependencies into requirements.txt, running uv pip compile and pasting the result back into the script):

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "requests<3",
#     "rich",
# ]
# compiled_dependencies = [
#     "certifi==2024.7.4",
#     "charset-normalizer==3.3.2",
#     "idna==3.7",
#     "markdown-it-py==3.0.0",
#     "mdurl==0.1.2",
#     "pygments==2.18.0",
#     "requests==2.32.3",
#     "rich==13.7.1",
#     "urllib3==2.2.2",
# ]
# ///
charliermarsh commented 2 months ago

This is really cool. We can write it under tool.uv.

charliermarsh commented 2 months ago

I think this should be opt-in (something in the script's [tool.uv] section, maybe?), and we should probably write the lockfile at the bottom, since it could be large.

SummerGram commented 2 months ago

Is anyone working on it?

zanieb commented 2 months ago

No, we need to design it. Are you interested in helping with that?

SummerGram commented 1 month ago

Hi, @zanieb

I do not see the information about the compiled_dependencies in https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata

Where can I search more information?

ketozhang commented 1 month ago

PEP 723 has a sentence on this

A script runner may support injecting of dependency resolution data for an embedded lock file (this is what Go’s gorun can do). --https://peps.python.org/pep-0723/#why-not-limit-tool-configuration

I do not interpret this as a recommendation, but it's definitely thought of when this was designed.

For me, this solution isn't as useful as I like it to be in the use case of "a repository of python scripts (not packaged)". Adopting inline script metadata is still very difficult as not many of my of my users use uv or a 723-compatible runner. External lock files would support a wider compatibility. pip not supporting 723 makes this problem worse.

marengaz commented 1 month ago

https://peps.python.org/pep-0723/#how-to-teach-this

It also mentions that tool blocks are allowed. If we dumped the lock file contents into some tool.uv.xxx property there, I guess the benefit would be 2-fold; it gives non-uv-users a hint that their life would be easier with UV, gives us free reign to decide the name and number of properties we need to accomplish this

SummerGram commented 1 month ago

I think uv lock --script example.py should provide an option to let users select the file to define dependencies. The default value should be the standard pyproject.toml. I am not sure if this command generate the requirements.txt.

https://docs.astral.sh/uv/pip/compile/

It mentions that uv allows dependencies to be locked in the requirements.txt format.

I think it is confused for the users to manually modify the locked environments if uv creates both the compiled_dependencies field and requirements.txt file. What is the uv suggested method to modify the locked environments?

zanieb commented 1 month ago

should provide an option to let users select the file to define dependencies

The point is that the dependencies are defined in the script per PEP 723 — I don't think we'd support other things here.

zanieb commented 1 month ago

Adopting inline script metadata is still very difficult as not many of my of my users use uv or a 723-compatible runner. External lock files would support a wider compatibility.

I think using uv pip compile would be the recommendation then — not the uv lock format (until PEP 751 is done). I think uv pip compile --script <path> would be fine to support for that purpose.

zanieb commented 1 month ago

If we dumped the lock file contents into some tool.uv.xxx property there...

The only problem with this is that the lockfile is quite long, so we probably don't want it at the top of the script.

ketozhang commented 1 month ago

I think uv pip compile --script <path> would be fine to support for that purpose.

I am in support of this. How do you see it integrate with uv run to use the lock file?

zanieb commented 1 month ago

uv run --with-requirements requirements.txt script.py probably already works, though I'm honestly not sure what happens if there's PEP 723 metadata in there.

SummerGram commented 1 month ago

I think using uv pip compile would be the recommendation then — not the uv lock format (until PEP 751 is done). I think uv pip compile --script <path> would be fine to support for that purpose.

Which one will uv pip compile --script example.py compile the requirements.in file to? The bottom of the inline script metadata in the example.py or the lock file?