We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
/pypi/{package}/{version}/json: yanking an older release updates latest release's yanked_reason field
**Describe the bug**
Yanking an older version of a package leads to unexpected side-effect for latest version's package info provided via the JSON endpoint. In particular, the `yanked_reason` field gets updated.

**Expected behavior**
When yanking a version of a package, no other verision's `yanked_reason` field should be updated.

**To Reproduce**
1. Create new package on test.pypi.org
2. Release version `0.2.0`.
3. Release version `0.3.0`.
4. Yank version `0.2.0`.
5. Check json endpoint of package version `0.3.0`.

```console
$ curl -sL https://test.pypi.org/pypi/abn-test-rss-yank/0.3.0/json | jq '.info.yanked'
false
$ curl -sL https://test.pypi.org/pypi/abn-test-rss-yank/0.3.0/json | jq '.info.yanked_reason'
"Testing Yank"
```

**My Platform**
N/A

**Additional context**
* Test package: https://test.pypi.org/project/abn-test-rss-yank/


--- 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/legacy/api/json.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 from collections import OrderedDict
14 
15 from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
16 from pyramid.view import view_config
17 from sqlalchemy.orm import Load
18 from sqlalchemy.orm.exc import NoResultFound
19 
20 from warehouse.cache.http import cache_control
21 from warehouse.cache.origin import origin_cache
22 from warehouse.packaging.models import File, Project, Release
23 
24 # Generate appropriate CORS headers for the JSON endpoint.
25 # We want to allow Cross-Origin requests here so that users can interact
26 # with these endpoints via XHR/Fetch APIs in the browser.
27 _CORS_HEADERS = {
28     "Access-Control-Allow-Origin": "*",
29     "Access-Control-Allow-Headers": ", ".join(
30         [
31             "Content-Type",
32             "If-Match",
33             "If-Modified-Since",
34             "If-None-Match",
35             "If-Unmodified-Since",
36         ]
37     ),
38     "Access-Control-Allow-Methods": "GET",
39     "Access-Control-Max-Age": "86400",  # 1 day.
40     "Access-Control-Expose-Headers": ", ".join(["X-PyPI-Last-Serial"]),
41 }
42 
43 _CACHE_DECORATOR = [
44     cache_control(15 * 60),  # 15 minutes
45     origin_cache(
46         1 * 24 * 60 * 60,  # 1 day
47         stale_while_revalidate=5 * 60,  # 5 minutes
48         stale_if_error=1 * 24 * 60 * 60,  # 1 day
49     ),
50 ]
51 
52 
53 @view_config(
54     route_name="legacy.api.json.project",
55     context=Project,
56     renderer="json",
57     decorator=_CACHE_DECORATOR,
58 )
59 def json_project(project, request):
60     if project.name != request.matchdict.get("name", project.name):
61         return HTTPMovedPermanently(
62             request.current_route_path(name=project.name), headers=_CORS_HEADERS
63         )
64 
65     try:
66         release = (
67             request.db.query(Release)
68             .filter(Release.project == project, Release.yanked.is_(False))
69             .order_by(Release.is_prerelease.nullslast(), Release._pypi_ordering.desc())
70             .limit(1)
71             .one()
72         )
73     except NoResultFound:
74         return HTTPNotFound(headers=_CORS_HEADERS)
75 
76     return json_release(release, request)
77 
78 
79 @view_config(
80     route_name="legacy.api.json.project_slash",
81     context=Project,
82     decorator=_CACHE_DECORATOR,
83 )
84 def json_project_slash(project, request):
85     return HTTPMovedPermanently(
86         # Respond with redirect to url without trailing slash
87         request.route_path("legacy.api.json.project", name=project.name),
88         headers=_CORS_HEADERS,
89     )
90 
91 
92 @view_config(
93     route_name="legacy.api.json.release",
94     context=Release,
95     renderer="json",
96     decorator=_CACHE_DECORATOR,
97 )
98 def json_release(release, request):
99     project = release.project
100 
101     if project.name != request.matchdict.get("name", project.name):
102         return HTTPMovedPermanently(
103             request.current_route_path(name=project.name), headers=_CORS_HEADERS
104         )
105 
106     # Apply CORS headers.
107     request.response.headers.update(_CORS_HEADERS)
108 
109     # Get the latest serial number for this project.
110     request.response.headers["X-PyPI-Last-Serial"] = str(project.last_serial)
111 
112     # Get all of the releases and files for this project.
113     release_files = (
114         request.db.query(Release, File)
115         .options(
116             Load(Release).load_only(
117                 "version", "requires_python", "yanked", "yanked_reason"
118             )
119         )
120         .outerjoin(File)
121         .filter(Release.project == project)
122         .order_by(Release._pypi_ordering.desc(), File.filename)
123         .all()
124     )
125 
126     # Map our releases + files into a dictionary that maps each release to a
127     # list of all its files.
128     releases = {}
129     for r, file_ in release_files:
130         files = releases.setdefault(r, [])
131         if file_ is not None:
132             files.append(file_)
133 
134     # Serialize our database objects to match the way that PyPI legacy
135     # presented this data.
136     releases = {
137         r.version: [
138             {
139                 "filename": f.filename,
140                 "packagetype": f.packagetype,
141                 "python_version": f.python_version,
142                 "has_sig": f.has_signature,
143                 "comment_text": f.comment_text,
144                 "md5_digest": f.md5_digest,
145                 "digests": {"md5": f.md5_digest, "sha256": f.sha256_digest},
146                 "size": f.size,
147                 # TODO: Remove this once we've had a long enough time with it
148                 #       here to consider it no longer in use.
149                 "downloads": -1,
150                 "upload_time": f.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
151                 "upload_time_iso_8601": f.upload_time.isoformat() + "Z",
152                 "url": request.route_url("packaging.file", path=f.path),
153                 "requires_python": r.requires_python if r.requires_python else None,
154                 "yanked": r.yanked,
155                 "yanked_reason": r.yanked_reason or None,
156             }
157             for f in fs
158         ]
159         for r, fs in releases.items()
160     }
161 
162     return {
163         "info": {
164             "name": project.name,
165             "version": release.version,
166             "summary": release.summary,
167             "description_content_type": release.description.content_type,
168             "description": release.description.raw,
169             "keywords": release.keywords,
170             "license": release.license,
171             "classifiers": list(release.classifiers),
172             "author": release.author,
173             "author_email": release.author_email,
174             "maintainer": release.maintainer,
175             "maintainer_email": release.maintainer_email,
176             "requires_python": release.requires_python,
177             "platform": release.platform,
178             "downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
179             "package_url": request.route_url("packaging.project", name=project.name),
180             "project_url": request.route_url("packaging.project", name=project.name),
181             "project_urls": OrderedDict(release.urls) if release.urls else None,
182             "release_url": request.route_url(
183                 "packaging.release", name=project.name, version=release.version
184             ),
185             "requires_dist": (
186                 list(release.requires_dist) if release.requires_dist else None
187             ),
188             "docs_url": project.documentation_url,
189             "bugtrack_url": None,
190             "home_page": release.home_page,
191             "download_url": release.download_url,
192             "yanked": release.yanked,
193             "yanked_reason": r.yanked_reason or None,
194         },
195         "urls": releases[release.version],
196         "releases": releases,
197         "last_serial": project.last_serial,
198     }
199 
200 
201 @view_config(
202     route_name="legacy.api.json.release_slash",
203     context=Release,
204     decorator=_CACHE_DECORATOR,
205 )
206 def json_release_slash(release, request):
207     return HTTPMovedPermanently(
208         # Respond with redirect to url without trailing slash
209         request.route_path(
210             "legacy.api.json.release",
211             name=release.project.name,
212             version=release.version,
213         ),
214         headers=_CORS_HEADERS,
215     )
216
```
--- 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/legacy/api/json.py b/warehouse/legacy/api/json.py
--- a/warehouse/legacy/api/json.py
+++ b/warehouse/legacy/api/json.py
@@ -190,7 +190,7 @@
             "home_page": release.home_page,
             "download_url": release.download_url,
             "yanked": release.yanked,
-            "yanked_reason": r.yanked_reason or None,
+            "yanked_reason": release.yanked_reason or None,
         },
         "urls": releases[release.version],
         "releases": releases,

{"golden_diff": "diff --git a/warehouse/legacy/api/json.py b/warehouse/legacy/api/json.py\n--- a/warehouse/legacy/api/json.py\n+++ b/warehouse/legacy/api/json.py\n@@ -190,7 +190,7 @@\n             \"home_page\": release.home_page,\n             \"download_url\": release.download_url,\n             \"yanked\": release.yanked,\n-            \"yanked_reason\": r.yanked_reason or None,\n+            \"yanked_reason\": release.yanked_reason or None,\n         },\n         \"urls\": releases[release.version],\n         \"releases\": releases,\n", "issue": "/pypi/{package}/{version}/json: yanking an older release updates latest release's yanked_reason field\n**Describe the bug**\r\nYanking an older version of a package leads to unexpected side-effect for latest version's package info provided via the JSON endpoint. In particular, the `yanked_reason` field gets updated.\r\n\r\n**Expected behavior**\r\nWhen yanking a version of a package, no other verision's `yanked_reason` field should be updated.\r\n\r\n**To Reproduce**\r\n1. Create new package on test.pypi.org\r\n2. Release version `0.2.0`.\r\n3. Release version `0.3.0`.\r\n4. Yank version `0.2.0`.\r\n5. Check json endpoint of package version `0.3.0`.\r\n\r\n```console\r\n$ curl -sL https://test.pypi.org/pypi/abn-test-rss-yank/0.3.0/json | jq '.info.yanked'\r\nfalse\r\n$ curl -sL https://test.pypi.org/pypi/abn-test-rss-yank/0.3.0/json | jq '.info.yanked_reason'\r\n\"Testing Yank\"\r\n```\r\n\r\n**My Platform**\r\nN/A\r\n\r\n**Additional context**\r\n* Test package: https://test.pypi.org/project/abn-test-rss-yank/\r\n\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\nfrom collections import OrderedDict\n\nfrom pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound\nfrom pyramid.view import view_config\nfrom sqlalchemy.orm import Load\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.models import File, Project, Release\n\n# Generate appropriate CORS headers for the JSON endpoint.\n# We want to allow Cross-Origin requests here so that users can interact\n# with these endpoints via XHR/Fetch APIs in the browser.\n_CORS_HEADERS = {\n    \"Access-Control-Allow-Origin\": \"*\",\n    \"Access-Control-Allow-Headers\": \", \".join(\n        [\n            \"Content-Type\",\n            \"If-Match\",\n            \"If-Modified-Since\",\n            \"If-None-Match\",\n            \"If-Unmodified-Since\",\n        ]\n    ),\n    \"Access-Control-Allow-Methods\": \"GET\",\n    \"Access-Control-Max-Age\": \"86400\",  # 1 day.\n    \"Access-Control-Expose-Headers\": \", \".join([\"X-PyPI-Last-Serial\"]),\n}\n\n_CACHE_DECORATOR = [\n    cache_control(15 * 60),  # 15 minutes\n    origin_cache(\n        1 * 24 * 60 * 60,  # 1 day\n        stale_while_revalidate=5 * 60,  # 5 minutes\n        stale_if_error=1 * 24 * 60 * 60,  # 1 day\n    ),\n]\n\n\n@view_config(\n    route_name=\"legacy.api.json.project\",\n    context=Project,\n    renderer=\"json\",\n    decorator=_CACHE_DECORATOR,\n)\ndef json_project(project, request):\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.name), headers=_CORS_HEADERS\n        )\n\n    try:\n        release = (\n            request.db.query(Release)\n            .filter(Release.project == project, Release.yanked.is_(False))\n            .order_by(Release.is_prerelease.nullslast(), Release._pypi_ordering.desc())\n            .limit(1)\n            .one()\n        )\n    except NoResultFound:\n        return HTTPNotFound(headers=_CORS_HEADERS)\n\n    return json_release(release, request)\n\n\n@view_config(\n    route_name=\"legacy.api.json.project_slash\",\n    context=Project,\n    decorator=_CACHE_DECORATOR,\n)\ndef json_project_slash(project, request):\n    return HTTPMovedPermanently(\n        # Respond with redirect to url without trailing slash\n        request.route_path(\"legacy.api.json.project\", name=project.name),\n        headers=_CORS_HEADERS,\n    )\n\n\n@view_config(\n    route_name=\"legacy.api.json.release\",\n    context=Release,\n    renderer=\"json\",\n    decorator=_CACHE_DECORATOR,\n)\ndef json_release(release, request):\n    project = release.project\n\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.name), headers=_CORS_HEADERS\n        )\n\n    # Apply CORS headers.\n    request.response.headers.update(_CORS_HEADERS)\n\n    # Get the latest serial number for this project.\n    request.response.headers[\"X-PyPI-Last-Serial\"] = str(project.last_serial)\n\n    # Get all of the releases and files for this project.\n    release_files = (\n        request.db.query(Release, File)\n        .options(\n            Load(Release).load_only(\n                \"version\", \"requires_python\", \"yanked\", \"yanked_reason\"\n            )\n        )\n        .outerjoin(File)\n        .filter(Release.project == project)\n        .order_by(Release._pypi_ordering.desc(), File.filename)\n        .all()\n    )\n\n    # Map our releases + files into a dictionary that maps each release to a\n    # list of all its files.\n    releases = {}\n    for r, file_ in release_files:\n        files = releases.setdefault(r, [])\n        if file_ is not None:\n            files.append(file_)\n\n    # Serialize our database objects to match the way that PyPI legacy\n    # presented this data.\n    releases = {\n        r.version: [\n            {\n                \"filename\": f.filename,\n                \"packagetype\": f.packagetype,\n                \"python_version\": f.python_version,\n                \"has_sig\": f.has_signature,\n                \"comment_text\": f.comment_text,\n                \"md5_digest\": f.md5_digest,\n                \"digests\": {\"md5\": f.md5_digest, \"sha256\": f.sha256_digest},\n                \"size\": f.size,\n                # TODO: Remove this once we've had a long enough time with it\n                #       here to consider it no longer in use.\n                \"downloads\": -1,\n                \"upload_time\": f.upload_time.strftime(\"%Y-%m-%dT%H:%M:%S\"),\n                \"upload_time_iso_8601\": f.upload_time.isoformat() + \"Z\",\n                \"url\": request.route_url(\"packaging.file\", path=f.path),\n                \"requires_python\": r.requires_python if r.requires_python else None,\n                \"yanked\": r.yanked,\n                \"yanked_reason\": r.yanked_reason or None,\n            }\n            for f in fs\n        ]\n        for r, fs in releases.items()\n    }\n\n    return {\n        \"info\": {\n            \"name\": project.name,\n            \"version\": release.version,\n            \"summary\": release.summary,\n            \"description_content_type\": release.description.content_type,\n            \"description\": release.description.raw,\n            \"keywords\": release.keywords,\n            \"license\": release.license,\n            \"classifiers\": list(release.classifiers),\n            \"author\": release.author,\n            \"author_email\": release.author_email,\n            \"maintainer\": release.maintainer,\n            \"maintainer_email\": release.maintainer_email,\n            \"requires_python\": release.requires_python,\n            \"platform\": release.platform,\n            \"downloads\": {\"last_day\": -1, \"last_week\": -1, \"last_month\": -1},\n            \"package_url\": request.route_url(\"packaging.project\", name=project.name),\n            \"project_url\": request.route_url(\"packaging.project\", name=project.name),\n            \"project_urls\": OrderedDict(release.urls) if release.urls else None,\n            \"release_url\": request.route_url(\n                \"packaging.release\", name=project.name, version=release.version\n            ),\n            \"requires_dist\": (\n                list(release.requires_dist) if release.requires_dist else None\n            ),\n            \"docs_url\": project.documentation_url,\n            \"bugtrack_url\": None,\n            \"home_page\": release.home_page,\n            \"download_url\": release.download_url,\n            \"yanked\": release.yanked,\n            \"yanked_reason\": r.yanked_reason or None,\n        },\n        \"urls\": releases[release.version],\n        \"releases\": releases,\n        \"last_serial\": project.last_serial,\n    }\n\n\n@view_config(\n    route_name=\"legacy.api.json.release_slash\",\n    context=Release,\n    decorator=_CACHE_DECORATOR,\n)\ndef json_release_slash(release, request):\n    return HTTPMovedPermanently(\n        # Respond with redirect to url without trailing slash\n        request.route_path(\n            \"legacy.api.json.release\",\n            name=release.project.name,\n            version=release.version,\n        ),\n        headers=_CORS_HEADERS,\n    )\n", "path": "warehouse/legacy/api/json.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\nfrom collections import OrderedDict\n\nfrom pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound\nfrom pyramid.view import view_config\nfrom sqlalchemy.orm import Load\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.models import File, Project, Release\n\n# Generate appropriate CORS headers for the JSON endpoint.\n# We want to allow Cross-Origin requests here so that users can interact\n# with these endpoints via XHR/Fetch APIs in the browser.\n_CORS_HEADERS = {\n    \"Access-Control-Allow-Origin\": \"*\",\n    \"Access-Control-Allow-Headers\": \", \".join(\n        [\n            \"Content-Type\",\n            \"If-Match\",\n            \"If-Modified-Since\",\n            \"If-None-Match\",\n            \"If-Unmodified-Since\",\n        ]\n    ),\n    \"Access-Control-Allow-Methods\": \"GET\",\n    \"Access-Control-Max-Age\": \"86400\",  # 1 day.\n    \"Access-Control-Expose-Headers\": \", \".join([\"X-PyPI-Last-Serial\"]),\n}\n\n_CACHE_DECORATOR = [\n    cache_control(15 * 60),  # 15 minutes\n    origin_cache(\n        1 * 24 * 60 * 60,  # 1 day\n        stale_while_revalidate=5 * 60,  # 5 minutes\n        stale_if_error=1 * 24 * 60 * 60,  # 1 day\n    ),\n]\n\n\n@view_config(\n    route_name=\"legacy.api.json.project\",\n    context=Project,\n    renderer=\"json\",\n    decorator=_CACHE_DECORATOR,\n)\ndef json_project(project, request):\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.name), headers=_CORS_HEADERS\n        )\n\n    try:\n        release = (\n            request.db.query(Release)\n            .filter(Release.project == project, Release.yanked.is_(False))\n            .order_by(Release.is_prerelease.nullslast(), Release._pypi_ordering.desc())\n            .limit(1)\n            .one()\n        )\n    except NoResultFound:\n        return HTTPNotFound(headers=_CORS_HEADERS)\n\n    return json_release(release, request)\n\n\n@view_config(\n    route_name=\"legacy.api.json.project_slash\",\n    context=Project,\n    decorator=_CACHE_DECORATOR,\n)\ndef json_project_slash(project, request):\n    return HTTPMovedPermanently(\n        # Respond with redirect to url without trailing slash\n        request.route_path(\"legacy.api.json.project\", name=project.name),\n        headers=_CORS_HEADERS,\n    )\n\n\n@view_config(\n    route_name=\"legacy.api.json.release\",\n    context=Release,\n    renderer=\"json\",\n    decorator=_CACHE_DECORATOR,\n)\ndef json_release(release, request):\n    project = release.project\n\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.name), headers=_CORS_HEADERS\n        )\n\n    # Apply CORS headers.\n    request.response.headers.update(_CORS_HEADERS)\n\n    # Get the latest serial number for this project.\n    request.response.headers[\"X-PyPI-Last-Serial\"] = str(project.last_serial)\n\n    # Get all of the releases and files for this project.\n    release_files = (\n        request.db.query(Release, File)\n        .options(\n            Load(Release).load_only(\n                \"version\", \"requires_python\", \"yanked\", \"yanked_reason\"\n            )\n        )\n        .outerjoin(File)\n        .filter(Release.project == project)\n        .order_by(Release._pypi_ordering.desc(), File.filename)\n        .all()\n    )\n\n    # Map our releases + files into a dictionary that maps each release to a\n    # list of all its files.\n    releases = {}\n    for r, file_ in release_files:\n        files = releases.setdefault(r, [])\n        if file_ is not None:\n            files.append(file_)\n\n    # Serialize our database objects to match the way that PyPI legacy\n    # presented this data.\n    releases = {\n        r.version: [\n            {\n                \"filename\": f.filename,\n                \"packagetype\": f.packagetype,\n                \"python_version\": f.python_version,\n                \"has_sig\": f.has_signature,\n                \"comment_text\": f.comment_text,\n                \"md5_digest\": f.md5_digest,\n                \"digests\": {\"md5\": f.md5_digest, \"sha256\": f.sha256_digest},\n                \"size\": f.size,\n                # TODO: Remove this once we've had a long enough time with it\n                #       here to consider it no longer in use.\n                \"downloads\": -1,\n                \"upload_time\": f.upload_time.strftime(\"%Y-%m-%dT%H:%M:%S\"),\n                \"upload_time_iso_8601\": f.upload_time.isoformat() + \"Z\",\n                \"url\": request.route_url(\"packaging.file\", path=f.path),\n                \"requires_python\": r.requires_python if r.requires_python else None,\n                \"yanked\": r.yanked,\n                \"yanked_reason\": r.yanked_reason or None,\n            }\n            for f in fs\n        ]\n        for r, fs in releases.items()\n    }\n\n    return {\n        \"info\": {\n            \"name\": project.name,\n            \"version\": release.version,\n            \"summary\": release.summary,\n            \"description_content_type\": release.description.content_type,\n            \"description\": release.description.raw,\n            \"keywords\": release.keywords,\n            \"license\": release.license,\n            \"classifiers\": list(release.classifiers),\n            \"author\": release.author,\n            \"author_email\": release.author_email,\n            \"maintainer\": release.maintainer,\n            \"maintainer_email\": release.maintainer_email,\n            \"requires_python\": release.requires_python,\n            \"platform\": release.platform,\n            \"downloads\": {\"last_day\": -1, \"last_week\": -1, \"last_month\": -1},\n            \"package_url\": request.route_url(\"packaging.project\", name=project.name),\n            \"project_url\": request.route_url(\"packaging.project\", name=project.name),\n            \"project_urls\": OrderedDict(release.urls) if release.urls else None,\n            \"release_url\": request.route_url(\n                \"packaging.release\", name=project.name, version=release.version\n            ),\n            \"requires_dist\": (\n                list(release.requires_dist) if release.requires_dist else None\n            ),\n            \"docs_url\": project.documentation_url,\n            \"bugtrack_url\": None,\n            \"home_page\": release.home_page,\n            \"download_url\": release.download_url,\n            \"yanked\": release.yanked,\n            \"yanked_reason\": release.yanked_reason or None,\n        },\n        \"urls\": releases[release.version],\n        \"releases\": releases,\n        \"last_serial\": project.last_serial,\n    }\n\n\n@view_config(\n    route_name=\"legacy.api.json.release_slash\",\n    context=Release,\n    decorator=_CACHE_DECORATOR,\n)\ndef json_release_slash(release, request):\n    return HTTPMovedPermanently(\n        # Respond with redirect to url without trailing slash\n        request.route_path(\n            \"legacy.api.json.release\",\n            name=release.project.name,\n            version=release.version,\n        ),\n        headers=_CORS_HEADERS,\n    )\n", "path": "warehouse/legacy/api/json.py"}]}