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

allow centralized venv storage like pyenv-virtualenv #1578

Closed
wakamex opened this issue Feb 17, 2024 · 4 comments
Closed

allow centralized venv storage like pyenv-virtualenv #1578

wakamex opened this issue Feb 17, 2024 · 4 comments
Labels
duplicate This issue or pull request already exists

Comments

@wakamex
Copy link

wakamex commented Feb 17, 2024

This provides a few handy features:

  • easily use the same venv in multiple locations
  • don't worry about losing your venv when you rm -rf a folder

To identify which venv should be used in a folder, pyenv-virtualenv uses .python-version. I suggest uv could use .uvenv as follows:

uv venv ~/.venvs/myvenv

by invoking uv venv with a non-standard location, we know we'll want to keep track of that location, so I suggest storing it in .uvenv right away without forcing the user to do echo "~/.venvs/myvenv" > .uvenv.

I can already use a script to auto-activate my venv from this remote location upon entering a folder (like an oh-my-zsh plugin):

if [ -f .uvenv ]; then
  source "$(cat .uvenv)/bin/activate"
fi

but if this were built-in behavior defined in a central location equivalent to PYENV_ROOT then you could just give the name:

uv venv local myvenv

pyenv blurb from https://github.com/pyenv/pyenv-virtualenv?tab=readme-ov-file#activate-virtualenv:

If eval "$(pyenv virtualenv-init -)" is configured in your shell, pyenv-virtualenv will automatically activate/deactivate virtualenvs on entering/leaving directories which contain a .python-version file that contains the name of a valid virtual environment as shown in the output of pyenv virtualenvs (e.g., venv34 or 3.4.3/envs/venv34 in example above) . .python-version files are used by pyenv to denote local Python versions and can be created and deleted with the pyenv local command.

@hauntsaninja
Copy link
Contributor

hauntsaninja commented Feb 17, 2024

I've been using a .venv file that can contain a location to accomplish something similar for the last half decade. It's great. I think when PEP 704 was being discussed the .venv file was suggested and had some support. This would also mean uv only needs to have a single special case name. But maybe I should just symlink?

Maybe something like the following diff:

diff --git a/crates/uv-interpreter/src/lib.rs b/crates/uv-interpreter/src/lib.rs
index 684c7f1..7c1caab 100644
--- a/crates/uv-interpreter/src/lib.rs
+++ b/crates/uv-interpreter/src/lib.rs
@@ -26,6 +26,8 @@ pub enum Error {
     MissingPyVenvCfg(PathBuf),
     #[error("Broken virtualenv `{0}`, it contains a pyvenv.cfg but no Python binary at `{1}`")]
     BrokenVenv(PathBuf, PathBuf),
+    #[error("File `{0}` points to invalid location `{1}`")]
+    InvalidVenvFile(PathBuf, PathBuf),
     #[error("Both VIRTUAL_ENV and CONDA_PREFIX are set. Please unset one of them.")]
     Conflict,
     #[error("No versions of Python could be found. Is Python installed?")]
diff --git a/crates/uv-interpreter/src/virtual_env.rs b/crates/uv-interpreter/src/virtual_env.rs
index 46c727c..a4b63a3 100644
--- a/crates/uv-interpreter/src/virtual_env.rs
+++ b/crates/uv-interpreter/src/virtual_env.rs
@@ -1,5 +1,6 @@
 use std::env;
 use std::env::consts::EXE_SUFFIX;
+use std::io::Read;
 use std::path::{Path, PathBuf};
 
 use tracing::debug;
@@ -116,21 +117,34 @@ pub(crate) fn detect_virtual_env(target: &PythonPlatform) -> Result<Option<PathB
         }
     };
 
-    // Search for a `.venv` directory in the current or any parent directory.
+
+    // Search for `.venv` in the current or any parent directory.
     let current_dir = env::current_dir().expect("Failed to detect current directory");
     for dir in current_dir.ancestors() {
         let dot_venv = dir.join(".venv");
-        if dot_venv.is_dir() {
-            if !dot_venv.join("pyvenv.cfg").is_file() {
-                return Err(Error::MissingPyVenvCfg(dot_venv));
-            }
-            let python = target.venv_python(&dot_venv);
-            if !python.is_file() {
-                return Err(Error::BrokenVenv(dot_venv, python));
+
+        let venv_dir;
+        if dot_venv.is_file() {
+            let mut contents = String::new();
+            fs_err::File::open(&dot_venv)?.read_to_string(&mut contents)?;
+            venv_dir = PathBuf::from(contents.trim());
+            if !venv_dir.is_dir() {
+                return Err(Error::InvalidVenvFile(dot_venv, venv_dir));
             }
-            debug!("Found a virtualenv named .venv at: {}", dot_venv.display());
-            return Ok(Some(dot_venv));
+        } else if dot_venv.is_dir() {
+            venv_dir = dot_venv;
+        } else {
+            continue;
+        }
+        if !venv_dir.join("pyvenv.cfg").is_file() {
+            return Err(Error::MissingPyVenvCfg(venv_dir));
+        }
+        let python = target.venv_python(&venv_dir);
+        if !python.is_file() {
+            return Err(Error::BrokenVenv(venv_dir, python));
         }
+        debug!("Found a virtualenv at: {}", venv_dir.display());
+        return Ok(Some(venv_dir));
     }
 
     Ok(None)

@swaldhoer
Copy link

Basically a duplicate of #1526

@zanieb
Copy link
Member

zanieb commented Feb 17, 2024

Not a duplicate of #1526, but this is a duplicate of #1495.

@zanieb zanieb closed this as not planned Won't fix, can't repro, duplicate, stale Feb 17, 2024
@zanieb zanieb added the duplicate This issue or pull request already exists label Feb 17, 2024
zanieb pushed a commit that referenced this issue Aug 26, 2024
For various reasons, I have a preference for out of tree virtual
environments. Things just work if I symlink, but I don't know that this
is guaranteed, so I thought I'd add a test for it. It looks like there's
another code path that matters (`FoundInterpreter::discover ->
PythonEnvironment::from_root`) for the higher level commands, but
couldn't spot a good place to test that.

Related discussion:
#1495 (comment) /
#1578 (comment)
@callegar
Copy link

callegar commented Oct 4, 2024

May I suggest the PDM model? That is nice IMHO. Via a config variable you decide if you want venvs to be generally managed centrally or locally. When they are centrally managed, PDM sorts out things nicely in an almost automatic way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

5 participants