sixty-north / venv-management

A Python package for programmatic creation of Python virtual environments
MIT License
0 stars 1 forks source link

Errors when using Pythons managed/installed by UV #6

Open abingham opened 5 days ago

abingham commented 5 days ago

First I'll lay out the problem as I've encountered it...

UV is able to install Pythons, much like pyenv. Suppose I create a virtual environment using a UV-managed Python, and then create a second virtual environment from the first one:

uv venv -p 3.11 from-uv
source from-uv/bin/activate
python -m venv child

In this case, the pyvenv.cfg of the second virtual environment has the key executable pointing to the UV-managed Python executable:

% cat child/pyvenv.cfg
home = /Users/austin/.local/share/uv/python/cpython-3.11.10-macos-aarch64-none/bin
include-system-site-packages = false
version = 3.11.10
executable = /Users/austin/.local/share/uv/python/cpython-3.11.10-macos-aarch64-none/bin/python3.11
command = /Users/austin/sandbox/from-uv/bin/python -m venv /Users/austin/sandbox/child

As a result, the python_executable_path() function returns that exectuble - i.e. the base UV-managed Python /Users/austin/.local/share/uv/python/cpython-3.11.10-macos-aarch64-none/bin/python3.11 - in situations where venv_management has used a UV-managed venv to create another venv.

This is a problem for the case where we use that executable to try to install packages using pip, e.g.:

run(
    [
        python_executable_path(venv_dirpath),  # <-- This resolves to the UV-managed Python
        "-m", "pip",
        "install",
        "-r", requirements_filepath
        ],
        check=True
    )

When we try to do this, the command reports that ERROR: Could not find an activated virtualenv (required)..

abingham commented 5 days ago

Even when the VIRTUAL_ENV environment variable is correctly set, the UV Python is reporting this error. Why?

Looking into the implementation of pip we see the function running_under_virtualenv():

def _running_under_venv() -> bool:
    """Checks if sys.base_prefix and sys.prefix match.

    This handles PEP 405 compliant virtual environments.
    """
    return sys.prefix != getattr(sys, "base_prefix", sys.prefix)

def _running_under_legacy_virtualenv() -> bool:
    """Checks if sys.real_prefix is set.

    This handles virtual environments created with pypa's virtualenv.
    """
    # pypa/virtualenv case
    return hasattr(sys, "real_prefix")

def running_under_virtualenv() -> bool:
    """True if we're running inside a virtual environment, False otherwise."""
    return _running_under_venv() or _running_under_legacy_virtualenv()

The result of this function ultimately triggers the "Could not find activated virtual environment" warning. As you can see, it doesn't check VIRTUAL_ENV at all...it just checks a few attributes in the sys module. If we execute the base Python directly, this function returns false.

There are more details about these sys attributes in the Python docs and in PEP 405.

abingham commented 5 days ago

My guess right now is that relying on the executable field in pyvenv.cfg is a mistake. I don't know precisely what the right answer is, but I suspect it's something like "look for 'python' in the 'bin' directory of the virtual environment' (taking platform specifics into account). By the time we're looking for the executable path, we already know the venv path, so this should be workable.

abingham commented 4 days ago

I've pushed a fix for this. However, the pipeline is not working at the moment due to #7 so we can't publish a new version. I'm leaving this open until we do that just for visibility.