We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Wrong key name used for PEP 658 metadata files in the JSON index
**Describe the bug**

[PEP 691](https://peps.python.org/pep-0691/#project-detail) states that the key name for metadata files in the JSON index should be `dist-info-metadata`:

> `dist-info-metadata`: An optional key that indicates that metadata for this file is available, via the same location as specified in [PEP 658](https://peps.python.org/pep-0658) (`{file_url}.metadata`).

However, warehouse is providing it under the `data-dist-info-metadata` key instead:

```
$ curl -H 'Accept: application/vnd.pypi.simple.v1+json' https://pypi.org/simple/fluffy-server/ | jq .files
[...]
  {
    "data-dist-info-metadata": {
      "sha256": "4db99543165cbdeef42ccb6257545911ccd7865d65e304e3e056f383a25f309c"
    },
    "filename": "fluffy_server-1.39.2-py3-none-any.whl",
    [...]
```

This is causing pip to not use the metadata files as it is looking for the `dist-info-metadata` key only:
https://github.com/pypa/pip/blob/f25f8fffbbd16fdb13a4f8977946afe9a3248453/src/pip/_internal/models/link.py#L265


**Additional context**

There are two bugs discovered recently in pip which may make this tricky to fix:

* https://github.com/pypa/pip/issues/12042
* https://github.com/pypa/pip/issues/12038

I believe if we simply fix the key name in pypi.org, it will break existing pip versions as it will cause users to encounter these bugs. It may be necessary to coordinate this fix with fixes to the above bugs in pip to avoid disruption?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `warehouse/packaging/utils.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 import hashlib
14 import os.path
15 import tempfile
16 
17 import packaging_legacy.version
18 
19 from pyramid_jinja2 import IJinja2Environment
20 from sqlalchemy.orm import joinedload
21 
22 from warehouse.packaging.interfaces import ISimpleStorage
23 from warehouse.packaging.models import File, Project, Release
24 
25 API_VERSION = "1.1"
26 
27 
28 def _simple_index(request, serial):
29     # Fetch the name and normalized name for all of our projects
30     projects = (
31         request.db.query(Project.name, Project.normalized_name, Project.last_serial)
32         .order_by(Project.normalized_name)
33         .all()
34     )
35 
36     return {
37         "meta": {"api-version": API_VERSION, "_last-serial": serial},
38         "projects": [{"name": p.name, "_last-serial": p.last_serial} for p in projects],
39     }
40 
41 
42 def _simple_detail(project, request):
43     # Get all of the files for this project.
44     files = sorted(
45         request.db.query(File)
46         .options(joinedload(File.release))
47         .join(Release)
48         .filter(Release.project == project)
49         .all(),
50         key=lambda f: (packaging_legacy.version.parse(f.release.version), f.filename),
51     )
52     versions = sorted(
53         {f.release.version for f in files}, key=packaging_legacy.version.parse
54     )
55 
56     return {
57         "meta": {"api-version": API_VERSION, "_last-serial": project.last_serial},
58         "name": project.normalized_name,
59         "versions": versions,
60         "files": [
61             {
62                 "filename": file.filename,
63                 "url": request.route_url("packaging.file", path=file.path),
64                 "hashes": {
65                     "sha256": file.sha256_digest,
66                 },
67                 "requires-python": file.release.requires_python,
68                 "size": file.size,
69                 "upload-time": file.upload_time.isoformat() + "Z",
70                 "yanked": file.release.yanked_reason
71                 if file.release.yanked and file.release.yanked_reason
72                 else file.release.yanked,
73                 "data-dist-info-metadata": {"sha256": file.metadata_file_sha256_digest}
74                 if file.metadata_file_sha256_digest
75                 else False,
76             }
77             for file in files
78         ],
79     }
80 
81 
82 def render_simple_detail(project, request, store=False):
83     context = _simple_detail(project, request)
84 
85     env = request.registry.queryUtility(IJinja2Environment, name=".jinja2")
86     template = env.get_template("templates/api/simple/detail.html")
87     content = template.render(**context, request=request)
88 
89     content_hasher = hashlib.blake2b(digest_size=256 // 8)
90     content_hasher.update(content.encode("utf-8"))
91     content_hash = content_hasher.hexdigest().lower()
92 
93     simple_detail_path = (
94         f"{project.normalized_name}/{content_hash}.{project.normalized_name}.html"
95     )
96 
97     if store:
98         storage = request.find_service(ISimpleStorage)
99         with tempfile.NamedTemporaryFile() as f:
100             f.write(content.encode("utf-8"))
101             f.flush()
102 
103             storage.store(
104                 simple_detail_path,
105                 f.name,
106                 meta={
107                     "project": project.normalized_name,
108                     "pypi-last-serial": project.last_serial,
109                     "hash": content_hash,
110                 },
111             )
112             storage.store(
113                 os.path.join(project.normalized_name, "index.html"),
114                 f.name,
115                 meta={
116                     "project": project.normalized_name,
117                     "pypi-last-serial": project.last_serial,
118                     "hash": content_hash,
119                 },
120             )
121 
122     return (content_hash, simple_detail_path)
123
```
--- 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/warehouse/packaging/utils.py b/warehouse/packaging/utils.py
--- a/warehouse/packaging/utils.py
+++ b/warehouse/packaging/utils.py
@@ -73,6 +73,9 @@
                 "data-dist-info-metadata": {"sha256": file.metadata_file_sha256_digest}
                 if file.metadata_file_sha256_digest
                 else False,
+                "core-metadata": {"sha256": file.metadata_file_sha256_digest}
+                if file.metadata_file_sha256_digest
+                else False,
             }
             for file in files
         ],

{"golden_diff": "diff --git a/warehouse/packaging/utils.py b/warehouse/packaging/utils.py\n--- a/warehouse/packaging/utils.py\n+++ b/warehouse/packaging/utils.py\n@@ -73,6 +73,9 @@\n                 \"data-dist-info-metadata\": {\"sha256\": file.metadata_file_sha256_digest}\n                 if file.metadata_file_sha256_digest\n                 else False,\n+                \"core-metadata\": {\"sha256\": file.metadata_file_sha256_digest}\n+                if file.metadata_file_sha256_digest\n+                else False,\n             }\n             for file in files\n         ],\n", "issue": "Wrong key name used for PEP 658 metadata files in the JSON index\n**Describe the bug**\r\n\r\n[PEP 691](https://peps.python.org/pep-0691/#project-detail) states that the key name for metadata files in the JSON index should be `dist-info-metadata`:\r\n\r\n> `dist-info-metadata`: An optional key that indicates that metadata for this file is available, via the same location as specified in [PEP 658](https://peps.python.org/pep-0658) (`{file_url}.metadata`).\r\n\r\nHowever, warehouse is providing it under the `data-dist-info-metadata` key instead:\r\n\r\n```\r\n$ curl -H 'Accept: application/vnd.pypi.simple.v1+json' https://pypi.org/simple/fluffy-server/ | jq .files\r\n[...]\r\n  {\r\n    \"data-dist-info-metadata\": {\r\n      \"sha256\": \"4db99543165cbdeef42ccb6257545911ccd7865d65e304e3e056f383a25f309c\"\r\n    },\r\n    \"filename\": \"fluffy_server-1.39.2-py3-none-any.whl\",\r\n    [...]\r\n```\r\n\r\nThis is causing pip to not use the metadata files as it is looking for the `dist-info-metadata` key only:\r\nhttps://github.com/pypa/pip/blob/f25f8fffbbd16fdb13a4f8977946afe9a3248453/src/pip/_internal/models/link.py#L265\r\n\r\n\r\n**Additional context**\r\n\r\nThere are two bugs discovered recently in pip which may make this tricky to fix:\r\n\r\n* https://github.com/pypa/pip/issues/12042\r\n* https://github.com/pypa/pip/issues/12038\r\n\r\nI believe if we simply fix the key name in pypi.org, it will break existing pip versions as it will cause users to encounter these bugs. It may be necessary to coordinate this fix with fixes to the above bugs in pip to avoid disruption?\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport hashlib\nimport os.path\nimport tempfile\n\nimport packaging_legacy.version\n\nfrom pyramid_jinja2 import IJinja2Environment\nfrom sqlalchemy.orm import joinedload\n\nfrom warehouse.packaging.interfaces import ISimpleStorage\nfrom warehouse.packaging.models import File, Project, Release\n\nAPI_VERSION = \"1.1\"\n\n\ndef _simple_index(request, serial):\n    # Fetch the name and normalized name for all of our projects\n    projects = (\n        request.db.query(Project.name, Project.normalized_name, Project.last_serial)\n        .order_by(Project.normalized_name)\n        .all()\n    )\n\n    return {\n        \"meta\": {\"api-version\": API_VERSION, \"_last-serial\": serial},\n        \"projects\": [{\"name\": p.name, \"_last-serial\": p.last_serial} for p in projects],\n    }\n\n\ndef _simple_detail(project, request):\n    # Get all of the files for this project.\n    files = sorted(\n        request.db.query(File)\n        .options(joinedload(File.release))\n        .join(Release)\n        .filter(Release.project == project)\n        .all(),\n        key=lambda f: (packaging_legacy.version.parse(f.release.version), f.filename),\n    )\n    versions = sorted(\n        {f.release.version for f in files}, key=packaging_legacy.version.parse\n    )\n\n    return {\n        \"meta\": {\"api-version\": API_VERSION, \"_last-serial\": project.last_serial},\n        \"name\": project.normalized_name,\n        \"versions\": versions,\n        \"files\": [\n            {\n                \"filename\": file.filename,\n                \"url\": request.route_url(\"packaging.file\", path=file.path),\n                \"hashes\": {\n                    \"sha256\": file.sha256_digest,\n                },\n                \"requires-python\": file.release.requires_python,\n                \"size\": file.size,\n                \"upload-time\": file.upload_time.isoformat() + \"Z\",\n                \"yanked\": file.release.yanked_reason\n                if file.release.yanked and file.release.yanked_reason\n                else file.release.yanked,\n                \"data-dist-info-metadata\": {\"sha256\": file.metadata_file_sha256_digest}\n                if file.metadata_file_sha256_digest\n                else False,\n            }\n            for file in files\n        ],\n    }\n\n\ndef render_simple_detail(project, request, store=False):\n    context = _simple_detail(project, request)\n\n    env = request.registry.queryUtility(IJinja2Environment, name=\".jinja2\")\n    template = env.get_template(\"templates/api/simple/detail.html\")\n    content = template.render(**context, request=request)\n\n    content_hasher = hashlib.blake2b(digest_size=256 // 8)\n    content_hasher.update(content.encode(\"utf-8\"))\n    content_hash = content_hasher.hexdigest().lower()\n\n    simple_detail_path = (\n        f\"{project.normalized_name}/{content_hash}.{project.normalized_name}.html\"\n    )\n\n    if store:\n        storage = request.find_service(ISimpleStorage)\n        with tempfile.NamedTemporaryFile() as f:\n            f.write(content.encode(\"utf-8\"))\n            f.flush()\n\n            storage.store(\n                simple_detail_path,\n                f.name,\n                meta={\n                    \"project\": project.normalized_name,\n                    \"pypi-last-serial\": project.last_serial,\n                    \"hash\": content_hash,\n                },\n            )\n            storage.store(\n                os.path.join(project.normalized_name, \"index.html\"),\n                f.name,\n                meta={\n                    \"project\": project.normalized_name,\n                    \"pypi-last-serial\": project.last_serial,\n                    \"hash\": content_hash,\n                },\n            )\n\n    return (content_hash, simple_detail_path)\n", "path": "warehouse/packaging/utils.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport hashlib\nimport os.path\nimport tempfile\n\nimport packaging_legacy.version\n\nfrom pyramid_jinja2 import IJinja2Environment\nfrom sqlalchemy.orm import joinedload\n\nfrom warehouse.packaging.interfaces import ISimpleStorage\nfrom warehouse.packaging.models import File, Project, Release\n\nAPI_VERSION = \"1.1\"\n\n\ndef _simple_index(request, serial):\n    # Fetch the name and normalized name for all of our projects\n    projects = (\n        request.db.query(Project.name, Project.normalized_name, Project.last_serial)\n        .order_by(Project.normalized_name)\n        .all()\n    )\n\n    return {\n        \"meta\": {\"api-version\": API_VERSION, \"_last-serial\": serial},\n        \"projects\": [{\"name\": p.name, \"_last-serial\": p.last_serial} for p in projects],\n    }\n\n\ndef _simple_detail(project, request):\n    # Get all of the files for this project.\n    files = sorted(\n        request.db.query(File)\n        .options(joinedload(File.release))\n        .join(Release)\n        .filter(Release.project == project)\n        .all(),\n        key=lambda f: (packaging_legacy.version.parse(f.release.version), f.filename),\n    )\n    versions = sorted(\n        {f.release.version for f in files}, key=packaging_legacy.version.parse\n    )\n\n    return {\n        \"meta\": {\"api-version\": API_VERSION, \"_last-serial\": project.last_serial},\n        \"name\": project.normalized_name,\n        \"versions\": versions,\n        \"files\": [\n            {\n                \"filename\": file.filename,\n                \"url\": request.route_url(\"packaging.file\", path=file.path),\n                \"hashes\": {\n                    \"sha256\": file.sha256_digest,\n                },\n                \"requires-python\": file.release.requires_python,\n                \"size\": file.size,\n                \"upload-time\": file.upload_time.isoformat() + \"Z\",\n                \"yanked\": file.release.yanked_reason\n                if file.release.yanked and file.release.yanked_reason\n                else file.release.yanked,\n                \"data-dist-info-metadata\": {\"sha256\": file.metadata_file_sha256_digest}\n                if file.metadata_file_sha256_digest\n                else False,\n                \"core-metadata\": {\"sha256\": file.metadata_file_sha256_digest}\n                if file.metadata_file_sha256_digest\n                else False,\n            }\n            for file in files\n        ],\n    }\n\n\ndef render_simple_detail(project, request, store=False):\n    context = _simple_detail(project, request)\n\n    env = request.registry.queryUtility(IJinja2Environment, name=\".jinja2\")\n    template = env.get_template(\"templates/api/simple/detail.html\")\n    content = template.render(**context, request=request)\n\n    content_hasher = hashlib.blake2b(digest_size=256 // 8)\n    content_hasher.update(content.encode(\"utf-8\"))\n    content_hash = content_hasher.hexdigest().lower()\n\n    simple_detail_path = (\n        f\"{project.normalized_name}/{content_hash}.{project.normalized_name}.html\"\n    )\n\n    if store:\n        storage = request.find_service(ISimpleStorage)\n        with tempfile.NamedTemporaryFile() as f:\n            f.write(content.encode(\"utf-8\"))\n            f.flush()\n\n            storage.store(\n                simple_detail_path,\n                f.name,\n                meta={\n                    \"project\": project.normalized_name,\n                    \"pypi-last-serial\": project.last_serial,\n                    \"hash\": content_hash,\n                },\n            )\n            storage.store(\n                os.path.join(project.normalized_name, \"index.html\"),\n                f.name,\n                meta={\n                    \"project\": project.normalized_name,\n                    \"pypi-last-serial\": project.last_serial,\n                    \"hash\": content_hash,\n                },\n            )\n\n    return (content_hash, simple_detail_path)\n", "path": "warehouse/packaging/utils.py"}]}