We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Expose requires_python for each release in JSON API
The following JSON:

https://pypi.python.org/pypi/astropy/json

or:

https://pypi.io/pypi/astropy/json

includes the following for the latest release:

```
        "requires_python": ">=3.5", 
```

but it would be really helpful to have this for all releases listed in the ``releases`` section, to avoid having to hit the server for each release to find out the ``requires_python``.

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

{"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@@ -138,6 +138,7 @@\n                 \"downloads\": -1,\n                 \"upload_time\": f.upload_time.strftime(\"%Y-%m-%dT%H:%M:%S\"),\n                 \"url\": request.route_url(\"packaging.file\", path=f.path),\n+                \"requires_python\": r.requires_python if r.requires_python else None,\n             }\n             for f in fs\n         ]\n", "issue": "Expose requires_python for each release in JSON API\nThe following JSON:\r\n\r\nhttps://pypi.python.org/pypi/astropy/json\r\n\r\nor:\r\n\r\nhttps://pypi.io/pypi/astropy/json\r\n\r\nincludes the following for the latest release:\r\n\r\n```\r\n        \"requires_python\": \">=3.5\", \r\n```\r\n\r\nbut it would be really helpful to have this for all releases listed in the ``releases`` section, to avoid having to hit the server for each release to find out the ``requires_python``.\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, Release, Project\n\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\n@view_config(\n    route_name=\"legacy.api.json.project\",\n    context=Project,\n    renderer=\"json\",\n    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)\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)\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.release\",\n    context=Release,\n    renderer=\"json\",\n    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)\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(Load(Release).load_only(\"version\"))\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                \"url\": request.route_url(\"packaging.file\", path=f.path),\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,\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\": project.bugtrack_url,\n            \"home_page\": release.home_page,\n            \"download_url\": release.download_url,\n        },\n        \"urls\": releases[release.version],\n        \"releases\": releases,\n        \"last_serial\": project.last_serial,\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, Release, Project\n\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\n@view_config(\n    route_name=\"legacy.api.json.project\",\n    context=Project,\n    renderer=\"json\",\n    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)\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)\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.release\",\n    context=Release,\n    renderer=\"json\",\n    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)\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(Load(Release).load_only(\"version\"))\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                \"url\": request.route_url(\"packaging.file\", path=f.path),\n                \"requires_python\": r.requires_python if r.requires_python else 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,\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\": project.bugtrack_url,\n            \"home_page\": release.home_page,\n            \"download_url\": release.download_url,\n        },\n        \"urls\": releases[release.version],\n        \"releases\": releases,\n        \"last_serial\": project.last_serial,\n    }\n", "path": "warehouse/legacy/api/json.py"}]}