> I don’t know if there’s even any other way to get the same isolation with Python, short of using a container or something else at the OS level.
You can, but you have to hack it together yourself, e.g. by modifying sys.path or setting up hooks that will do so (e.g. by creating a `usercustomize.py` or `sitecustomize.py` that gets pulled in by the standard library `site` module). And then it will amount to almost the same thing anyway, because there's really no reason for anything else.
The virtual environment itself is really barely anything at all: a place to put installed packages, and a `pyvenv.cfg` config/marker file that Python uses to automate said sys.path hacking at startup. Oh, and a bin/ subdirectory with symlinks (stub wrapper executables on Windows) to Python, and also a place to put entry-point wrapper scripts for the installed code. That's the main thing that isn't necessary for isolation, but it's pretty convenient. Oh, and some "activation" scripts that hack some environment variables together to make the environment easier to use (see, it's the use of those symlinks that determines whether the venv's packages are used).
The built-in support (such that `pyvenv.cfg` files are created and the Python binary directly cares about them) has existed since 2012, and its third-party basis (which automated the sys.path hacking in a somewhat more brutal way; https://archive.org/details/pyvideo_568___reverse-engineerin... is quite informative) since 2007.
My reply here is to people reading along. I know you know this stuff.
Yeah, I can imagine hacking something together on my own if I had to ("first run `zfs create tank/myproject`...") but I'm glad I don't. I'm not referring to the person here, but I've seen people go to great lengths to avoid virtualenvs when they're about as lightweight as such a thing can possibly be. Like, this is the entire process for using one in a project:
Now if you run `pip install foo` in that shell, it'll install the package inside that virtualenv. If your code has `import foo` somewhere, it'll load the virtualenv's package, no matter what versions of foo are installed elsewhere. It's nearly identical in concept to Node's ./node_modules directories, except that you have to explicitly activate the virtualenv, which also gives you the flexibility to easily use the same venv for multiple projects if you really want to.
If you have something like mise or uv, you can ask them to manage a venv for you, but they're conceptually just wrappers around those steps.
You can, but you have to hack it together yourself, e.g. by modifying sys.path or setting up hooks that will do so (e.g. by creating a `usercustomize.py` or `sitecustomize.py` that gets pulled in by the standard library `site` module). And then it will amount to almost the same thing anyway, because there's really no reason for anything else.
The virtual environment itself is really barely anything at all: a place to put installed packages, and a `pyvenv.cfg` config/marker file that Python uses to automate said sys.path hacking at startup. Oh, and a bin/ subdirectory with symlinks (stub wrapper executables on Windows) to Python, and also a place to put entry-point wrapper scripts for the installed code. That's the main thing that isn't necessary for isolation, but it's pretty convenient. Oh, and some "activation" scripts that hack some environment variables together to make the environment easier to use (see, it's the use of those symlinks that determines whether the venv's packages are used).
The built-in support (such that `pyvenv.cfg` files are created and the Python binary directly cares about them) has existed since 2012, and its third-party basis (which automated the sys.path hacking in a somewhat more brutal way; https://archive.org/details/pyvideo_568___reverse-engineerin... is quite informative) since 2007.