Repository: pypa/virtualenv

Issue:
Virtualenv corrupts `dist-info` during embedded downgrade overlaying existing venv
Scenario:

1. Virtualenv carries outdated embedded setuptools (or any other internal essential).
2. Rollout virtualenv.
3. Activate rolled out venv.
4. Upgrade setuptools with pip.
5. Deactivate venv.
6. Rollout virtualenv again on top of the existing venv.
7. Observe multiple `dist-info` directories present for incorrectly downgraded tool, resulting in venv corruption.

Example - always reproducible:

```console
(pyb-3.9.7) [username@hostname pybuilder]$ pip install virtualenv\<20.8
Collecting virtualenv<20.8
  Using cached virtualenv-20.7.2-py2.py3-none-any.whl (5.3 MB)
Requirement already satisfied: distlib<1,>=0.3.1 in /home/username/.pyenv/versions/3.9.7/envs/pyb-3.9.7/lib/python3.9/site-packages (from virtualenv<20.8) (0.3.2)
Requirement already satisfied: backports.entry-points-selectable>=1.0.4 in /home/username/.pyenv/versions/3.9.7/envs/pyb-3.9.7/lib/python3.9/site-packages (from virtualenv<20.8) (1.1.0)
Requirement already satisfied: platformdirs<3,>=2 in /home/username/.pyenv/versions/3.9.7/envs/pyb-3.9.7/lib/python3.9/site-packages (from virtualenv<20.8) (2.3.0)
Requirement already satisfied: six<2,>=1.9.0 in /home/username/.pyenv/versions/3.9.7/envs/pyb-3.9.7/lib/python3.9/site-packages (from virtualenv<20.8) (1.16.0)
Requirement already satisfied: filelock<4,>=3.0.0 in /home/username/.pyenv/versions/3.9.7/envs/pyb-3.9.7/lib/python3.9/site-packages (from virtualenv<20.8) (3.0.12)
Installing collected packages: virtualenv
  Attempting uninstall: virtualenv
    Found existing installation: virtualenv 20.8.0
    Uninstalling virtualenv-20.8.0:
      Successfully uninstalled virtualenv-20.8.0
Successfully installed virtualenv-20.7.2
(pyb-3.9.7) [username@hostname pybuilder]$ virtualenv test-env
created virtual environment CPython3.9.7.final.0-64 in 113ms
  creator CPython3Posix(dest=/home/username/src/pybuilder/test-env, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/username/.local/share/virtualenv)
    added seed packages: pip==21.2.3, setuptools==57.4.0, wheel==0.37.0
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator
(pyb-3.9.7) [username@hostname pybuilder]$ source test-env/bin/activate
(test-env) (pyb-3.9.7) [username@hostname pybuilder]$ pip install -U setuptools
Requirement already satisfied: setuptools in ./test-env/lib/python3.9/site-packages (57.4.0)
Collecting setuptools
  Using cached setuptools-58.0.4-py3-none-any.whl (816 kB)
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 57.4.0
    Uninstalling setuptools-57.4.0:
      Successfully uninstalled setuptools-57.4.0
Successfully installed setuptools-58.0.4
WARNING: You are using pip version 21.2.3; however, version 21.2.4 is available.
You should consider upgrading via the '/home/username/src/pybuilder/test-env/bin/python -m pip install --upgrade pip' command.
(test-env) (pyb-3.9.7) [username@hostname pybuilder]$ deactivate
(pyb-3.9.7) [username@hostname pybuilder]$ ls -la test-env/lib/python3.9/site-packages/
total 24
drwxr-xr-x 11 username username 4096 Sep 16 13:34 .
drwxr-xr-x  3 username username   27 Sep 16 13:34 ..
drwxr-xr-x  3 username username   63 Sep 16 13:34 _distutils_hack
-rw-r--r--  1 username username  152 Sep 16 13:34 distutils-precedence.pth
drwxr-xr-x  5 username username  111 Sep 16 13:34 pip
drwxr-xr-x  2 username username  130 Sep 16 13:34 pip-21.2.3.dist-info
-rw-r--r--  1 username username    0 Sep 16 13:34 pip-21.2.3.virtualenv
drwxr-xr-x  6 username username   86 Sep 16 13:34 pkg_resources
drwxr-xr-x  2 username username   40 Sep 16 13:34 __pycache__
drwxr-xr-x  7 username username 4096 Sep 16 13:34 setuptools
drwxr-xr-x  2 username username  143 Sep 16 13:34 setuptools-58.0.4.dist-info
-rw-r--r--  1 username username   18 Sep 16 13:34 _virtualenv.pth
-rw-r--r--  1 username username 5662 Sep 16 13:34 _virtualenv.py
drwxr-xr-x  4 username username  190 Sep 16 13:34 wheel
drwxr-xr-x  2 username username  130 Sep 16 13:34 wheel-0.37.0.dist-info
-rw-r--r--  1 username username    0 Sep 16 13:34 wheel-0.37.0.virtualenv
(pyb-3.9.7) [username@hostname pybuilder]$ virtualenv test-env
created virtual environment CPython3.9.7.final.0-64 in 128ms
  creator CPython3Posix(dest=/home/username/src/pybuilder/test-env, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/username/.local/share/virtualenv)
    added seed packages: pip==21.2.3, setuptools==57.4.0, setuptools==58.0.4, wheel==0.37.0
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator
(pyb-3.9.7) [username@hostname pybuilder]$ ls -la test-env/lib/python3.9/site-packages/
total 24
drwxr-xr-x 12 username username 4096 Sep 16 13:35 .
drwxr-xr-x  3 username username   27 Sep 16 13:34 ..
drwxr-xr-x  2 username username   44 Sep 16 13:35 _distutils_hack
-rw-r--r--  1 username username  152 Sep 16 13:35 distutils-precedence.pth
drwxr-xr-x  4 username username   92 Sep 16 13:35 pip
drwxr-xr-x  2 username username  130 Sep 16 13:35 pip-21.2.3.dist-info
-rw-r--r--  1 username username    0 Sep 16 13:35 pip-21.2.3.virtualenv
drwxr-xr-x  5 username username   67 Sep 16 13:35 pkg_resources
drwxr-xr-x  2 username username   40 Sep 16 13:34 __pycache__

********  <=======================
drwxr-xr-x  6 username username 4096 Sep 16 13:35 setuptools
drwxr-xr-x  2 username username  126 Sep 16 13:35 setuptools-57.4.0.dist-info
-rw-r--r--  1 username username    0 Sep 16 13:35 setuptools-57.4.0.virtualenv
drwxr-xr-x  2 username username  143 Sep 16 13:34 setuptools-58.0.4.dist-info
*********

-rw-r--r--  1 username username   18 Sep 16 13:35 _virtualenv.pth
-rw-r--r--  1 username username 5662 Sep 16 13:35 _virtualenv.py
drwxr-xr-x  4 username username  190 Sep 16 13:35 wheel
drwxr-xr-x  2 username username  130 Sep 16 13:35 wheel-0.37.0.dist-info
-rw-r--r--  1 username username    0 Sep 16 13:35 wheel-0.37.0.virtualenv
```

This environment is now broken producing pybuilder/pybuilder#795 due to `setuptools-57.4.0.dist-info` and `setuptools-58.0.4.dist-info` being present.


Discussion:
```console
$ virtualenv -vvv --with-traceback test-env
111 setup logging to NOTSET [DEBUG report:43]
120 find interpreter for spec PythonSpec(path=/home/username/.pyenv/versions/pyb-3.9.7/bin/python) [INFO builtin:62]
120 discover exe for PythonInfo(spec=CPython3.9.7.final.0-64, exe=/home/username/.pyenv/versions/pyb-3.9.7/bin/python, platform=linux, version='3.9.7 (default, Aug 31 2021, 15:30:43) \n[GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]', encoding_fs_io=utf-8-utf-8) in /home/username/.pyenv/versions/3.9.7 [DEBUG py_info:389]
120 filesystem is case-sensitive [DEBUG info:28]
121 Attempting to acquire lock 139925606013152 on /home/username/.local/share/virtualenv/py_info/1/57838396cefc4382825b151b3bf88f470d177753880377de6cba0503e39937b3.lock [DEBUG filelock:270]
121 Lock 139925606013152 acquired on /home/username/.local/share/virtualenv/py_info/1/57838396cefc4382825b151b3bf88f470d177753880377de6cba0503e39937b3.lock [INFO filelock:274]
121 got python info of /home/username/.pyenv/versions/3.9.7/bin/python3.9 from /home/username/.local/share/virtualenv/py_info/1/57838396cefc4382825b151b3bf88f470d177753880377de6cba0503e39937b3.json [DEBUG via_disk_folder:135]
122 Attempting to release lock 139925606013152 on /home/username/.local/share/virtualenv/py_info/1/57838396cefc4382825b151b3bf88f470d177753880377de6cba0503e39937b3.lock [DEBUG filelock:315]
122 Lock 139925606013152 released on /home/username/.local/share/virtualenv/py_info/1/57838396cefc4382825b151b3bf88f470d177753880377de6cba0503e39937b3.lock [INFO filelock:318]
122 proposed PythonInfo(spec=CPython3.9.7.final.0-64, system=/home/username/.pyenv/versions/3.9.7/bin/python3.9, exe=/home/username/.pyenv/versions/pyb-3.9.7/bin/python, platform=linux, version='3.9.7 (default, Aug 31 2021, 15:30:43) \n[GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]', encoding_fs_io=utf-8-utf-8) [INFO builtin:69]
122 accepted PythonInfo(spec=CPython3.9.7.final.0-64, system=/home/username/.pyenv/versions/3.9.7/bin/python3.9, exe=/home/username/.pyenv/versions/pyb-3.9.7/bin/python, platform=linux, version='3.9.7 (default, Aug 31 2021, 15:30:43) \n[GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]', encoding_fs_io=utf-8-utf-8) [DEBUG builtin:71]
151 create virtual environment via CPython3Posix(dest=/home/username/src/pybuilder/test-env, clear=False, no_vcs_ignore=False, global=False) [INFO session:52]
152 write /home/username/src/pybuilder/test-env/pyvenv.cfg [DEBUG pyenv_cfg:34]
152     home = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
152     implementation = CPython [DEBUG pyenv_cfg:38]
152     version_info = 3.9.7.final.0 [DEBUG pyenv_cfg:38]
152     virtualenv = 20.7.2 [DEBUG pyenv_cfg:38]
152     include-system-site-packages = false [DEBUG pyenv_cfg:38]
152     base-prefix = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
152     base-exec-prefix = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
152     base-executable = /home/username/.pyenv/versions/3.9.7/bin/python3.9 [DEBUG pyenv_cfg:38]
152 remove file /home/username/src/pybuilder/test-env/bin/python [DEBUG _sync:38]
152 symlink /home/username/.pyenv/versions/3.9.7/bin/python3.9 to /home/username/src/pybuilder/test-env/bin/python [DEBUG _sync:44]
152 create virtualenv import hook file /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/_virtualenv.pth [DEBUG api:95]
153 create /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/_virtualenv.py [DEBUG api:98]
153 ============================== target debug ============================== [DEBUG session:54]
153 debug via /home/username/src/pybuilder/test-env/bin/python /home/username/.pyenv/versions/pyb-3.9.7/lib/python3.9/site-packages/virtualenv/create/debug.py [DEBUG creator:224]
153 {
  "sys": {
    "executable": "/home/username/src/pybuilder/test-env/bin/python",
    "_base_executable": "/home/username/src/pybuilder/test-env/bin/python",
    "prefix": "/home/username/src/pybuilder/test-env",
    "base_prefix": "/home/username/.pyenv/versions/3.9.7",
    "real_prefix": null,
    "exec_prefix": "/home/username/src/pybuilder/test-env",
    "base_exec_prefix": "/home/username/.pyenv/versions/3.9.7",
    "path": [
      "/home/username/.pyenv/versions/3.9.7/lib/python39.zip",
      "/home/username/.pyenv/versions/3.9.7/lib/python3.9",
      "/home/username/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload",
      "/home/username/src/pybuilder/test-env/lib/python3.9/site-packages"
    ],
    "meta_path": [
      "<class '_virtualenv._Finder'>",
      "<class '_frozen_importlib.BuiltinImporter'>",
      "<class '_frozen_importlib.FrozenImporter'>",
      "<class '_frozen_importlib_external.PathFinder'>"
    ],
    "fs_encoding": "utf-8",
    "io_encoding": "utf-8"
  },
  "version": "3.9.7 (default, Aug 31 2021, 15:30:43) \n[GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]",
  "makefile_filename": "/home/username/.pyenv/versions/3.9.7/lib/python3.9/config-3.9-x86_64-linux-gnu/Makefile",
  "os": "<module 'os' from '/home/username/.pyenv/versions/3.9.7/lib/python3.9/os.py'>",
  "site": "<module 'site' from '/home/username/.pyenv/versions/3.9.7/lib/python3.9/site.py'>",
  "datetime": "<module 'datetime' from '/home/username/.pyenv/versions/3.9.7/lib/python3.9/datetime.py'>",
  "math": "<module 'math' from '/home/username/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload/math.cpython-39-x86_64-linux-gnu.so'>",
  "json": "<module 'json' from '/home/username/.pyenv/versions/3.9.7/lib/python3.9/json/__init__.py'>"
} [DEBUG session:55]
183 add seed packages via FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/username/.local/share/virtualenv) [INFO session:59]
186 got embed update of distribution setuptools from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/setuptools.json [DEBUG via_disk_folder:135]
186 got embed update of distribution pip from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/pip.json [DEBUG via_disk_folder:135]
186 got embed update of distribution wheel from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/wheel.json [DEBUG via_disk_folder:135]
189 got embed update of distribution setuptools from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/setuptools.json [DEBUG via_disk_folder:135]
189 got embed update of distribution pip from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/pip.json [DEBUG via_disk_folder:135]
189 using periodically updated wheel /home/username/.local/share/virtualenv/wheel/house/pip-21.2.4-py3-none-any.whl [DEBUG periodic_update:53]
189 got embed update of distribution wheel from /home/username/.local/share/virtualenv/wheel/3.9/embed/1/wheel.json [DEBUG via_disk_folder:135]
190 install setuptools from wheel /home/username/.pyenv/versions/pyb-3.9.7/lib/python3.9/site-packages/virtualenv/seed/wheels/embed/setuptools-57.4.0-py3-none-any.whl via CopyPipInstall [DEBUG via_app_data:49]
190 install pip from wheel /home/username/.local/share/virtualenv/wheel/house/pip-21.2.4-py3-none-any.whl via CopyPipInstall [DEBUG via_app_data:49]
190 install wheel from wheel /home/username/.pyenv/versions/pyb-3.9.7/lib/python3.9/site-packages/virtualenv/seed/wheels/embed/wheel-0.37.0-py2.py3-none-any.whl via CopyPipInstall [DEBUG via_app_data:49]
190 Attempting to acquire lock 139925603225552 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any.lock [DEBUG filelock:270]
190 Attempting to acquire lock 139925577568848 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any.lock [DEBUG filelock:270]
191 Attempting to acquire lock 139925603224976 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any.lock [DEBUG filelock:270]
191 Lock 139925603225552 acquired on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any.lock [INFO filelock:274]
191 Lock 139925577568848 acquired on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any.lock [INFO filelock:274]
191 Lock 139925603224976 acquired on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any.lock [INFO filelock:274]
191 Attempting to release lock 139925577568848 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any.lock [DEBUG filelock:315]
191 Lock 139925577568848 released on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any.lock [INFO filelock:318]
191 Attempting to release lock 139925603225552 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any.lock [DEBUG filelock:315]
191 Attempting to release lock 139925603224976 on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any.lock [DEBUG filelock:315]
191 Lock 139925603225552 released on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any.lock [INFO filelock:318]
191 Lock 139925603224976 released on /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any.lock [INFO filelock:318]
192 copy /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/distutils-precedence.pth to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/distutils-precedence.pth [DEBUG _sync:52]
192 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/_distutils_hack to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/_distutils_hack [DEBUG _sync:52]
193 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any/wheel to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/wheel [DEBUG _sync:52]
196 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/pkg_resources to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/pkg_resources [DEBUG _sync:52]
197 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any/wheel-0.37.0.dist-info to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/wheel-0.37.0.dist-info [DEBUG _sync:52]
199 copy /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/wheel-0.37.0-py2.py3-none-any/wheel-0.37.0.virtualenv to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/wheel-0.37.0.virtualenv [DEBUG _sync:52]
201 generated console scripts wheel3 wheel3.9 wheel-3.9 wheel [DEBUG base:48]
207 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/setuptools to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/setuptools [DEBUG _sync:52]
218 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any/pip to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/pip [DEBUG _sync:52]
225 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/setuptools-57.4.0.dist-info to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/setuptools-57.4.0.dist-info [DEBUG _sync:52]
226 copy /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/setuptools-57.4.0-py3-none-any/setuptools-57.4.0.virtualenv to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/setuptools-57.4.0.virtualenv [DEBUG _sync:52]
227 generated console scripts  [DEBUG base:48]
256 copy directory /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any/pip-21.2.4.dist-info to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/pip-21.2.4.dist-info [DEBUG _sync:52]
257 copy /home/username/.local/share/virtualenv/wheel/3.9/image/1/CopyPipInstall/pip-21.2.4-py3-none-any/pip-21.2.4.virtualenv to /home/username/src/pybuilder/test-env/lib/python3.9/site-packages/pip-21.2.4.virtualenv [DEBUG _sync:52]
258 generated console scripts pip-3.9 pip pip3 pip3.9 [DEBUG base:48]
258 add activators for Bash, CShell, Fish, PowerShell, Python [INFO session:64]
259 write /home/username/src/pybuilder/test-env/pyvenv.cfg [DEBUG pyenv_cfg:34]
259     home = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
259     implementation = CPython [DEBUG pyenv_cfg:38]
259     version_info = 3.9.7.final.0 [DEBUG pyenv_cfg:38]
259     virtualenv = 20.7.2 [DEBUG pyenv_cfg:38]
259     include-system-site-packages = false [DEBUG pyenv_cfg:38]
259     base-prefix = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
259     base-exec-prefix = /home/username/.pyenv/versions/3.9.7 [DEBUG pyenv_cfg:38]
259     base-executable = /home/username/.pyenv/versions/3.9.7/bin/python3.9 [DEBUG pyenv_cfg:38]
259 created virtual environment CPython3.9.7.final.0-64 in 149ms
  creator CPython3Posix(dest=/home/username/src/pybuilder/test-env, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/username/.local/share/virtualenv)
    added seed packages: pip==21.2.4, setuptools==57.4.0, setuptools==58.0.4, wheel==0.37.0
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator [WARNING __main__:19]
```
PR to fix it welcome. Otherwise be warned there's no one else picking this up and working on it 👍
LOL I am looking at it. Is there a staffing shortage or just low priority?

This is an open source project, there's no staff. I'm the only maintainer, and I do that role mostly in my free time for free. As this feels like an edge that impacts few people it's very low on my priority list.
Yes, I'm familiar with how OSS works :smile: , I just didn't realize a project under PyPA only had one maintainer. Thanks!
It's a complex project with lots of hard problems that don't get noticed until someone runs into them. We're always looking for new contributors (and eventually maintainer) if you have time to work on issues.

Resolving patch:
diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py
--- a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py
+++ b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py
@@ -5,6 +5,7 @@
 import re
 import zipfile
 from abc import ABCMeta, abstractmethod
+from itertools import chain
 from tempfile import mkdtemp
 
 from distlib.scripts import ScriptMaker, enquote_executable
@@ -31,14 +32,10 @@ def _sync(self, src, dst):
 
     def install(self, version_info):
         self._extracted = True
+        self._uninstall_previous_version()
         # sync image
         for filename in self._image_dir.iterdir():
             into = self._creator.purelib / filename.name
-            if into.exists():
-                if into.is_dir() and not into.is_symlink():
-                    safe_delete(into)
-                else:
-                    into.unlink()
             self._sync(filename, into)
         # generate console executables
         consoles = set()
@@ -150,6 +147,36 @@ def _create_console_entry_point(self, name, value, to_folder, version_info):
         result.extend(Path(i) for i in new_files)
         return result
 
+    def _uninstall_previous_version(self):
+        dist_name = self._dist_info.stem.split("-")[0]
+        in_folders = chain.from_iterable([i.iterdir() for i in {self._creator.purelib, self._creator.platlib}])
+        paths = (p for p in in_folders if p.stem.split("-")[0] == dist_name and p.suffix == ".dist-info" and p.is_dir())
+        existing_dist = next(paths, None)
+        if existing_dist is not None:
+            self._uninstall_dist(existing_dist)
+
+    @staticmethod
+    def _uninstall_dist(dist):
+        dist_base = dist.parent
+        logging.debug("uninstall existing distribution %s from %s", dist.stem, dist_base)
+
+        top_txt = dist / "top_level.txt"  # add top level packages at folder level
+        paths = {dist.parent / i.strip() for i in top_txt.read_text().splitlines()} if top_txt.exists() else set()
+        paths.add(dist)  # add the dist-info folder itself
+
+        base_dirs, record = paths.copy(), dist / "RECORD"  # collect entries in record that we did not register yet
+        for name in (i.split(",")[0] for i in record.read_text().splitlines()) if record.exists() else ():
+            path = dist_base / name
+            if not any(p in base_dirs for p in path.parents):  # only add if not already added as a base dir
+                paths.add(path)
+
+        for path in sorted(paths):  # actually remove stuff in a stable order
+            if path.exists():
+                if path.is_dir() and not path.is_symlink():
+                    safe_delete(path)
+                else:
+                    path.unlink()
+
     def clear(self):
         if self._image_dir.exists():
             safe_delete(self._image_dir)


Test patch:
diff --git a/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py b/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py
--- a/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py
+++ b/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py
@@ -52,7 +52,7 @@ def test_seed_link_via_app_data(tmp_path, coverage_env, current_fastest, copies)
     pip = site_package / "pip"
     setuptools = site_package / "setuptools"
 
-    files_post_first_create = list(site_package.iterdir())
+    files_post_first_create = set(site_package.iterdir())
     assert pip in files_post_first_create
     assert setuptools in files_post_first_create
     for pip_exe in [
@@ -82,15 +82,25 @@ def test_seed_link_via_app_data(tmp_path, coverage_env, current_fastest, copies)
     assert not process.returncode
     assert site_package.exists()
 
-    files_post_first_uninstall = list(site_package.iterdir())
+    files_post_first_uninstall = set(site_package.iterdir())
     assert pip in files_post_first_uninstall
     assert setuptools not in files_post_first_uninstall
 
+    # install a different setuptools to test that virtualenv removes this before installing new
+    version = "setuptools<{}".format(bundle_ver["setuptools"].split("-")[1])
+    install_cmd = [str(result.creator.script("pip")), "--verbose", "--disable-pip-version-check", "install", version]
+    process = Popen(install_cmd)
+    process.communicate()
+    assert not process.returncode
+    assert site_package.exists()
+    files_post_downgrade = set(site_package.iterdir())
+    assert setuptools in files_post_downgrade
+
     # check we can run it again and will work - checks both overwrite and reuse cache
     result = cli_run(create_cmd)
     coverage_env()
     assert result
-    files_post_second_create = list(site_package.iterdir())
+    files_post_second_create = set(site_package.iterdir())
     assert files_post_first_create == files_post_second_create
 
     # Windows does not allow removing a executable while running it, so when uninstalling pip we need to do it via