Repository: pypa/virtualenv

Issue:
Scripts and bin in bad location
Reporting the same bug here on MacOS, only I'm highlighting the old vs new-unexpected behavior in a (hopefully) easy reproduction case:

```
./.test-venv/bin/pip list | grep virtualenv
virtualenv         16.7.9
./.test-venv/bin/python -m virtualenv .mac-os-venv
Using base prefix '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7'
New python executable in /Users/aegelhofer/.mac-os-venv/bin/python
Installing setuptools, pip, wheel...
done.
ls ~/.mac-os-venv/bin/*
/Users/aegelhofer/.mac-os-venv/bin/activate		/Users/aegelhofer/.mac-os-venv/bin/pip
/Users/aegelhofer/.mac-os-venv/bin/activate.csh		/Users/aegelhofer/.mac-os-venv/bin/pip3
/Users/aegelhofer/.mac-os-venv/bin/activate.fish	/Users/aegelhofer/.mac-os-venv/bin/pip3.7
/Users/aegelhofer/.mac-os-venv/bin/activate.ps1		/Users/aegelhofer/.mac-os-venv/bin/python
/Users/aegelhofer/.mac-os-venv/bin/activate.xsh		/Users/aegelhofer/.mac-os-venv/bin/python-config
/Users/aegelhofer/.mac-os-venv/bin/activate_this.py	/Users/aegelhofer/.mac-os-venv/bin/python3
/Users/aegelhofer/.mac-os-venv/bin/easy_install		/Users/aegelhofer/.mac-os-venv/bin/python3.7
/Users/aegelhofer/.mac-os-venv/bin/easy_install-3.7	/Users/aegelhofer/.mac-os-venv/bin/wheel
rm -rf .mac-os-venv
./.test-venv/bin/pip install -U virtualenv
Looking in indexes:
 ...
Requirement already satisfied, skipping upgrade: six<2,>=1.9.0 in ./.test-venv/lib/python3.7/site-packages (from virtualenv) (1.14.0)
Requirement already satisfied, skipping upgrade: importlib-metadata<2,>=0.12; python_version < "3.8" in ./.test-venv/lib/python3.7/site-packages (from virtualenv) (1.5.0)
Requirement already satisfied, skipping upgrade: filelock<4,>=3.0.0 in ./.test-venv/lib/python3.7/site-packages (from virtualenv) (3.0.12)
Requirement already satisfied, skipping upgrade: appdirs<2,>=1.4.3 in ./.test-venv/lib/python3.7/site-packages (from virtualenv) (1.4.3)
Requirement already satisfied, skipping upgrade: distlib<1,>=0.3.0 in ./.test-venv/lib/python3.7/site-packages (from virtualenv) (0.3.0)
Requirement already satisfied, skipping upgrade: zipp>=0.5 in ./.test-venv/lib/python3.7/site-packages (from importlib-metadata<2,>=0.12; python_version < "3.8"->virtualenv) (2.2.0)
Installing collected packages: virtualenv
  Found existing installation: virtualenv 16.7.9
    Uninstalling virtualenv-16.7.9:
      Successfully uninstalled virtualenv-16.7.9
Successfully installed virtualenv-20.0.4
./.test-venv/bin/python -m virtualenv .mac-os-venv
created virtual environment in 169ms CPython3Posix(dest=/Users/aegelhofer/.mac-os-venv, clear=False, global=False) with seeder FromAppData pip=latest setuptools=latest wheel=latest app_data_dir=/Users/aegelhofer/Library/Application Support/virtualenv/seed-v1 via=copy
```

Now at this point I expect the python and pip "binaries" to be under `~/.mac-os-venv/bin/*`, but they now seem to be under `~/.mac-os-venv/<sourced-prefix>/bin/*`:

```
ls ~/.mac-os-venv/bin/*
zsh: no matches found: /Users/aegelhofer/.mac-os-venv/bin/*
ls ~/.mac-os-venv/usr/local/bin/*
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate		/Users/aegelhofer/.mac-os-venv/usr/local/bin/pip
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate.csh	/Users/aegelhofer/.mac-os-venv/usr/local/bin/pip-3.7
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate.fish	/Users/aegelhofer/.mac-os-venv/usr/local/bin/pip3
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate.ps1	/Users/aegelhofer/.mac-os-venv/usr/local/bin/python
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate.xsh	/Users/aegelhofer/.mac-os-venv/usr/local/bin/python3
/Users/aegelhofer/.mac-os-venv/usr/local/bin/activate_this.py	/Users/aegelhofer/.mac-os-venv/usr/local/bin/python3.7
/Users/aegelhofer/.mac-os-venv/usr/local/bin/easy_install	/Users/aegelhofer/.mac-os-venv/usr/local/bin/wheel
/Users/aegelhofer/.mac-os-venv/usr/local/bin/easy_install-3.7	/Users/aegelhofer/.mac-os-venv/usr/local/bin/wheel-3.7
/Users/aegelhofer/.mac-os-venv/usr/local/bin/easy_install3	/Users/aegelhofer/.mac-os-venv/usr/local/bin/wheel3
```

This behavior change messes with tools like `tox` that expect to have the `pip` binary under `<ENV-ROOT>/bin` - Reading the changelog I didn't see anything mentioning this behavior change

If this is un-related, I can file a separate bug, but it seems suspect that we're getting hung up on the same issues relating to "prefixes" here. Attached is my debug output of `virtualenv-20.0.4` command creating a virtualenv:

[virtualenv-debug-output-issue-1632.txt](https://github.com/pypa/virtualenv/files/4206940/virtualenv-debug-output-issue-1632.txt)

_Originally posted by @andrewegel in https://github.com/pypa/virtualenv/issues/1632#issuecomment-586485362_

PS. moved here as it's a new issue.

Discussion:

Patch:
diff --git a/docs/changelog/1663.bugfix.rst b/docs/changelog/1663.bugfix.rst
new file mode 100644
index 00000000..f162936a
--- /dev/null
+++ b/docs/changelog/1663.bugfix.rst
@@ -0,0 +1,2 @@
+Fix acquiring python information might be altered by distutils configuration files generating incorrect layout virtual
+environments - by :user:`gaborbernat`.
diff --git a/docs/changelog/1702.bugfix.rst b/docs/changelog/1702.bugfix.rst
index dd1dab2a..4e1c97c9 100644
--- a/docs/changelog/1702.bugfix.rst
+++ b/docs/changelog/1702.bugfix.rst
@@ -1,1 +1,1 @@
-Upgrade embedded setuptools to ``46.0.0`` from ``45.3.0`` on Python ``3.5+``.
+Upgrade embedded setuptools to ``46.0.0`` from ``45.3.0`` on Python ``3.5+`` - by :user:`gaborbernat`.
diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py
index dd314761..5a1f76a0 100644
--- a/src/virtualenv/create/via_global_ref/_virtualenv.py
+++ b/src/virtualenv/create/via_global_ref/_virtualenv.py
@@ -23,11 +23,10 @@ def patch_dist(dist):
 
         if "prefix" in install:  # the prefix governs where to install the libraries
             install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
-
-        if "install_scripts" in install:  # the install_scripts governs where to generate console scripts
-            script_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "__SCRIPT_DIR__"))
-            install["install_scripts"] = VIRTUALENV_PATCH_FILE, script_path
-
+        for base in ("purelib", "platlib", "headers", "scripts", "data"):
+            key = "install_{}".format(base)
+            if key in install:  # do not allow global configs to hijack venv paths
+                install.pop(key, None)
         return result
 
     dist.Distribution.parse_config_files = parse_config_files
diff --git a/src/virtualenv/discovery/py_info.py b/src/virtualenv/discovery/py_info.py
index 894082ce..fae91bf9 100644
--- a/src/virtualenv/discovery/py_info.py
+++ b/src/virtualenv/discovery/py_info.py
@@ -111,10 +111,9 @@ class PythonInfo(object):
     @staticmethod
     def _distutils_install():
         # follow https://github.com/pypa/pip/blob/master/src/pip/_internal/locations.py#L95
-        d = Distribution({"script_args": "--no-user-cfg"})
-        d.parse_config_files()
+        d = Distribution({"script_args": "--no-user-cfg"})  # configuration files not parsed so they do not hijack paths
         i = d.get_command_obj("install", create=True)
-        i.prefix = "a"
+        i.prefix = os.sep  # paths generated are relative to prefix that contains the path sep, this makes it relative
         i.finalize_options()
         result = {key: (getattr(i, "install_{}".format(key))[1:]).lstrip(os.sep) for key in SCHEME_KEYS}
         return result


Test patch:
diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py
index bf2de472..70e0e6a8 100644
--- a/tests/unit/create/test_creator.py
+++ b/tests/unit/create/test_creator.py
@@ -369,11 +369,17 @@ def test_create_distutils_cfg(creator, tmp_path, monkeypatch):
     setup_cfg = dest / "setup.cfg"
     conf = dedent(
         """
-    [install]
-    prefix={}/a
-    install_scripts={}/b
-    """
-    ).format(tmp_path, tmp_path)
+            [install]
+            prefix={0}{1}prefix
+            install_purelib={0}{1}purelib
+            install_platlib={0}{1}platlib
+            install_headers={0}{1}headers
+            install_scripts={0}{1}scripts
+            install_data={0}{1}data
+            """.format(
+            tmp_path, os.sep
+        )
+    )
     setup_cfg.write_text(setup_cfg.read_text() + conf)
 
     monkeypatch.chdir(dest)  # distutils will read the setup.cfg from the cwd, so change to that
diff --git a/tests/unit/discovery/py_info/test_py_info.py b/tests/unit/discovery/py_info/test_py_info.py
index 11b52c70..ad9d399e 100644
--- a/tests/unit/discovery/py_info/test_py_info.py
+++ b/tests/unit/discovery/py_info/test_py_info.py
@@ -4,8 +4,10 @@ import copy
 import itertools
 import json
 import logging
+import os
 import sys
 from collections import namedtuple
+from textwrap import dedent
 
 import pytest
 
@@ -237,3 +239,26 @@ def test_system_executable_no_exact_match(target, discovered, position, tmp_path
     warn_similar = caplog.records[-1]
     assert warn_similar.levelno == logging.DEBUG
     assert warn_similar.msg.startswith("no exact match found, chosen most similar")
+
+
+def test_py_info_ignores_distutils_config(monkeypatch, tmp_path):
+    (tmp_path / "setup.cfg").write_text(
+        dedent(
+            """
+            [install]
+            prefix={0}{1}prefix
+            install_purelib={0}{1}purelib
+            install_platlib={0}{1}platlib
+            install_headers={0}{1}headers
+            install_scripts={0}{1}scripts
+            install_data={0}{1}data
+            """.format(
+                tmp_path, os.sep
+            )
+        )
+    )
+    monkeypatch.chdir(tmp_path)
+    py_info = PythonInfo.from_exe(sys.executable)
+    distutils = py_info.distutils_install
+    for key, value in distutils.items():
+        assert not value.startswith(str(tmp_path)), "{}={}".format(key, value)
diff --git a/tests/unit/discovery/py_info/test_py_info_exe_based_of.py b/tests/unit/discovery/py_info/test_py_info_exe_based_of.py
index 3f88dd58..9b87be02 100644
--- a/tests/unit/discovery/py_info/test_py_info_exe_based_of.py
+++ b/tests/unit/discovery/py_info/test_py_info_exe_based_of.py
@@ -7,7 +7,6 @@ import pytest
 
 from virtualenv.discovery.py_info import EXTENSIONS, PythonInfo
 from virtualenv.info import IS_WIN, fs_is_case_sensitive, fs_supports_symlink
-from virtualenv.util.path import Path
 
 CURRENT = PythonInfo.current()
 
@@ -17,7 +16,7 @@ def test_discover_empty_folder(tmp_path, monkeypatch, session_app_data):
         CURRENT.discover_exe(session_app_data, prefix=str(tmp_path))
 
 
-BASE = {str(Path(CURRENT.executable).parent.relative_to(Path(CURRENT.prefix))), "."}
+BASE = {CURRENT.distutils_install["scripts"], "."}
 
 
 @pytest.mark.skipif(not fs_supports_symlink(), reason="symlink is not supported")