# Q: What stops a venv from being relocatable?

**URL:** <https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166>\
**Category:** Packaging\
**Created:** [July 2, 2024, 11:23am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166 "2024-07-02T11:23:24Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![groodt](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/groodt/32/3248_2.png) [@groodt](https://discuss.python.org/u/groodt)\
**Post date:** [July 2, 2024, 11:23am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/1 "2024-07-02T11:23:24Z")

</div>

After re-reading Brett’s [excellent blog about how virtual environments work](https://snarky.ca/how-virtual-environments-work/), I’ve been wondering something.

If a venv is just:

- 3 directories (bin, include, lib/pythonX.Y/site-packages)
- Optional lib64 symlink

Then is the only reason that venv aren’t relocatable due to the shebangs with absolute urls in the “bin” folder? These shebangs help to “autoactivate” the Python interpreter to pickup site-packages?

If my understanding is correct, and I don’t need any console scripts in “bin”, does that mean that a venv is “relocatable” if I add the parent of site-packages to PYTHONPATH?

Anything else that won’t work as expected?

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [July 2, 2024, 11:45am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/2 "2024-07-02T11:45:40Z")

</div>

More or less. As long as there’s nothing in the venv that relies on an absolute path, it should be relocatable. The only thing that’s explicitly absolute is the script wrappers\[1\].

But we don’t guarantee relocatability (apart from anything else, we have no control over what installed code might do), so it’s very much an “at your own risk” situation.

* * *

1. and that’s deliberate, so they can be copied/symlinked and still work - so don’t ask “why not make script wrappers use relative paths?”, it’s been asked plenty of times before 😉

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [July 2, 2024, 4:25pm UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/3 "2024-07-02T16:25:48Z")

</div>

> [@groodt](#):
>
> does that mean that a venv is “relocatable” if I add the parent of site-packages to PYTHONPATH?

It won’t operate as a venv in this case, it’s just a search path (which is often fine, certainly if you’re just trying to distribute an app - you might also want to look up [`._pth` files](https://docs.python.org/3/library/sys_path_init.html#pth-files) for this). So `sys.base_*` attributes will also be set differently, and `sysconfig.get_path` will pull from a different set.

However, if you bring the `python3` symlink and it continues to refer to a real Python executable, it’ll still work as a venv if you launch that. You shouldn’t need to set `PYTHONPATH` in that case, because it’ll find the `pyvenv.cfg` and set things up “normally”.

---

<div class="post-metadata">

**Author:** ![groodt](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/groodt/32/3248_2.png) [@groodt](https://discuss.python.org/u/groodt)\
**Post date:** [July 3, 2024, 12:57am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/4 "2024-07-03T00:57:39Z")

</div>

Thanks for the replies!

> [@steve.dower](#):
>
> So `sys.base_*` attributes will also be set differently, and `sysconfig.get_path` will pull from a different set.

Makes sense. Im sure there are strange edge-cases, but are there any major areas where this would be important at runtime from an application perspective? Not from installation time (installer or build-backend) perspective?

So after looking at the contents of my own `venv`. Im beginning to realise they are more useful as “installation time” concerns? Rather than runtime concerns? Yes, they are necessary to keep the system Python clean and to prevent different projects from installing incompatible versions of things, but if you already have all packages resolved and installed into a folder somewhere (aka no build-on-install behaviour), then… you more or less don’t need a `venv`? You really just need a site-packages and a runtime?

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [July 3, 2024, 1:49am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/5 "2024-07-03T01:49:56Z")

</div>

> [@groodt](#):
>
> Then is the only reason that venv aren’t relocatable due to the shebangs with absolute urls in the “bin” folder?

It also depends on your definition of “relocatable”. For some it’s restricted to the same machine, for others it’s the same OS, and others still to any machine.

---

<div class="post-metadata">

**Author:** ![groodt](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/groodt/32/3248_2.png) [@groodt](https://discuss.python.org/u/groodt)\
**Post date:** [July 3, 2024, 3:23am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/6 "2024-07-03T03:23:41Z")

</div>

> [@brettcannon](#):
>
> It also depends on your definition of “relocatable”. For some it’s restricted to the same machine, for others it’s the same OS, and others still to any machine.

Thanks. Yes, my context would be a build system or CI system. So ignore for a moment that docker containers exist.

Lets assume that we are building for a fixed target platform represented by the cpu, vendor, os, abi and a fixed target cpython interpreter version (major.minor.patch).

Lets also assume that I am willing to ignore the contents of `venv/bin` and don’t need them on `PATH`.

I’m seeing the following folders:  
./bin - I am confident that I don’t need activation scripts or any of the console scripts  
./etc - These seem to come from jupyterlab, but not by much else. I think I can conceivably configure jupyter not to do this and place jupyter home elsewhere  
./include - I have a greenlet.h file here. It is shipped as a header with greenlet, probably so that other extensions modules can build against it. I don’t think I need this at runtime (hopefully)  
./lib/site-packages - Yes, this is the main runtime libraries I expect to be on search path  
./share/\* - Similar to jupyter. This looks like a place where docs and icons and man pages go, that I can ignore for runtime or configure tools that use these to look elsewhere

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [July 3, 2024, 4:05am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/7 "2024-07-03T04:05:23Z")

</div>

If you want to make scripts relocatable, I do that for the Hatch distributions that binaries use:

- [hatch/release/unix/make\_scripts\_portable.py at master · pypa/hatch · GitHub](https://github.com/pypa/hatch/blob/master/release/unix/make_scripts_portable.py)
- [hatch/release/windows/make\_scripts\_portable.py at master · pypa/hatch · GitHub](https://github.com/pypa/hatch/blob/master/release/windows/make_scripts_portable.py)

Feel free to copy.

edit: it’s important to note that for the Windows script it required me adding relative path support and therefore you must use the trampoline binaries after this commit [Allow relative Python executable paths in Windows trampoline by ofek · Pull Request #3717 · astral-sh/uv · GitHub](https://github.com/astral-sh/uv/pull/3717)

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [July 3, 2024, 11:49am UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/8 "2024-07-03T11:49:34Z")

</div>

> [@groodt](#):
>
> So after looking at the contents of my own `venv`. Im beginning to realise they are more useful as “installation time” concerns? Rather than runtime concerns?

`venv` is a development tool, intended to help developers manage their various projects. You can tell this because it requires a global Python install to work, which is something that typically (averaged across all platforms) only developers will have.

It gets occasionally abused as an installation tool, for those platforms where “people who install things” and “developers” tend to be very overlapping. “Non-developers” don’t want to have to install a developer tool globally in order to just run an app.

From a runtime point of view, anything on `sys.path` is importable. A `venv` is one way to indirectly configure `sys.path`, but there are a multitude of other ways, any of which are perfectly valid from the perspective of “which module will I be importing at runtime”.

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [July 3, 2024, 9:16pm UTC](https://discuss.python.org/t/q-what-stops-a-venv-from-being-relocatable/57166/9 "2024-07-03T21:16:14Z")

</div>

> [@steve.dower](#):
>
> From a runtime point of view, anything on `sys.path` is importable. A `venv` is one way to indirectly configure `sys.path`, but there are a multitude of other ways, any of which are perfectly valid from the perspective of “which module will I be importing at runtime”.

And one way is to accomplish that is to tell e.g. pip to install into a specific directory and making sure that path is on `sys.path` (e.g. `PYTHONPATH` or installing into `.`).
