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

Add an option to store virtual environments in a centralized location outside projects #1495

Open
DanCardin opened this issue Feb 16, 2024 · 30 comments
Labels
enhancement New feature or improvement to existing functionality projects Related to project management capabilities virtualenv Related to virtual environments

Comments

@DanCardin
Copy link

I ideally dont .venv/ folders cluttering my project folders (not the least because they may not be safe to move, at least with venv)

In my personal (also rust) workflow tool (that i'd love to not have to maintain if uv could arrive at some of the same decisions 😆) there is a setting which when set to "central" puts all venvs in $XDG_DATA_HOME/<toolname>/..., and the path to the venv is determined by mirroring the path to the project. That is, ~/foo/bar/baz/pyproject.toml -> $XDG_DATA_HOME/uv/foo/bar/baz/.venv/.

By comparison to more traditional tools, poetry also (by default) will automatically create venvs in a central location. Although it puts all venvs in the same folder using a somewhat inscrutable hash to disambiguate projects of the same name.


This ^ feature sort of implies a few other mostly separate features in order to be useful:

  • uv activate (because it becomes impractical to self-activate, except by copy-pasting the path that gets printed)
  • which then implies some uv self init --shell zsh (or whatever), that gives you the shell integration to automatically activate through the CLI itself.
  • uv venv --delete or -d
@zanieb
Copy link
Member

zanieb commented Feb 16, 2024

Thanks for the issue!

This is the kind of thing we plan to tackle in the future, i.e. when we build an opinionated workflow for environment management. It's not in scope right this second, but we'll revisit it :)

@zanieb zanieb added enhancement New feature or improvement to existing functionality projects Related to project management capabilities labels Feb 16, 2024
@woutervh
Copy link

woutervh commented Feb 16, 2024

By comparison to more traditional tools, poetry also (by default) will automatically create venvs in a central location. Although it puts all venvs in the same folder using a somewhat inscrutable hash to disambiguate projects of the same name.

IMHO:
It's one of the really bad decisions of poetry.
As a python contractor usually supporting other python developers, I really dislike any situation where devs have no idea where their virtualenv of their project is. Your executables are the most important thing in your project, hiding them in arcane directories should never be a default.

Activating a venv is an antipattern because it introduces state in your shell.
I really hope it can be de-emphasized in the future.

@DanCardin
Copy link
Author

Imo there are various positive sideeffects of the venv not being local, although I dont personally like their chosen algorithm for selecting it.

But ultimately, as long as the tool can know where the venv should be given the settings/invocation options, then there isn't generally a need to activate the venv, at least for uv itself to function. But it's the reality that many tools require VIRTUAL_ENV to be set to function properly, which either means activating the venv or routing all commands through a uv run (which might be nice interactively but isn't ideal for committed files imo).

@ResRipper
Copy link

I use Windows/Linux/Mac every day and synchronise my projects using OneDrive, having .venv in my project folder is such a nightmare, and that's why I started using pipenv. IMO venv should always be centralised as environments and packages are platform dependent, they are not part of the "content" of the project.

I'm using this plugin to manage and switch between different environments and don't have to know their location most of the time, but I do hope there is a standard for that.

@hauntsaninja
Copy link
Contributor

hauntsaninja commented Feb 17, 2024

Note uv currently appears to work if you make .venv file a symlink to your actual venv

If you don't have symlinks on your platform, this patch of uv may work for you by adding support for .venv files that point to the actual venv location: #1578 (comment)

When uv does go in the higher level workflow direction, I'd advocate leaving a .venv symlink or file pointing to the central location. This makes it easy for IDEs and other tools to figure out where the environment is. This .venv file / symlink idea was discussed around the time PEP 704 was a thing and had fairly positive support

@woutervh
Copy link

@hauntsaninja Thanks for the tip.

I was annoyed that uv does not support the most common normal use-case of virtualenv ootb:

> virtualenv foo
> cd foo
> bin/pip install <package1>

To make it work, the symlink indeed works

> uv venv foo
> cd foo
> ln -s .  .env
> uv pip install <package2>

It would be nice if "uv venv" would make the link by default.

And this would be a nice solution for managing venvs centrally outside the project-folder.

@ResRipper and for syncing via onedrive, you can point .venv to one of the os-specific .venv-files

.venv -> .venv-linux
.venv-linux  --> /some/linux/path
.venv-mac  --> /some/mac/path
.venv-windows  --> /some/windows/path

@zanieb zanieb added the virtualenv Related to virtual environments label Feb 18, 2024
@matterhorn103
Copy link

matterhorn103 commented Apr 16, 2024

Just to add my voice that this would be really really nice to have. Different tools seem to choose either one approach or the other, and it would be great if uv would do both as I like and use both in different situations:

For development situations where git is being used, having .venv there in the project's folder is convenient.

For other projects e.g. scientific ones that are in maybe shared or cloud or working folders, it's undesirable behaviour, and then working with conda is much smoother than with Python venvs. That's pretty much the only thing that keeps me using conda for some things (other than the different ecosystem, naturally).

My impression was that symlinks have poor portability, so like @DanCardin I'd prefer something like a simple -c switch to create the venv in an automatically determined central location, and simple automatic activation, e.g.:

~/example/foo >>> uv venv -c
Using Python 3.11.9 interpreter at: /usr/bin/python3
Creating virtualenv at: /home/jdoe/.local/share/uv/example/foo/.venv
Activate with: uv venv activate
~/example/foo >>> uv venv activate
Activating virtualenv at /home/jdoe/.local/share/uv/example/foo/.venv
(foo) ~/example/foo >>> deactivate
~/example/foo >>>

but with the addition that it would be cool to be able to also activate the venv by name from another location, with the search resolved intelligently and some ability to disambiguate; I could imagine that looking like:

~/some/folder >>> uv activate foo
Searching for virtualenvs at /home/jdoe/.local/share/uv/**/foo/.venv
Activating virtualenv at: /home/jdoe/.local/share/uv/example/foo/.venv
(foo) ~/some/folder >>> uv activate bar
Searching for virtualenvs at /home/jdoe/.local/share/uv/**/bar/.venv
error: virtualenv name is ambiguous! The following matches were found:
  1) /home/jdoe/.local/share/uv/Documents/bar/.venv
  2) /home/jdoe/.local/share/uv/project1/bar/.venv
disambiguate venvs with the same name using parents e.g. to activate 1) use:
  uv venv activate Documents/bar
(foo) ~/some/folder >>> uv activate project1/bar
Searching for virtualenvs at /home/jdoe/.local/share/uv/**/project1/bar/.venv
Activating virtualenv at: /home/jdoe/.local/share/uv/project1/bar/.venv
(bar) ~/some/folder >>>

@matterhorn103
Copy link

So in addition to the "not wanting the venv to be stored in a folder that is backed up to the cloud" use case, I found another use case today:

  • Creating compiled binaries of Qt apps written with PySide using pyside6-deploy (a nuitka wrapper) isn't possible if a venv folder is present in the application folder

(In my view this is a short-sighted approach on the tool's part, but it's just another example of why it might be necessary to keep a venv elsewhere.)

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)
zanieb added a commit that referenced this issue Sep 3, 2024
…ONMENT` (#6834)

Allows configuration of the (currently hard-coded) path to the virtual
environment in projects using the `UV_PROJECT_ENVIRONMENT` environment
variable.

If empty, we'll ignore it. If a relative path, it will be resolved
relative to the workspace root. If an absolute path, we'll use that.

This feature targets use in Docker images and CI. The variable is
intended to be set once in an isolated system and used for all uv
operations.

We do not expose a CLI option or configuration file setting — we may
pursue those later but I see them as lower priority. I think a
system-level environment variable addresses the most pressing use-cases
here.

This doesn't special-case the system environment. Which means that you
can use this to write to the system Python environment. I would
generally strongly recommend against doing so. The insightful comment
from @edmorley at
#5229 (comment)
provides some context on why. More generally, `uv sync` will remove
packages from the environment by default. This means that if the system
environment contains any packages relevant to the operation of the
system (that are not dependencies of your project), `uv sync` will break
it. I'd only use this in Docker or CI, if anywhere. Virtual environments
have lots of benefits, and it's only [one line to "activate"
them](https://docs.astral.sh/uv/guides/integration/docker/#using-the-environment).

If you are considering using this feature to use Docker bind mounts for
developing in containers, I would highly recommend reading our [Docker
container development
documentation](https://docs.astral.sh/uv/guides/integration/docker/#developing-in-a-container)
first. If the solutions there do not work for you, please open an issue
describing your use-case and why.

We do not read `VIRTUAL_ENV` and do not have plans to at this time.
Reading `VIRTUAL_ENV` is high-risk, because users can easily leave an
environment active and use the uv project interface today. Reading
`VIRTUAL_ENV` would be a breaking change. Additionally, uv is
intentionally moving away from the concept of "active environments" and
I don't think syncing to an "active" environment is the right behavior
while managing projects. I plan to add a warning if `VIRTUAL_ENV` is
set, to avoid confusion in this area (see
#6864).

This does not directly enable centrally managed virtual environments. If
you set `UV_PROJECT_ENVIRONMENT` to an absolute path and use it across
multiple projects, they will clobber each other's environments. However,
you could use this with something like `direnv` to achieve "centrally
managed" environments. I intend to build a prototype of this eventually.
See #1495 for more details on this use-case.

Lots of discussion about this feature in:

- astral-sh/rye#371
- astral-sh/rye#1222
- astral-sh/rye#1211
- #5229
- #6669
- #6612

Follow-ups:

- #6835 
- #6864
- Document this in the project concept documentation (can probably
re-use some of this post)

Closes #6669
Closes #5229
Closes #6612
@callegar
Copy link

callegar commented Sep 18, 2024

When uv does go in the higher level workflow direction, I'd advocate leaving a .venv symlink or file pointing to the central location.

and

It would be nice if "uv venv" would make the link by default.
And this would be a nice solution for managing venvs centrally outside the project-folder.

Even this makes things more complex than needed if you sync to the cloud across multiple systems, because the link might need to be to different places on different machines.

If only for compatibility and ease of migration from one tool to another, I would offer the possibility to do what pdm does. That tool offers either the option to have a local .venv or the option to have venvs stored all together in a centralized place.

Note that having the venvs all stored in a single place also makes it easier to apply deduplication tools on suitable filesystems.

@PhilipVinc
Copy link

@zanieb is this a feature you're considering for the short term, or it's not high priority?

@callegar
Copy link

callegar commented Sep 29, 2024

The .venv symlink is possibly not a good workaround for those who store their work in the cloud and use it on different platforms. Depending on the cloud-sync tool that you use, you may either:

  • get your symlink also synchronized "as is", forcing you to recreate the link in case the actual venv is in different locations on the different platforms (as it is often the case)
  • have your sync client broken enough to follow the link and sync the destination.

Often you can "exclude files by pattern" on sync clients, to relieve these problems, but it is not always handy. In these cases, the env variable might be a better stopgap.

@fabienval
Copy link

I too would be keen to have venv centrally stored due to onedrive not playing nice when trying to sync .venv....
I see people suggesting symlinks, but when we are on windows, is there a way to have similar behavior?
I tried creating a shortcut named .venv but uv recreate a proper folder called .venv and install in there when I uv sync...
( We do not have permission to use tools like mklink)

@hauntsaninja
Copy link
Contributor

Does the UV_PROJECT_ENVIRONMENT trick work for your use case? If not, you could try rebasing this patch #1578 (comment) and see if uv is willing to merge it

@krstp
Copy link

krstp commented Oct 26, 2024

See my solution with .bashrc/.zshrc function uvactivate <named_venv> from a central local where I keep all my venvs: #7898

@callegar
Copy link

callegar commented Nov 8, 2024

Possibly, having venvs in a central location would also allow making --link-mode=symlink more robust. Currently, if you use it and then by mistake you clear the cache, all your venvs made with this link mode will be broken. If all the venvs are centralized, the cache clearing command could get an operating mode (maybe to be made the default) where the (centralized) environments are scanned and the stuff that has symlinks to is preserved in the cache.

@YodaEmbedding
Copy link

YodaEmbedding commented Nov 22, 2024

@callegar Poetry puts its virtualenvs in $XDG_CACHE_HOME/pypoetry/virtualenvs. Theoretically, poetry virtualenvs should be reproducible, so this is logical. However, cache clearing should be a safe operation that should not have adverse effects on startup performance.

Thus, a better location might be $XDG_STATE_HOME/pypoetry/virtualenvs, i.e. ~/.local/state/pypoetry/virtualenvs.

@zanieb zanieb changed the title Option/setting to manage virtualenvs centrally Add an option to store virtual environments in a centralized location outside projects Nov 26, 2024
@FranzForstmayr
Copy link

Another usecase: Some of my colleagues work mainly on network drives, however Python is really slow loading a huge env from a network drive. Poetry is currently a good fit, as the default location of the venv is somewhere on the local SSD which speeds up loading significantly.

@matterhorn103
Copy link

Coming back to this and reading my earlier comment, it's clear that uv wants to, and will, make activating and deactivating venvs something a user never has to think or even know about, which will be a blessing.

So that renders suggestions back in the early days like uv activate, an interface to delete the venvs (they could be treated as part of the normal cache), or a way to launch a shell with the venv activated, kind of irrelevant. But it honestly makes the prospect of this being implemented even more attractive...

With uv's vision much clearer these days, a very dreamy API now can be imagined: I would run uv init -c for a new project or uv sync -c for an existing one, and that would be the first and final time I think about it.

Thereafter I would use uv exactly as normal and it would know to use the central venv i.e. uv add, uv run, uv sync etc. all just work without me having to do anything different or provide a flag every time. 😀

@DanCardin
Copy link
Author

Keep in mind tooling and/or editors still universally react to a VIRTUAL_ENV env var. So I think it'd be a shame if uv leaned too far in that direction. Certainly it's already clearly the case that uv itself and/or project tooling you write which uses uv can stop caring about virtualenvs and activation, but i dont think eschewing them entirely is a good idea.

@jromal
Copy link

jromal commented Nov 29, 2024

So in addition to the "not wanting the venv to be stored in a folder that is backed up to the cloud" use case, I found another use case today:

* Creating compiled binaries of Qt apps written with PySide using `pyside6-deploy` (a nuitka wrapper) [isn't possible](https://doc.qt.io/qtforpython-6/deployment/deployment-pyside6-deploy.html) if a venv folder is present in the application folder

(In my view this is a short-sighted approach on the tool's part, but it's just another example of why it might be necessary to keep a venv elsewhere.)

Maybe you can install PySide as a tool and then set the TCL/TK paths of your uv python for running all pyside6 tools.
This is how I do it for rcc and uic.

I agree on the ".venv" on OneDrive. But this is because OneDrive is terrible on selecting what not to backup. A ".onedriveignore" like the ".gitignore" would do it. But the problem is OneDrive, not uv. Actually, even if I use it, I do not reccomend anybody to run any python on a OneDrive folder.

@jromal
Copy link

jromal commented Nov 29, 2024

For other projects e.g. scientific ones that are in maybe shared or cloud or working folders, it's undesirable behaviour, and then working with conda is much smoother than with Python venvs. That's pretty much the only thing that keeps me using conda for some things (other than the different ecosystem, naturally).

I moved everything I had in conda (miniforge) to uv. And I am delighted. What a change.
At the beginning I was missing this decentralized virtual environment. But not anymore.
I did lost control of what project uses what environment, and when working on another computer was a nightmare.
Now "uv sync" and done. No need to think. Furthermore, many environments are not needed.
Just use "uv run --with ???" with a --with clause for each requirement and you do wonders.
And if you work on single file scripts, check the "--script" argument for init and add.

@ostpoller
Copy link

Another use case (maybe a niche one):
My main development environment is a Linux VM on a Windows machine (Oracle VM VirtualBox). My projects are located in a directory shared between the Linux guest and the Windows host to have them available on both, quickly transfer data in and out of project directories and have them backed-up (the Windows host is hooked up to a corporate network).
Symlinks are not possible on this shared directory (a Windows file system folder).
I currently use conda (miniforge) and have a central location in a directory on the Linux VM where I keep all conda environments.
I would potentially keep conda for managing Python versions (via corporate internal mirrors of conda channels), but would like to use uv to manage individual projects and their dependencies.

@dest1n1s
Copy link

Keep in mind tooling and/or editors still universally react to a VIRTUAL_ENV env var. So I think it'd be a shame if uv leaned too far in that direction. Certainly it's already clearly the case that uv itself and/or project tooling you write which uses uv can stop caring about virtualenvs and activation, but i dont think eschewing them entirely is a good idea.

@DanCardin A VIRTUAL_ENV env var does not necessarily conflict with a centralized virtual env management. uv could set this env var correctly to the centralized location. IMO your development experience would not get worse in a centralized management. Your IDE should still be able to perform correct code jumping. You can get rid of random large cache files living with your source code (which may be tricky for some syncing tools to deal with). You can test some random code with an existing environment with something like conda activate while keeping the code portability and reproducibility brought by dependency specifications in pyproject.toml.

@DanCardin
Copy link
Author

Oh I agree, this is my issue after all :P. I meant leaning away from VIRTUAL_ENV, which they seem to already want to do. The ability to "activate" a shell, even if that's only setting VIRTUAL_ENV seems critical to any solution.

Even if uv itself can, given a specific project in a directory, determine the central venv storage location, other tools wont natively, so being able to activate the shell becomes important (lest every other project be forced to have uv-specific adapters to figure it out)

@dest1n1s
Copy link

dest1n1s commented Dec 13, 2024

Oh sorry for my misunderstanding. I totally agree with you.

@Cornelius-Figgle
Copy link

Just to weight in, I sometimes edit projects (mainly for college) off a SMB share on my home server. I generally prefer having .venv in the project folder as it is neater, but the venv tools for python don't seem to play nice with SMB, so the solution is to store the venvs locally. In poetry this is rather easy as you just add a poetry.toml to the project that sets in-project to false (since my global config sets it to true for most projects). Having a way to do this in uv would be very useful, as besides this uv seems an amazing tool.

@zanieb
Copy link
Member

zanieb commented Dec 17, 2024

@Cornelius-Figgle Does a symlink work for you, e.g., .venv -> <somewhere else>?

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 Related to project management capabilities virtualenv Related to virtual environments
Projects
None yet
Development

No branches or pull requests