We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
piptools depcache is shared between implementations causing incorrect dependencies
<!-- Describe the issue briefly here. -->

#### Environment Versions

```console
$ uname -a
Linux asottile-MacBookPro 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.3 LTS
Release:	18.04
Codename:	bionic
$ /tmp/x/venv36/bin/pip freeze --all
Click==7.0
pip==20.0.2
pip-tools==4.4.0
setuptools==45.1.0
six==1.14.0
wheel==0.34.1
$ /tmp/x/venv36/bin/python --version --version
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0]
$ /tmp/x/venvpp36/bin/python --version --version
Python 3.6.9 (1608da62bfc7, Dec 23 2019, 10:50:04)
[PyPy 7.3.0 with GCC 7.3.1 20180303 (Red Hat 7.3.1-5)]
```

#### Steps to replicate

```bash
set -euxo pipefail

cache="$PWD/cache"
export XDG_CACHE_HOME="$cache"
export CUSTOM_COMPILE_COMMAND='<<redacted>>'

rm -rf "$cache" venv36 venvpp36 reqs*.txt

virtualenv venv36 -ppython3.6 >& /dev/null
venv36/bin/pip install -qq pip-tools
virtualenv venvpp36 -ppypy3 >& /dev/null
venvpp36/bin/pip install -qq pip-tools

echo 'cffi' > requirements.in

venv36/bin/pip-compile --output-file reqs36_1.txt
venvpp36/bin/pip-compile --output-file reqspp36_1.txt

rm -rf "$cache"
venvpp36/bin/pip-compile --output-file reqspp36_2.txt
venv36/bin/pip-compile --output-file reqs36_2.txt

diff -u reqs36_1.txt reqs36_2.txt
diff -u reqspp36_1.txt reqspp36_2.txt
```

#### Expected result

I expect no diff to be produced and the script to exit 0

#### Actual result

```console
$ bash t.sh 
+ cache=/tmp/x/cache
+ export XDG_CACHE_HOME=/tmp/x/cache
+ XDG_CACHE_HOME=/tmp/x/cache
+ export 'CUSTOM_COMPILE_COMMAND=<<redacted>>'
+ CUSTOM_COMPILE_COMMAND='<<redacted>>'
+ rm -rf /tmp/x/cache venv36 venvpp36 reqs36_1.txt reqs36_2.txt reqspp36_1.txt reqspp36_2.txt
+ virtualenv venv36 -ppython3.6
+ venv36/bin/pip install -qq pip-tools
+ virtualenv venvpp36 -ppypy3
+ venvpp36/bin/pip install -qq pip-tools
+ echo cffi
+ venv36/bin/pip-compile --output-file reqs36_1.txt
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    <<redacted>>
#
cffi==1.13.2
pycparser==2.19           # via cffi
+ venvpp36/bin/pip-compile --output-file reqspp36_1.txt
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    <<redacted>>
#
cffi==1.13.2
pycparser==2.19           # via cffi
+ rm -rf /tmp/x/cache
+ venvpp36/bin/pip-compile --output-file reqspp36_2.txt
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    <<redacted>>
#
cffi==1.13.2
+ venv36/bin/pip-compile --output-file reqs36_2.txt
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    <<redacted>>
#
cffi==1.13.2
+ diff -u reqs36_1.txt reqs36_2.txt
--- reqs36_1.txt	2020-01-30 08:46:36.913122945 -0800
+++ reqs36_2.txt	2020-01-30 08:46:41.696946374 -0800
@@ -5,4 +5,3 @@
 #    <<redacted>>
 #
 cffi==1.13.2
-pycparser==2.19           # via cffi
```


as you can see here, the pypy 3.6 cache is poisoning the result of the cpython 3.6 pip-compile

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `piptools/cache.py`
Content:
```
1 # coding: utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import json
5 import os
6 import sys
7 
8 from pip._vendor.packaging.requirements import Requirement
9 
10 from .exceptions import PipToolsError
11 from .utils import as_tuple, key_from_req, lookup_table
12 
13 
14 class CorruptCacheError(PipToolsError):
15     def __init__(self, path):
16         self.path = path
17 
18     def __str__(self):
19         lines = [
20             "The dependency cache seems to have been corrupted.",
21             "Inspect, or delete, the following file:",
22             "  {}".format(self.path),
23         ]
24         return os.linesep.join(lines)
25 
26 
27 def read_cache_file(cache_file_path):
28     with open(cache_file_path, "r") as cache_file:
29         try:
30             doc = json.load(cache_file)
31         except ValueError:
32             raise CorruptCacheError(cache_file_path)
33 
34         # Check version and load the contents
35         if doc["__format__"] != 1:
36             raise AssertionError("Unknown cache file format")
37         return doc["dependencies"]
38 
39 
40 class DependencyCache(object):
41     """
42     Creates a new persistent dependency cache for the current Python version.
43     The cache file is written to the appropriate user cache dir for the
44     current platform, i.e.
45 
46         ~/.cache/pip-tools/depcache-pyX.Y.json
47 
48     Where X.Y indicates the Python version.
49     """
50 
51     def __init__(self, cache_dir):
52         if not os.path.isdir(cache_dir):
53             os.makedirs(cache_dir)
54         py_version = ".".join(str(digit) for digit in sys.version_info[:2])
55         cache_filename = "depcache-py{}.json".format(py_version)
56 
57         self._cache_file = os.path.join(cache_dir, cache_filename)
58         self._cache = None
59 
60     @property
61     def cache(self):
62         """
63         The dictionary that is the actual in-memory cache.  This property
64         lazily loads the cache from disk.
65         """
66         if self._cache is None:
67             self.read_cache()
68         return self._cache
69 
70     def as_cache_key(self, ireq):
71         """
72         Given a requirement, return its cache key. This behavior is a little weird
73         in order to allow backwards compatibility with cache files. For a requirement
74         without extras, this will return, for example:
75 
76         ("ipython", "2.1.0")
77 
78         For a requirement with extras, the extras will be comma-separated and appended
79         to the version, inside brackets, like so:
80 
81         ("ipython", "2.1.0[nbconvert,notebook]")
82         """
83         name, version, extras = as_tuple(ireq)
84         if not extras:
85             extras_string = ""
86         else:
87             extras_string = "[{}]".format(",".join(extras))
88         return name, "{}{}".format(version, extras_string)
89 
90     def read_cache(self):
91         """Reads the cached contents into memory."""
92         if os.path.exists(self._cache_file):
93             self._cache = read_cache_file(self._cache_file)
94         else:
95             self._cache = {}
96 
97     def write_cache(self):
98         """Writes the cache to disk as JSON."""
99         doc = {"__format__": 1, "dependencies": self._cache}
100         with open(self._cache_file, "w") as f:
101             json.dump(doc, f, sort_keys=True)
102 
103     def clear(self):
104         self._cache = {}
105         self.write_cache()
106 
107     def __contains__(self, ireq):
108         pkgname, pkgversion_and_extras = self.as_cache_key(ireq)
109         return pkgversion_and_extras in self.cache.get(pkgname, {})
110 
111     def __getitem__(self, ireq):
112         pkgname, pkgversion_and_extras = self.as_cache_key(ireq)
113         return self.cache[pkgname][pkgversion_and_extras]
114 
115     def __setitem__(self, ireq, values):
116         pkgname, pkgversion_and_extras = self.as_cache_key(ireq)
117         self.cache.setdefault(pkgname, {})
118         self.cache[pkgname][pkgversion_and_extras] = values
119         self.write_cache()
120 
121     def reverse_dependencies(self, ireqs):
122         """
123         Returns a lookup table of reverse dependencies for all the given ireqs.
124 
125         Since this is all static, it only works if the dependency cache
126         contains the complete data, otherwise you end up with a partial view.
127         This is typically no problem if you use this function after the entire
128         dependency tree is resolved.
129         """
130         ireqs_as_cache_values = [self.as_cache_key(ireq) for ireq in ireqs]
131         return self._reverse_dependencies(ireqs_as_cache_values)
132 
133     def _reverse_dependencies(self, cache_keys):
134         """
135         Returns a lookup table of reverse dependencies for all the given cache keys.
136 
137         Example input:
138 
139             [('pep8', '1.5.7'),
140              ('flake8', '2.4.0'),
141              ('mccabe', '0.3'),
142              ('pyflakes', '0.8.1')]
143 
144         Example output:
145 
146             {'pep8': ['flake8'],
147              'flake8': [],
148              'mccabe': ['flake8'],
149              'pyflakes': ['flake8']}
150 
151         """
152         # First, collect all the dependencies into a sequence of (parent, child)
153         # tuples, like [('flake8', 'pep8'), ('flake8', 'mccabe'), ...]
154         return lookup_table(
155             (key_from_req(Requirement(dep_name)), name)
156             for name, version_and_extras in cache_keys
157             for dep_name in self.cache[name][version_and_extras]
158         )
159
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/piptools/cache.py b/piptools/cache.py
--- a/piptools/cache.py
+++ b/piptools/cache.py
@@ -3,6 +3,7 @@
 
 import json
 import os
+import platform
 import sys
 
 from pip._vendor.packaging.requirements import Requirement
@@ -10,6 +11,17 @@
 from .exceptions import PipToolsError
 from .utils import as_tuple, key_from_req, lookup_table
 
+_PEP425_PY_TAGS = {"cpython": "cp", "pypy": "pp", "ironpython": "ip", "jython": "jy"}
+
+
+def _implementation_name():
+    """similar to PEP 425, however the minor version is separated from the
+    major to differentation "3.10" and "31.0".
+    """
+    implementation_name = platform.python_implementation().lower()
+    implementation = _PEP425_PY_TAGS.get(implementation_name, "??")
+    return "{}{}.{}".format(implementation, *sys.version_info)
+
 
 class CorruptCacheError(PipToolsError):
     def __init__(self, path):
@@ -45,14 +57,14 @@
 
         ~/.cache/pip-tools/depcache-pyX.Y.json
 
+    Where py indicates the Python implementation.
     Where X.Y indicates the Python version.
     """
 
     def __init__(self, cache_dir):
         if not os.path.isdir(cache_dir):
             os.makedirs(cache_dir)
-        py_version = ".".join(str(digit) for digit in sys.version_info[:2])
-        cache_filename = "depcache-py{}.json".format(py_version)
+        cache_filename = "depcache-{}.json".format(_implementation_name())
 
         self._cache_file = os.path.join(cache_dir, cache_filename)
         self._cache = None

{"golden_diff": "diff --git a/piptools/cache.py b/piptools/cache.py\n--- a/piptools/cache.py\n+++ b/piptools/cache.py\n@@ -3,6 +3,7 @@\n \n import json\n import os\n+import platform\n import sys\n \n from pip._vendor.packaging.requirements import Requirement\n@@ -10,6 +11,17 @@\n from .exceptions import PipToolsError\n from .utils import as_tuple, key_from_req, lookup_table\n \n+_PEP425_PY_TAGS = {\"cpython\": \"cp\", \"pypy\": \"pp\", \"ironpython\": \"ip\", \"jython\": \"jy\"}\n+\n+\n+def _implementation_name():\n+    \"\"\"similar to PEP 425, however the minor version is separated from the\n+    major to differentation \"3.10\" and \"31.0\".\n+    \"\"\"\n+    implementation_name = platform.python_implementation().lower()\n+    implementation = _PEP425_PY_TAGS.get(implementation_name, \"??\")\n+    return \"{}{}.{}\".format(implementation, *sys.version_info)\n+\n \n class CorruptCacheError(PipToolsError):\n     def __init__(self, path):\n@@ -45,14 +57,14 @@\n \n         ~/.cache/pip-tools/depcache-pyX.Y.json\n \n+    Where py indicates the Python implementation.\n     Where X.Y indicates the Python version.\n     \"\"\"\n \n     def __init__(self, cache_dir):\n         if not os.path.isdir(cache_dir):\n             os.makedirs(cache_dir)\n-        py_version = \".\".join(str(digit) for digit in sys.version_info[:2])\n-        cache_filename = \"depcache-py{}.json\".format(py_version)\n+        cache_filename = \"depcache-{}.json\".format(_implementation_name())\n \n         self._cache_file = os.path.join(cache_dir, cache_filename)\n         self._cache = None\n", "issue": "piptools depcache is shared between implementations causing incorrect dependencies\n<!-- Describe the issue briefly here. -->\r\n\r\n#### Environment Versions\r\n\r\n```console\r\n$ uname -a\r\nLinux asottile-MacBookPro 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux\r\n$ lsb_release -a\r\nNo LSB modules are available.\r\nDistributor ID:\tUbuntu\r\nDescription:\tUbuntu 18.04.3 LTS\r\nRelease:\t18.04\r\nCodename:\tbionic\r\n$ /tmp/x/venv36/bin/pip freeze --all\r\nClick==7.0\r\npip==20.0.2\r\npip-tools==4.4.0\r\nsetuptools==45.1.0\r\nsix==1.14.0\r\nwheel==0.34.1\r\n$ /tmp/x/venv36/bin/python --version --version\r\nPython 3.6.9 (default, Nov  7 2019, 10:44:02) \r\n[GCC 8.3.0]\r\n$ /tmp/x/venvpp36/bin/python --version --version\r\nPython 3.6.9 (1608da62bfc7, Dec 23 2019, 10:50:04)\r\n[PyPy 7.3.0 with GCC 7.3.1 20180303 (Red Hat 7.3.1-5)]\r\n```\r\n\r\n#### Steps to replicate\r\n\r\n```bash\r\nset -euxo pipefail\r\n\r\ncache=\"$PWD/cache\"\r\nexport XDG_CACHE_HOME=\"$cache\"\r\nexport CUSTOM_COMPILE_COMMAND='<<redacted>>'\r\n\r\nrm -rf \"$cache\" venv36 venvpp36 reqs*.txt\r\n\r\nvirtualenv venv36 -ppython3.6 >& /dev/null\r\nvenv36/bin/pip install -qq pip-tools\r\nvirtualenv venvpp36 -ppypy3 >& /dev/null\r\nvenvpp36/bin/pip install -qq pip-tools\r\n\r\necho 'cffi' > requirements.in\r\n\r\nvenv36/bin/pip-compile --output-file reqs36_1.txt\r\nvenvpp36/bin/pip-compile --output-file reqspp36_1.txt\r\n\r\nrm -rf \"$cache\"\r\nvenvpp36/bin/pip-compile --output-file reqspp36_2.txt\r\nvenv36/bin/pip-compile --output-file reqs36_2.txt\r\n\r\ndiff -u reqs36_1.txt reqs36_2.txt\r\ndiff -u reqspp36_1.txt reqspp36_2.txt\r\n```\r\n\r\n#### Expected result\r\n\r\nI expect no diff to be produced and the script to exit 0\r\n\r\n#### Actual result\r\n\r\n```console\r\n$ bash t.sh \r\n+ cache=/tmp/x/cache\r\n+ export XDG_CACHE_HOME=/tmp/x/cache\r\n+ XDG_CACHE_HOME=/tmp/x/cache\r\n+ export 'CUSTOM_COMPILE_COMMAND=<<redacted>>'\r\n+ CUSTOM_COMPILE_COMMAND='<<redacted>>'\r\n+ rm -rf /tmp/x/cache venv36 venvpp36 reqs36_1.txt reqs36_2.txt reqspp36_1.txt reqspp36_2.txt\r\n+ virtualenv venv36 -ppython3.6\r\n+ venv36/bin/pip install -qq pip-tools\r\n+ virtualenv venvpp36 -ppypy3\r\n+ venvpp36/bin/pip install -qq pip-tools\r\n+ echo cffi\r\n+ venv36/bin/pip-compile --output-file reqs36_1.txt\r\n#\r\n# This file is autogenerated by pip-compile\r\n# To update, run:\r\n#\r\n#    <<redacted>>\r\n#\r\ncffi==1.13.2\r\npycparser==2.19           # via cffi\r\n+ venvpp36/bin/pip-compile --output-file reqspp36_1.txt\r\n#\r\n# This file is autogenerated by pip-compile\r\n# To update, run:\r\n#\r\n#    <<redacted>>\r\n#\r\ncffi==1.13.2\r\npycparser==2.19           # via cffi\r\n+ rm -rf /tmp/x/cache\r\n+ venvpp36/bin/pip-compile --output-file reqspp36_2.txt\r\n#\r\n# This file is autogenerated by pip-compile\r\n# To update, run:\r\n#\r\n#    <<redacted>>\r\n#\r\ncffi==1.13.2\r\n+ venv36/bin/pip-compile --output-file reqs36_2.txt\r\n#\r\n# This file is autogenerated by pip-compile\r\n# To update, run:\r\n#\r\n#    <<redacted>>\r\n#\r\ncffi==1.13.2\r\n+ diff -u reqs36_1.txt reqs36_2.txt\r\n--- reqs36_1.txt\t2020-01-30 08:46:36.913122945 -0800\r\n+++ reqs36_2.txt\t2020-01-30 08:46:41.696946374 -0800\r\n@@ -5,4 +5,3 @@\r\n #    <<redacted>>\r\n #\r\n cffi==1.13.2\r\n-pycparser==2.19           # via cffi\r\n```\r\n\r\n\r\nas you can see here, the pypy 3.6 cache is poisoning the result of the cpython 3.6 pip-compile\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport json\nimport os\nimport sys\n\nfrom pip._vendor.packaging.requirements import Requirement\n\nfrom .exceptions import PipToolsError\nfrom .utils import as_tuple, key_from_req, lookup_table\n\n\nclass CorruptCacheError(PipToolsError):\n    def __init__(self, path):\n        self.path = path\n\n    def __str__(self):\n        lines = [\n            \"The dependency cache seems to have been corrupted.\",\n            \"Inspect, or delete, the following file:\",\n            \"  {}\".format(self.path),\n        ]\n        return os.linesep.join(lines)\n\n\ndef read_cache_file(cache_file_path):\n    with open(cache_file_path, \"r\") as cache_file:\n        try:\n            doc = json.load(cache_file)\n        except ValueError:\n            raise CorruptCacheError(cache_file_path)\n\n        # Check version and load the contents\n        if doc[\"__format__\"] != 1:\n            raise AssertionError(\"Unknown cache file format\")\n        return doc[\"dependencies\"]\n\n\nclass DependencyCache(object):\n    \"\"\"\n    Creates a new persistent dependency cache for the current Python version.\n    The cache file is written to the appropriate user cache dir for the\n    current platform, i.e.\n\n        ~/.cache/pip-tools/depcache-pyX.Y.json\n\n    Where X.Y indicates the Python version.\n    \"\"\"\n\n    def __init__(self, cache_dir):\n        if not os.path.isdir(cache_dir):\n            os.makedirs(cache_dir)\n        py_version = \".\".join(str(digit) for digit in sys.version_info[:2])\n        cache_filename = \"depcache-py{}.json\".format(py_version)\n\n        self._cache_file = os.path.join(cache_dir, cache_filename)\n        self._cache = None\n\n    @property\n    def cache(self):\n        \"\"\"\n        The dictionary that is the actual in-memory cache.  This property\n        lazily loads the cache from disk.\n        \"\"\"\n        if self._cache is None:\n            self.read_cache()\n        return self._cache\n\n    def as_cache_key(self, ireq):\n        \"\"\"\n        Given a requirement, return its cache key. This behavior is a little weird\n        in order to allow backwards compatibility with cache files. For a requirement\n        without extras, this will return, for example:\n\n        (\"ipython\", \"2.1.0\")\n\n        For a requirement with extras, the extras will be comma-separated and appended\n        to the version, inside brackets, like so:\n\n        (\"ipython\", \"2.1.0[nbconvert,notebook]\")\n        \"\"\"\n        name, version, extras = as_tuple(ireq)\n        if not extras:\n            extras_string = \"\"\n        else:\n            extras_string = \"[{}]\".format(\",\".join(extras))\n        return name, \"{}{}\".format(version, extras_string)\n\n    def read_cache(self):\n        \"\"\"Reads the cached contents into memory.\"\"\"\n        if os.path.exists(self._cache_file):\n            self._cache = read_cache_file(self._cache_file)\n        else:\n            self._cache = {}\n\n    def write_cache(self):\n        \"\"\"Writes the cache to disk as JSON.\"\"\"\n        doc = {\"__format__\": 1, \"dependencies\": self._cache}\n        with open(self._cache_file, \"w\") as f:\n            json.dump(doc, f, sort_keys=True)\n\n    def clear(self):\n        self._cache = {}\n        self.write_cache()\n\n    def __contains__(self, ireq):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        return pkgversion_and_extras in self.cache.get(pkgname, {})\n\n    def __getitem__(self, ireq):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        return self.cache[pkgname][pkgversion_and_extras]\n\n    def __setitem__(self, ireq, values):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        self.cache.setdefault(pkgname, {})\n        self.cache[pkgname][pkgversion_and_extras] = values\n        self.write_cache()\n\n    def reverse_dependencies(self, ireqs):\n        \"\"\"\n        Returns a lookup table of reverse dependencies for all the given ireqs.\n\n        Since this is all static, it only works if the dependency cache\n        contains the complete data, otherwise you end up with a partial view.\n        This is typically no problem if you use this function after the entire\n        dependency tree is resolved.\n        \"\"\"\n        ireqs_as_cache_values = [self.as_cache_key(ireq) for ireq in ireqs]\n        return self._reverse_dependencies(ireqs_as_cache_values)\n\n    def _reverse_dependencies(self, cache_keys):\n        \"\"\"\n        Returns a lookup table of reverse dependencies for all the given cache keys.\n\n        Example input:\n\n            [('pep8', '1.5.7'),\n             ('flake8', '2.4.0'),\n             ('mccabe', '0.3'),\n             ('pyflakes', '0.8.1')]\n\n        Example output:\n\n            {'pep8': ['flake8'],\n             'flake8': [],\n             'mccabe': ['flake8'],\n             'pyflakes': ['flake8']}\n\n        \"\"\"\n        # First, collect all the dependencies into a sequence of (parent, child)\n        # tuples, like [('flake8', 'pep8'), ('flake8', 'mccabe'), ...]\n        return lookup_table(\n            (key_from_req(Requirement(dep_name)), name)\n            for name, version_and_extras in cache_keys\n            for dep_name in self.cache[name][version_and_extras]\n        )\n", "path": "piptools/cache.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport json\nimport os\nimport platform\nimport sys\n\nfrom pip._vendor.packaging.requirements import Requirement\n\nfrom .exceptions import PipToolsError\nfrom .utils import as_tuple, key_from_req, lookup_table\n\n_PEP425_PY_TAGS = {\"cpython\": \"cp\", \"pypy\": \"pp\", \"ironpython\": \"ip\", \"jython\": \"jy\"}\n\n\ndef _implementation_name():\n    \"\"\"similar to PEP 425, however the minor version is separated from the\n    major to differentation \"3.10\" and \"31.0\".\n    \"\"\"\n    implementation_name = platform.python_implementation().lower()\n    implementation = _PEP425_PY_TAGS.get(implementation_name, \"??\")\n    return \"{}{}.{}\".format(implementation, *sys.version_info)\n\n\nclass CorruptCacheError(PipToolsError):\n    def __init__(self, path):\n        self.path = path\n\n    def __str__(self):\n        lines = [\n            \"The dependency cache seems to have been corrupted.\",\n            \"Inspect, or delete, the following file:\",\n            \"  {}\".format(self.path),\n        ]\n        return os.linesep.join(lines)\n\n\ndef read_cache_file(cache_file_path):\n    with open(cache_file_path, \"r\") as cache_file:\n        try:\n            doc = json.load(cache_file)\n        except ValueError:\n            raise CorruptCacheError(cache_file_path)\n\n        # Check version and load the contents\n        if doc[\"__format__\"] != 1:\n            raise AssertionError(\"Unknown cache file format\")\n        return doc[\"dependencies\"]\n\n\nclass DependencyCache(object):\n    \"\"\"\n    Creates a new persistent dependency cache for the current Python version.\n    The cache file is written to the appropriate user cache dir for the\n    current platform, i.e.\n\n        ~/.cache/pip-tools/depcache-pyX.Y.json\n\n    Where py indicates the Python implementation.\n    Where X.Y indicates the Python version.\n    \"\"\"\n\n    def __init__(self, cache_dir):\n        if not os.path.isdir(cache_dir):\n            os.makedirs(cache_dir)\n        cache_filename = \"depcache-{}.json\".format(_implementation_name())\n\n        self._cache_file = os.path.join(cache_dir, cache_filename)\n        self._cache = None\n\n    @property\n    def cache(self):\n        \"\"\"\n        The dictionary that is the actual in-memory cache.  This property\n        lazily loads the cache from disk.\n        \"\"\"\n        if self._cache is None:\n            self.read_cache()\n        return self._cache\n\n    def as_cache_key(self, ireq):\n        \"\"\"\n        Given a requirement, return its cache key. This behavior is a little weird\n        in order to allow backwards compatibility with cache files. For a requirement\n        without extras, this will return, for example:\n\n        (\"ipython\", \"2.1.0\")\n\n        For a requirement with extras, the extras will be comma-separated and appended\n        to the version, inside brackets, like so:\n\n        (\"ipython\", \"2.1.0[nbconvert,notebook]\")\n        \"\"\"\n        name, version, extras = as_tuple(ireq)\n        if not extras:\n            extras_string = \"\"\n        else:\n            extras_string = \"[{}]\".format(\",\".join(extras))\n        return name, \"{}{}\".format(version, extras_string)\n\n    def read_cache(self):\n        \"\"\"Reads the cached contents into memory.\"\"\"\n        if os.path.exists(self._cache_file):\n            self._cache = read_cache_file(self._cache_file)\n        else:\n            self._cache = {}\n\n    def write_cache(self):\n        \"\"\"Writes the cache to disk as JSON.\"\"\"\n        doc = {\"__format__\": 1, \"dependencies\": self._cache}\n        with open(self._cache_file, \"w\") as f:\n            json.dump(doc, f, sort_keys=True)\n\n    def clear(self):\n        self._cache = {}\n        self.write_cache()\n\n    def __contains__(self, ireq):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        return pkgversion_and_extras in self.cache.get(pkgname, {})\n\n    def __getitem__(self, ireq):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        return self.cache[pkgname][pkgversion_and_extras]\n\n    def __setitem__(self, ireq, values):\n        pkgname, pkgversion_and_extras = self.as_cache_key(ireq)\n        self.cache.setdefault(pkgname, {})\n        self.cache[pkgname][pkgversion_and_extras] = values\n        self.write_cache()\n\n    def reverse_dependencies(self, ireqs):\n        \"\"\"\n        Returns a lookup table of reverse dependencies for all the given ireqs.\n\n        Since this is all static, it only works if the dependency cache\n        contains the complete data, otherwise you end up with a partial view.\n        This is typically no problem if you use this function after the entire\n        dependency tree is resolved.\n        \"\"\"\n        ireqs_as_cache_values = [self.as_cache_key(ireq) for ireq in ireqs]\n        return self._reverse_dependencies(ireqs_as_cache_values)\n\n    def _reverse_dependencies(self, cache_keys):\n        \"\"\"\n        Returns a lookup table of reverse dependencies for all the given cache keys.\n\n        Example input:\n\n            [('pep8', '1.5.7'),\n             ('flake8', '2.4.0'),\n             ('mccabe', '0.3'),\n             ('pyflakes', '0.8.1')]\n\n        Example output:\n\n            {'pep8': ['flake8'],\n             'flake8': [],\n             'mccabe': ['flake8'],\n             'pyflakes': ['flake8']}\n\n        \"\"\"\n        # First, collect all the dependencies into a sequence of (parent, child)\n        # tuples, like [('flake8', 'pep8'), ('flake8', 'mccabe'), ...]\n        return lookup_table(\n            (key_from_req(Requirement(dep_name)), name)\n            for name, version_and_extras in cache_keys\n            for dep_name in self.cache[name][version_and_extras]\n        )\n", "path": "piptools/cache.py"}]}