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:
@andrewegel can you validate if this is still an issue with ``virtualenv 20.0.5``?
@arlaneenalra can you validate this no longer happens with master
I don't think this should be still the case, but if so we'll reopen.
This is definitely still happening:

```
% pip3 install -U virtualenv
Looking in indexes: https://<sanitized>.com/repository/pypi/simple
Collecting virtualenv
  Downloading https://<sanitized>.com/repository/pypi/files.pythonhosted.org/https/packages/33/4e/9f3e70ed2bf3de603bf4de46cb60af1309c8d97fd81cbde317507a57573e/virtualenv-20.0.5-py2.py3-none-any.whl (4.6 MB)
     |████████████████████████████████| 4.6 MB 2.0 MB/s
Requirement already satisfied, skipping upgrade: appdirs<2,>=1.4.3 in /usr/local/lib/python3.6/site-packages (from virtualenv) (1.4.3)
Requirement already satisfied, skipping upgrade: importlib-metadata<2,>=0.12; python_version < "3.8" in /usr/local/lib/python3.6/site-packages (from virtualenv) (1.5.0)
Requirement already satisfied, skipping upgrade: six<2,>=1.9.0 in /usr/local/lib/python3.6/site-packages (from virtualenv) (1.13.0)
Requirement already satisfied, skipping upgrade: filelock<4,>=3.0.0 in /usr/local/lib/python3.6/site-packages (from virtualenv) (3.0.12)
Requirement already satisfied, skipping upgrade: importlib-resources<2,>=1.0; python_version < "3.7" in /usr/local/lib/python3.6/site-packages (from virtualenv) (1.0.2)
Requirement already satisfied, skipping upgrade: distlib<1,>=0.3.0 in /usr/local/lib/python3.6/site-packages (from virtualenv) (0.3.0)
Requirement already satisfied, skipping upgrade: zipp>=0.5 in /usr/local/lib/python3.6/site-packages (from importlib-metadata<2,>=0.12; python_version < "3.8"->virtualenv) (2.2.0)
Installing collected packages: virtualenv
  Attempting uninstall: virtualenv
    Found existing installation: virtualenv 16.7.9
    Uninstalling virtualenv-16.7.9:
      Successfully uninstalled virtualenv-16.7.9
Successfully installed virtualenv-20.0.5
 % virtualenv --python python3 venv
created virtual environment CPython3.6.8.final.0-64 in 556ms
  creator CPython3Posix(dest=/Users/csalch/test/venv, clear=False, global=False)
  seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/csalch/Library/Application Support/virtualenv/seed-v1)
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
 % cd venv
% ls
lib  pyvenv.cfg  usr
 % ls -l
total 4
drwxr-xr-x 3 csalch admin  96 Feb 25 10:57 lib
-rw-r--r-- 1 csalch admin 451 Feb 25 10:57 pyvenv.cfg
drwxr-xr-x 3 csalch admin  96 Feb 25 10:57 usr
 % tree
.
├── lib
│   └── python3.6
│       └── site-packages
... clip ...
├── pyvenv.cfg
└── usr
    └── local
        └── opt
            └── python@3.6.8
                └── bin
                    ├── activate
                    ├── activate.csh
                    ├── activate.fish
                    ├── activate.ps1
                    ├── activate.xsh
                    ├── activate_this.py
                    ├── easy_install
                    ├── easy_install-3.6
                    ├── easy_install3
                    ├── pip
                    ├── pip-3.6
                    ├── pip3
                    ├── python -> /usr/local/opt/python@3.6.8/bin/python3.6
                    ├── python3 -> python
                    ├── python3.6 -> python
                    ├── wheel
                    ├── wheel-3.6
                    └── wheel3

67 directories, 507 files
% virtualenv --version
virtualenv 20.0.5 from /usr/local/lib/python3.6/site-packages/virtualenv/__init__.py
%
```
@gaborbernat This is not fixed with `20.0.5`:

```
./.test-venv/bin/pip list | grep vir
virtualenv         20.0.5
./.test-venv/bin/python -m virtualenv .mac-os-venv
created virtual environment CPython3.7.3.final.0-64 in 178ms
  creator CPython3Posix(dest=/Users/aegelhofer/.mac-os-venv, clear=False, global=False)
  seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/aegelhofer/Library/Application Support/virtualenv/seed-v1)
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
ls ~/.mac-os-venv/
Library		lib		pyvenv.cfg	usr
```
What about master?
No joy in 20.0.7 either.

Can you test ``20.0.6`` we had to revart the fix due to https://github.com/pypa/pip/issues/7778
No joy with 20.0.6 as well. We have a distutils.cfg file present that looks like:
```
[install]
prefix=/usr/local
install_scripts=/usr/local/opt/python@3.6.8/bin

[build_ext]
include_dirs=/usr/local/include:/usr/local/opt/openssl@1.1/include:/usr/local/opt/sqlite/include
library_dirs=/usr/local/lib:/usr/local/opt/openssl@1.1/lib:/usr/local/opt/sqlite/lib
```

It lives in `<python install root>/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/distutils.cfg` 

It look like that config is leaking into virtualenv somehow ... 
Will give a try with that config and report back next week 👍
Hello, a fix for this issue has been released via virtualenv 20.0.8; see https://pypi.org/project/virtualenv/20.0.8/ (https://virtualenv.pypa.io/en/latest/changelog.html#v20-0-8-2020-03-04). Please give a try and report back if your issue has not been addressed; if not, please comment here, and we'll reopen the ticket. We want to apologize for the inconvenience this has caused you and say thanks for having patience while we resolve the unexpected bugs with this new major release.
![thanks](https://media.tenor.co/images/1114067881329bb4218880eeb7ec34fd/tenor.gif)
This is still not fixed in `20.0.8`

```
% ./.test-venv/bin/pip list| grep virt
virtualenv         20.0.8
% ./.test-venv/bin/python -m virtualenv .mac-os-venv
created virtual environment CPython3.7.3.final.0-64 in 182ms
  creator CPython3Posix(dest=/Users/aegelhofer/.mac-os-venv, clear=False, global=False)
  seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/aegelhofer/Library/Application Support/virtualenv/seed-app-data/v1)
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
% ls .mac-os-venv
Library		lib		pyvenv.cfg	usr
```
Can you provide detailed instructions to reproduce? Ideally a docker image, otherwise Python used, creation log, tree of the created virtual environment, etc 👍 also what configuration files you've used for distutils 
Closing until someone can reproduce this. I fail to do so on the environments accessible to me, so it's unactionable at the moment. If someone can create a reproducible example we'll reopen.
Please re-open. 

> Ideally a docker image

AFAIK MacOS doesn't have a "docker image"

> Can you provide detailed instructions to reproduce?

So, MacOS Catalina 10.15.3 came with a stock /usr/bin/python3 (version 3.7.3) - It also came with what looks like a "light weight" virtual env:

```
aegelhofer@Andrew-Egelhofer's- docs % /usr/bin/python3 -m venv --help
usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear]
            [--upgrade] [--without-pip] [--prompt PROMPT]
            ENV_DIR [ENV_DIR ...]

Creates virtual Python environments in one or more target directories.

positional arguments:
  ENV_DIR               A directory to create the environment in.

optional arguments:
  -h, --help            show this help message and exit
  --system-site-packages
                        Give the virtual environment access to the system
                        site-packages dir.
  --symlinks            Try to use symlinks rather than copies, when symlinks
                        are not the default for the platform.
  --copies              Try to use copies rather than symlinks, even when
                        symlinks are the default for the platform.
  --clear               Delete the contents of the environment directory if it
                        already exists, before environment creation.
  --upgrade             Upgrade the environment directory to use this version
                        of Python, assuming Python has been upgraded in-place.
  --without-pip         Skips installing or upgrading pip in the virtual
                        environment (pip is bootstrapped by default)
  --prompt PROMPT       Provides an alternative prompt prefix for this
                        environment.

Once an environment has been created, you may wish to activate it, e.g. by
sourcing an activate script in its bin directory.
```

*This* virtualenv doesn't have this issue. MacOS also will no longer allow edits under `/usr/lib` (or `/Library/`), so I have to generate a "user" pip install:

```
aegelhofer@Andrew-Egelhofer's- ~ % /usr/bin/pip3 install --user virtualenv
Collecting virtualenv
  Downloading https://files.pythonhosted.org/packages/c8/96/0d929b45d4c0cfbb81fecb41c4ea5b48ea8834b9184e1fef4a7871b01eb1/virtualenv-20.0.9-py2.py3-none-any.whl (4.6MB)
    100% |████████████████████████████████| 4.6MB 7.4MB/s
Collecting importlib-metadata<2,>=0.12; python_version < "3.8" (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/8b/03/a00d504808808912751e64ccf414be53c29cad620e3de2421135fcae3025/importlib_metadata-1.5.0-py2.py3-none-any.whl
Collecting filelock<4,>=3.0.0 (from virtualenv)
  Downloading https://files.pythonhosted.org/packages/93/83/71a2ee6158bb9f39a90c0dea1637f81d5eef866e188e1971a1b1ab01a35a/filelock-3.0.12-py3-none-any.whl
Collecting distlib<1,>=0.3.0 (from virtualenv)
  Downloading https://files.pythonhosted.org/packages/7d/29/694a3a4d7c0e1aef76092e9167fbe372e0f7da055f5dcf4e1313ec21d96a/distlib-0.3.0.zip (571kB)
    100% |████████████████████████████████| 573kB 11.1MB/s
Collecting appdirs<2,>=1.4.3 (from virtualenv)
  Downloading https://files.pythonhosted.org/packages/56/eb/810e700ed1349edde4cbdc1b2a21e28cdf115f9faf263f6bbf8447c1abf3/appdirs-1.4.3-py2.py3-none-any.whl
Requirement already satisfied: six<2,>=1.9.0 in /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages (from virtualenv) (1.12.0)
Collecting zipp>=0.5 (from importlib-metadata<2,>=0.12; python_version < "3.8"->virtualenv)
  Using cached https://files.pythonhosted.org/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Building wheels for collected packages: distlib
  Building wheel for distlib (setup.py) ... done
  Stored in directory: /Users/aegelhofer/Library/Caches/pip/wheels/6e/e8/db/c73dae4867666e89ba3cfbc4b5c092446f0e584eda6f409cbb
Successfully built distlib
Installing collected packages: zipp, importlib-metadata, filelock, distlib, appdirs, virtualenv
  The script virtualenv is installed in '/Users/aegelhofer/Library/Python/3.7/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed appdirs-1.4.3 distlib-0.3.0 filelock-3.0.12 importlib-metadata-1.5.0 virtualenv-20.0.9 zipp-3.1.0
You are using pip version 19.0.3, however version 20.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
aegelhofer@Andrew-Egelhofer's- ~ % /usr/bin/python3 -m virtualenv --version
virtualenv 20.0.9 from /Users/aegelhofer/Library/Python/3.7/lib/python/site-packages/virtualenv/__init__.py
```

Now I create it:

```
aegelhofer@Andrew-Egelhofer's- ~ % /usr/bin/python3 -m virtualenv -vvvvvvvvv ~/.mac-os-venv
```

Debug Log is here: [virtualenv-macos-20.0.9-debug-output.txt](https://github.com/pypa/virtualenv/files/4308134/virtualenv-macos-20.0.9-debug-output.txt)

`find` output of `~/.mac-os-venv` is here:

[virtualenv-macos-20.0.9-find-output.txt](https://github.com/pypa/virtualenv/files/4308139/virtualenv-macos-20.0.9-find-output.txt)

> also what configuration files you've used for distutils

This is stock configuration from Apple, so AFAIK, I'm not doing anything different, so I'm attaching what I think is what you're looking for:

[virtualenv-macos-20.0.9-distlib-sysconfig.txt](https://github.com/pypa/virtualenv/files/4308166/virtualenv-macos-20.0.9-distlib-sysconfig.txt)

@andrewegel where does virtualenv-macos-20.0.9-distlib-sysconfig.txt come from? Path?
I have Catalina and don't have that python install. How does one install on Catalina to be able to reproduce? 
> @andrewegel where does virtualenv-macos-20.0.9-distlib-sysconfig.txt come from? Path?

/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg

> I have Catalina and don't have that python install. How does one install on Catalina to be able to reproduce?

This came pre-installed with the laptop that was provisioned to me by my employer, so I don't have a definitive answer, I think it was one of the `Command Line Tools` you can obtain from Apple:

https://developer.apple.com/download/more/

I've tried downloading one, installing and testing, but that didn't seem to put anything at /use/bin/python3 or anything - so I guess we have to chock this up to w/e environment I'm running, and is therefore now a wide-spread bug. 

If I find time to dig deep into this issue, I'll send a PR with a fix, but no promises. 
Here's a  small repo that reproduces this issue in a linux based docker container:

https://github.com/arlaneenalra/broken-virtualenv-example

With  short README containing instructions on how to duplicate the issue.

This is definitely still a problem with 20.0.9

Resolving patch:
diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py
--- a/src/virtualenv/create/via_global_ref/_virtualenv.py
+++ b/src/virtualenv/create/via_global_ref/_virtualenv.py
@@ -23,11 +23,10 @@ def parse_config_files(self, *args, **kwargs):
 
         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
--- a/src/virtualenv/discovery/py_info.py
+++ b/src/virtualenv/discovery/py_info.py
@@ -111,10 +111,9 @@ def _fast_get_system_executable(self):
     @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
--- 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
--- 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 itertools
 import json
 import logging
+import os
 import sys
 from collections import namedtuple
+from textwrap import dedent
 
 import pytest
 
@@ -237,3 +239,26 @@ def func(k, app_data, resolve_to_host, raise_on_error):
     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)