astral-sh / uv

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

Support `pip wheel` subcommand #1681

Open kvelicka opened 7 months ago

kvelicka commented 7 months ago

I've been trying uv on work projects to assess its coverage and suitability of being a full pip/pip-tools replacement for us. One of the features of pip that we use is pip wheel, which seems to not be supported by uv currently and I couln't find an open ticket discussing it, so I'm making one here.

zanieb commented 7 months ago

We have all the plumbing to do this, I'm not sure where it fits on our roadmap though.

I'm curious how this would relate to

henryiii commented 7 months ago

Pip would prefer to remove the wheel subcommand AFIAK, and has refused adding a matching sdist command. I think uv build --wheel (along with uv build --sdist and uv build, which builds both) would be better than adding uv pip wheel.

pip wheel does one thing differently, by the way - it includes any wheels it also builds (but not just downloads), while build --wheel only gives you the wheel you asked for. But most of the time that's actually a bug (trying to upload wheels you don't own), and pip download is there if you want to actually get existing files from PyPI (not in uv yet though).

zanieb commented 7 months ago

Thanks for the additional context. I wonder what we can provide for people who want to generate wheels for all their dependencies though. It makes sense for pre-building and sharing when they aren't distributed by the original package.

sbidoul commented 6 months ago

I wonder what we can provide for people who want to generate wheels for all their dependencies though. It makes sense for pre-building and sharing when they aren't distributed by the original package.

I echo this. pip wheel is useful to download and build all parts of an app for deployment. For instance in a multi-stage Dockerfile it is common to have a fat build stage with all build tools to create the wheels, then a slim stage which only installs the wheels built in the previous stage.

henryiii commented 6 months ago

But it doesn’t actually put wheels in the wheelhouse if there’s already a built wheel, IIRC? Pip download will get everything you need then you can loop over any SDists and build them if you need them?

sbidoul commented 6 months ago

But it doesn’t actually put wheels in the wheelhouse if there’s already a built wheel, IIRC?

@henryiii I'm not entirely sure what you mean with that?

Pip download will get everything you need then you can loop over any SDists and build them if you need them?

Sure. pip wheel is convenient because it does exactly that for you.

Actually pip wheel is quite easy to implement and maintain because, it basically does everything pip install does except it stores the wheels (whether they are downloaded or built locally) into a directory instead of unpacking them in site-packages. I'd argue it is simpler than pip download, from my experience with that part of the pip code base.

sbidoul commented 6 months ago

That said, build, wheel and download are all useful, they are for different use cases.

henryiii commented 6 months ago

because it does exactly that for you ... whether they are downloaded or built locally

That's the problem, it does not store wheels that are downloaded. So if you depend on numpy, you will not get a numpy wheel in your wheelhouse unless there is no numpy wheel for that platform. This causes twine wheelhouse/* to work until you build on a platform without numpy wheels, then it crashes because you can't push numpy to PyPI. We have to work around that in cibuildwheel, as well as all the other builders that have an option to use pip wheel.

sbidoul commented 6 months ago

That's the problem, it does not store wheels that are downloaded. So if you depend on numpy, you will not get a numpy wheel in your wheelhouse unless there is no numpy wheel for that platform.

That's strange. It is not my understanding of how pip wheel works. I use it daily and it always stores a wheel for each top level requirement and all their dependencies, whether dependent wheels exist in the index (in which case it will download them to the wheelhouse) or not (in which case it will download the sdists, build them and store the resulting wheels in the wheelhouse).

Now if the purpose is uploading wheels to an index, I'd use build -w, or pip wheel --no-deps, which are more or less equivalent?

henryiii commented 6 months ago

I’ll investigate, maybe it changed or there’s an another reason it was behaving like that.

akx commented 5 months ago

I'd like an uv pip wheel sort of command; it's very useful for multi-stage Docker image builds where you might need e.g. the full Python dev kit to build wheels, and then want to just use those cached wheels in a subsequent stage, á la

FROM python:3.12 AS requirements
COPY requirements.txt /wheels/requirements.txt
RUN cd /wheels && pip wheel --no-cache-dir -r requirements.txt

FROM python:3.12-slim AS runtime
RUN --mount=type=cache,ro,from=requirements,source=/wheels,target=/wheels pip install --no-cache-dir --no-index --find-links /wheels /wheels/*.whl
potiuk commented 5 months ago

I'd like an uv pip wheel sort of command; it's very useful for multi-stage Docker image builds where you might need e.g. the full Python dev kit to build wheels, and then want to just use those cached wheels in a subsequent stage, á la

Small comment on that one (I do not contest the need for pip wheel of course just explaining how we are doing it in Airflow). I found that you can do quite a bit better than that by just copying the whole venv installed in the "build stage". If you keep it in the same location, this will work out of the box as well.

Smthing like:

FROM python:3.12 AS requirements
COPY requirements.txt /requirements.txt
RUN python -m venv /home/user/.venv && /home/user/.ven/bin/python -m pip install -r /requirements.txt

FROM python:3.12-slim AS runtime
COPY --from=requirements ~/home/user/.venv /home/user/.venv

That saves the hassle of running pip/uv install twice.

mmerickel commented 5 months ago

Just to second this issue, my pipeline is dependent on the recursive nature of pip wheel to download all dependencies into a wheelhouse which we can then install offline. Using pip download is possible but would require another loop to build wheels for everything into a proper wheelhouse and I'd really be looking for the uv equivalent to support the full cycle directly from requirements file -> wheelhouse.

pradyunsg commented 5 months ago

I tend to think of pip wheel as being useful for multiple roles:

The default is not the most common operation during development, and the functional behaviour provided by pypa/build is better suited to the right behaviours during package development workflows. Ideally, these two should be under a logically different commands in uv.

None the less, my two cents would be that this shim should not be provided and uv should instead focus on its own dedicated CLI outside of the pip namespace; keeping these two "consumer" and "publisher" pieces separated.

vigneshmanick commented 5 months ago

To second this issue, In our usecase we build the wheel once (pip wheel . -w dist)and store it as an artifact which is then used by subsequent pipelines. This helps us to ensure that the production and ci environments are consistent and also helps to manually check the wheel contents without having to resort to additional commands.

mmerickel commented 1 week ago

FWIW I added a concrete example of how I use pip wheel to https://github.com/astral-sh/uv/issues/7148 if it's helpful in motivating this feature.

notatallshaw commented 1 week ago

The command uv build now exists, does this not cover this requirement?

mmerickel commented 1 week ago

The issue is that the new command doesn’t build the dependencies. Neither the local dependencies in the workspace nor the remote dependencies from the package index.

Ralith commented 1 day ago

This would be useful for packaging Blender extensions, which need their dependencies bundled as wheels in a subdirectory: https://docs.blender.org/manual/en/dev/advanced/extensions/python_wheels.html