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

Consider system site packages if activated #11670

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

konstin
Copy link
Member

@konstin konstin commented Feb 20, 2025

Summary

We use site.getsitepackages() for discovering system packages for venvs created with --system-site-packages, while trying to avoid breaking exiting workflows.

Details

We currently only check in ~1 directory for install packages when looking a system or venv interpreter, usually the venv site packages. But in reality, Python (and importlib.metadata) look in all sys.path entries for packages.

This can lead to a difference between the runtime behavior and uv pip list as well as other uv operation. Notably, we miss system site packages when used (--system-site-packages on venv installation) and PyPy's std vendored packages (such as cffi).

Using the full sys.path for discovery means that we're using a variable that has many sources (including .pth files) and that can change at runtime. It easily clashes with our caching as we need to either figure all the places that influence sys.path or re-query the Python interpreter on each operation.

Besides the usual venv or system Python operation mode, uv pip also has target and prefix options that install into a directory (structure). We keep target and prefix installation as they were, so that they only consider and modify packages in their directory prefix.

Packages may be uninstallable or not un-uninstallable, where the latter category are system packages, e.g., those installed into std for PyPy. Currently, a PyPy venv will for example shadow the system cffi version with its own version. With the current change, we still don't consider the PyPy std installed packages (they are not reported by site.getsitepackages()), preserving the current behavior. If we add support in the future, we need to consider what happens when let's say a lockfile requires cffi 1.71.1 but PyPy has a vendored, non-removable cffi 1.80.0.

TODO --exact with --system-site-packages.

An open question is how to handle system packages during: uv sync and uv pip install. For uv sync, do we error or try to upgrade (and potentially fail) when the versions mismatch? For uv pip install should we set them as constraint, and if so, do we need to add a new incompatibility kind for system packages (currently, system packages are preferences).

Best reviewed commit-by-commit.

Closes #9849
Resolves #2500
TODO #4466

@konstin konstin added the enhancement New feature or improvement to existing functionality label Feb 20, 2025
@konstin konstin force-pushed the konsti/sys-path-discovery branch 6 times, most recently from 2ab39f4 to 658d4d4 Compare February 27, 2025 09:59
@konstin konstin force-pushed the konsti/sys-path-discovery branch from 658d4d4 to f926830 Compare March 4, 2025 15:38
@konstin konstin force-pushed the konsti/sys-path-discovery branch from f926830 to 0c24298 Compare March 5, 2025 10:25
@konstin konstin force-pushed the konsti/sys-path-discovery branch from 0c24298 to 690a068 Compare March 5, 2025 16:55
konstin and others added 5 commits March 5, 2025 18:04
This better reflects the intended behavior of this object, which is to
serve as a database of InstalledPackages rather than a representation of
the relevant environment/interpreter path.
This behavior was originally written for supporting `sys.path` instead of `site.getsitepackages()`: It's currently not load-bearing, but it is correct deduplication if we need to add more paths to the package discovery in the future.

With this change the behavior of `uv pip list` mirrors the behavior of `pip list`: It shows only the first package in `sys.path`, which is the package that Python will load, and ignores the others.

In the JSON output, we show all packages, but tagged with a `shadowed` bool. The exception is `--outdated`, in which case we only check if the main packages are outdated (updating shadowed packages is ineffective). The consumer is responsible for filtering on `shadowed` if applicable.

We show a hint (stderr, not stdout like the main output), if shadowed packages exist.

The logic includes handling of the fedora lib/lib64 venv split.
Previously, uv was discovering packages only in purelib/platlib, which excluded system site packages if activated.

`site.getsitepackages()` is more stable than `sys.path`. `sys.path` can change in a number of ways such as `.pth` files and runtime modification, while `site.getsitepackages()` is generally stable for a Python environment. Using the `site`, we get the same paths that Python itself uses for `sys.path`.
Co-authored-by: Pradyun Gedam <[email protected]>

When a venv is created, values such as `sys.path` and `site.getsitepackages()` are different from the base interpreter, which we query from the interpreter directly instead of trying to do the transformation ourselves.
@konstin konstin force-pushed the konsti/sys-path-discovery branch from 690a068 to 459604e Compare March 5, 2025 17:04
@konstin konstin changed the title Run CI Consider system site packages for uv pip list Mar 5, 2025
@konstin konstin changed the title Consider system site packages for uv pip list Consider system site packages if activated Mar 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or improvement to existing functionality
Projects
None yet
2 participants