Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support uv #11289

Closed
njzjz opened this issue Apr 18, 2024 · 14 comments
Closed

Support uv #11289

njzjz opened this issue Apr 18, 2024 · 14 comments

Comments

@njzjz
Copy link

njzjz commented Apr 18, 2024

What's the problem this feature will solve?

Speed up Python installation using uv, an extremely fast Python package installer that can replace pip. I have seen uv faster than pip in all of my Python projects.

Describe the solution you'd like

Provide an option to replace python -m pip below with python -m uv pip.

image

I think the related code is here:

pip_install_cmd = [
self.venv_bin(filename="python"),
"-m",
"pip",
"install",
"--upgrade",
"--no-cache-dir",
]

args = [
self.venv_bin(filename="python"),
"-m",
"pip",
"install",
]

and uv is required to be installed in advance.

Alternative solutions

None

Additional context

@humitos
Copy link
Member

humitos commented Apr 18, 2024

Hi! We currently don't support uv at the core level and we are not planning to change the default pip behavior in the near future. However, users wanting to use uv can build their project using build.jobs.post_install (similar to how people is using poetry: https://docs.readthedocs.io/en/latest/build-customization.html#install-dependencies-with-poetry)

On the other hand, the whole build process can be overwritten by using build.commands (https://docs.readthedocs.io/en/latest/build-customization.html) and running all the commands manually, including uv to install the requirements.

Please, let me know in a following comment if you were able to make it work with any of these approaches. That way, users arriving to this issue will find an immediately solution.

Finally, it's worth to mention that we have plans to support overriding pre-defined jobs like build.jobs.install and build.jobs.build in the following quarter (hopefully) 1. With that in place, users will be able to do something like the following to override pip install command:

version: 2
build:
  jobs:
    install:
      - uv pip sync docs/requirements.txt

Footnotes

  1. internal and private conversation about this: https://github.com/readthedocs/meta/discussions/46. We will eventually make this task a public issue to receive feedback from our users.

@humitos humitos closed this as not planned Won't fix, can't repro, duplicate, stale Apr 18, 2024
@njzjz
Copy link
Author

njzjz commented Apr 18, 2024

Thanks. The configuration below seems to be working for me, which is similar to poetry:

build:
  jobs:
    post_create_environment:
      - pip install uv
    post_install:
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install .[docs]

image

In this project, the installation time takes 115s (pip) -> 69s (uv), saving 46s.

@humitos
Copy link
Member

humitos commented Apr 18, 2024

Great, thanks! I opened a PR with a small documentation section showing how to use uv currently: #11290

@henryiii
Copy link
Contributor

By the way, the two remaining steps are really slow: python -m pip install --upgrade --no-cache-dir pip setuptools and python -m pip install --upgrade --no-cache-dir sphinx readthedocs-sphinx-ext. If there was some way to override these to be uv pip instead, I expect a lot of that remaining time would be gone. They seem to take 40 seconds; while uv should be able to do it in a few seconds, probably more than halving your remaining time in the example above.

@henryiii
Copy link
Contributor

henryiii commented May 10, 2024

The following workaround does work:

build:
  jobs:
    post_create_environment:
      - pip install uv
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install --upgrade pip setuptools
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install --upgrade sphinx readthedocs-sphinx-ext
    post_install:
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install .[docs]

UV resolves the first line in 0 seconds (not measurable) vs. pip taking 18 seconds. And the second takes 1 seconds (down from 20). Then pip wastes two seconds checking and reporting that everything is up to date. So this changes 40 seconds into <4 seconds. ;)

Now the biggest time sink is the pip install uv step which takes 7 seconds. 😆

@henryiii
Copy link
Contributor

You can drop the uv install time to 4 seconds with curl, but the fastest (and least annoying way) is to use asdf, there's a plugin: https://github.com/asdf-community/asdf-uv. That gets it in 0s + 0s + 1s, which regardless of rounding should be less than four seconds, and doesn't require adding a bunch of full paths to ~/.cargo/bin/uv. But it still wastes time on the pip steps that aren't doing anything (and it's installing things that aren't needed, like pip and setuptools). The fastest method is this:

build:
  os: "ubuntu-22.04"
  tools:
    python: "3.12"
  commands:
    - asdf plugin add uv
    - asdf install uv latest
    - asdf global uv latest
    - uv venv
    - uv pip install .[docs]
    - .venv/bin/python -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html

The entire build takes 21 seconds, less than half of the time it takes with the default pip configuration.

(Would adding tools: uv: "latest" be something easy and reasonable to do?)

matthewfeickert added a commit to scikit-hep/pyhf that referenced this issue May 10, 2024
* RTD doesn't support uv at the same level as pip, but there are ways
  to still use it along with most of the infrastructure. Adopt the
  strategy provided in ReadTheDocs's 'Build process customization' section
  https://docs.readthedocs.io/en/latest/build-customization.html#install-dependencies-with-uv
  , from readthedocs/readthedocs.org#11152,
  but continue to monitor alternative faster methods such as those
  described in readthedocs/readthedocs.org#11289 .
@humitos
Copy link
Member

humitos commented May 10, 2024

Thanks @henryiii for all the research on this and for sharing it on this issue. I think it will help other users having the same doubts. We could probably update our documentation to reflect what you posted using build.commands 👍🏼

(Would adding tools: uv: "latest" be something easy and reasonable to do?)

Considering that installing uv via asdf as you mentioned only takes 1s, we won't earn too much time by adding it as an option for build.tools (they live in S3 and are copied at build time into the Docker image).

I would say that the build.commands example that you posted here is the way to go for users that want to use uv to speed up their builds ⚡

@ddelange
Copy link

ddelange commented Jul 4, 2024

Hi 👋 We currently have the following config:

---
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
  os: ubuntu-22.04
  tools:
    python: '3.12'

# Build documentation in the docs/ directory with Sphinx
sphinx:
  configuration: docs/source/conf.py

# Build documentation with MkDocs
#mkdocs:
#  configuration: mkdocs.yml

# Optionally build your docs in additional formats such as PDF and ePub
formats: all

# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
  install:
  - method: pip
    path: .
  - requirements: requirements_rtd.txt

It would be cool if we can change - method: pip to - method: uv, instead of adding a whole new commands section as described above. Is that worth re-opening this issue?

@EwoutH
Copy link
Contributor

EwoutH commented Sep 3, 2024

I would love to have improved uv support in Readthedocs, making it as simple defining:

python:
  install:
    - method: uv

Would you like me to open a new issue for that or shall we reopen this issue and discuss it here further?

@ddelange
Copy link

ddelange commented Sep 4, 2024

please see #11551 for a proposal that would add more options for the user than just uv

@ddelange
Copy link

ddelange commented Sep 4, 2024

not saying I'm against a method: uv alternative, feels like a simpler solution:)

@humitos
Copy link
Member

humitos commented Sep 4, 2024

As @ddelange mentioned, we are going to move forward with a more generic solution here. Implementing python.install.method: uv will require a lot of effort from the core team and will solve only a particular and specific use case. We want to support more tools (e.g. poetry, pdm, etc) and we also want to provide users with a more customizable experience like passing extra arguments to the these command.

@effigies
Copy link

effigies commented Nov 8, 2024

FWIW, I found this approach that didn't require replicating the RTD sphinx command, so that can be allowed to change upstream:

  jobs:
    pre_create_environment:
      - asdf plugin add uv
      - asdf install uv latest
      - asdf global uv latest
      # Turn `python -m virtualenv` into `python -c pass`
      - truncate --size 0 $( dirname $( uv python find ) )/../lib/python3*/site-packages/virtualenv/__main__.py
    post_create_environment:
      - uv venv $READTHEDOCS_VIRTUALENV_PATH
      # Turn `python -m pip` into `python -c pass`
      - truncate --size 0 $( ls -d $READTHEDOCS_VIRTUALENV_PATH/lib/python3* )/site-packages/pip.py
      # Use a cache dir in the same mount to halve the install time
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install --cache-dir $READTHEDOCS_VIRTUALENV_PATH/../../uv_cache --upgrade sphinx
    post_install:
      - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH uv pip install --cache-dir $READTHEDOCS_VIRTUALENV_PATH/../../uv_cache -r docs/requirements.txt

@ddelange
Copy link

ddelange commented Nov 9, 2024

Another alternative is to commit a lockfile in your repo using uv (and update it whenever you change requirements files), and point RTD to that file for a regular pip install (which hence will be fast).

uv pip compile requirements/*.txt -o requirements/requirements.lock --prerelease=allow --no-annotate --no-header

Example enforcement in Github Actions:

    - name: Check whether requirements changed
      id: requirements-changed
      uses: tj-actions/changed-files@v34
      with:
        files: |
          requirements/*.txt

    - name: Check whether make lockfile was forgotten
      if: steps.requirements-changed.outputs.any_changed == 'true'
      run: |
        uv pip compile requirements/*.txt -o requirements/requirements.lock --prerelease=allow --no-annotate --no-header
        test ! "$(git diff)" && echo "no changes" || ( git diff && exit 1 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants