prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Warehouse file order differs from legacy PyPI file list
  Tonight, while load testing of pypi.org was ongoing, we saw some failures in automated systems that use `--require-hashes` with `pip install`, as ordering on the package file list page changed.
  
  The specific package we saw break was `pandas` at version `0.12.0`. We had a single hash for `pandas-0.12.0.tar.gz`. A few of our hosts were served from the legacy PyPI service, which succeeded as normal. The Warehouse endpoint, however, failed, since `pandas-0.12.0.zip` now preceded `pandas-0.12.0.tar.gz` in the file list.
  
  At the moment, you can see that https://pypi.org/simple/pandas/ and https://pypi.python.org/simple/pandas/ differ by searching for `pandas-0.12.0.tar.gz` and `pandas-0.12.0.zip` and comparing the position.
  
  --- 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/simple.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 
  14 from packaging.version import parse
  15 from pyramid.httpexceptions import HTTPMovedPermanently
  16 from pyramid.view import view_config
  17 from sqlalchemy import func
  18 from sqlalchemy.orm import joinedload
  19 
  20 from warehouse.cache.http import cache_control
  21 from warehouse.cache.origin import origin_cache
  22 from warehouse.packaging.models import JournalEntry, File, Project, Release
  23 
  24 
  25 @view_config(
  26     route_name="legacy.api.simple.index",
  27     renderer="legacy/api/simple/index.html",
  28     decorator=[
  29         cache_control(10 * 60),               # 10 minutes
  30         origin_cache(
  31             1 * 24 * 60 * 60,                 # 1 day
  32             stale_while_revalidate=5 * 60,    # 5 minutes
  33             stale_if_error=1 * 24 * 60 * 60,  # 1 day
  34         ),
  35     ],
  36 )
  37 def simple_index(request):
  38     # Get the latest serial number
  39     serial = request.db.query(func.max(JournalEntry.id)).scalar() or 0
  40     request.response.headers["X-PyPI-Last-Serial"] = str(serial)
  41 
  42     # Fetch the name and normalized name for all of our projects
  43     projects = (
  44         request.db.query(Project.name, Project.normalized_name)
  45                   .order_by(Project.normalized_name)
  46                   .all()
  47     )
  48 
  49     return {"projects": projects}
  50 
  51 
  52 @view_config(
  53     route_name="legacy.api.simple.detail",
  54     renderer="legacy/api/simple/detail.html",
  55     decorator=[
  56         cache_control(10 * 60),               # 10 minutes
  57         origin_cache(
  58             1 * 24 * 60 * 60,                 # 1 day
  59             stale_while_revalidate=5 * 60,    # 5 minutes
  60             stale_if_error=1 * 24 * 60 * 60,  # 1 day
  61         ),
  62     ],
  63 )
  64 def simple_detail(project, request):
  65     # TODO: Handle files which are not hosted on PyPI
  66 
  67     # Make sure that we're using the normalized version of the URL.
  68     if (project.normalized_name !=
  69             request.matchdict.get("name", project.normalized_name)):
  70         return HTTPMovedPermanently(
  71             request.current_route_path(name=project.normalized_name),
  72         )
  73 
  74     # Get the latest serial number for this project.
  75     request.response.headers["X-PyPI-Last-Serial"] = str(project.last_serial)
  76 
  77     # Get all of the files for this project.
  78     files = sorted(
  79         request.db.query(File)
  80         .options(joinedload(File.release))
  81         .filter(
  82             File.name == project.name,
  83             File.version.in_(
  84                 request.db.query(Release)
  85                           .filter(Release.project == project)
  86                           .with_entities(Release.version)
  87             )
  88         )
  89         .all(),
  90         key=lambda f: (parse(f.version), f.packagetype)
  91     )
  92 
  93     return {"project": project, "files": files}
  94
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/warehouse/legacy/api/simple.py b/warehouse/legacy/api/simple.py
  --- a/warehouse/legacy/api/simple.py
  +++ b/warehouse/legacy/api/simple.py
  @@ -87,7 +87,7 @@
               )
           )
           .all(),
  -        key=lambda f: (parse(f.version), f.packagetype)
  +        key=lambda f: (parse(f.version), f.filename)
       )
   
       return {"project": project, "files": files}
  
verification_info: {"golden_diff": "diff --git a/warehouse/legacy/api/simple.py b/warehouse/legacy/api/simple.py\n--- a/warehouse/legacy/api/simple.py\n+++ b/warehouse/legacy/api/simple.py\n@@ -87,7 +87,7 @@\n             )\n         )\n         .all(),\n-        key=lambda f: (parse(f.version), f.packagetype)\n+        key=lambda f: (parse(f.version), f.filename)\n     )\n \n     return {\"project\": project, \"files\": files}\n", "issue": "Warehouse file order differs from legacy PyPI file list\nTonight, while load testing of pypi.org was ongoing, we saw some failures in automated systems that use `--require-hashes` with `pip install`, as ordering on the package file list page changed.\r\n\r\nThe specific package we saw break was `pandas` at version `0.12.0`. We had a single hash for `pandas-0.12.0.tar.gz`. A few of our hosts were served from the legacy PyPI service, which succeeded as normal. The Warehouse endpoint, however, failed, since `pandas-0.12.0.zip` now preceded `pandas-0.12.0.tar.gz` in the file list.\r\n\r\nAt the moment, you can see that https://pypi.org/simple/pandas/ and https://pypi.python.org/simple/pandas/ differ by searching for `pandas-0.12.0.tar.gz` and `pandas-0.12.0.zip` and comparing the position.\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\n\nfrom packaging.version import parse\nfrom pyramid.httpexceptions import HTTPMovedPermanently\nfrom pyramid.view import view_config\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import joinedload\n\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.models import JournalEntry, File, Project, Release\n\n\n@view_config(\n    route_name=\"legacy.api.simple.index\",\n    renderer=\"legacy/api/simple/index.html\",\n    decorator=[\n        cache_control(10 * 60),               # 10 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 simple_index(request):\n    # Get the latest serial number\n    serial = request.db.query(func.max(JournalEntry.id)).scalar() or 0\n    request.response.headers[\"X-PyPI-Last-Serial\"] = str(serial)\n\n    # Fetch the name and normalized name for all of our projects\n    projects = (\n        request.db.query(Project.name, Project.normalized_name)\n                  .order_by(Project.normalized_name)\n                  .all()\n    )\n\n    return {\"projects\": projects}\n\n\n@view_config(\n    route_name=\"legacy.api.simple.detail\",\n    renderer=\"legacy/api/simple/detail.html\",\n    decorator=[\n        cache_control(10 * 60),               # 10 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 simple_detail(project, request):\n    # TODO: Handle files which are not hosted on PyPI\n\n    # Make sure that we're using the normalized version of the URL.\n    if (project.normalized_name !=\n            request.matchdict.get(\"name\", project.normalized_name)):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.normalized_name),\n        )\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 files for this project.\n    files = sorted(\n        request.db.query(File)\n        .options(joinedload(File.release))\n        .filter(\n            File.name == project.name,\n            File.version.in_(\n                request.db.query(Release)\n                          .filter(Release.project == project)\n                          .with_entities(Release.version)\n            )\n        )\n        .all(),\n        key=lambda f: (parse(f.version), f.packagetype)\n    )\n\n    return {\"project\": project, \"files\": files}\n", "path": "warehouse/legacy/api/simple.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\n\nfrom packaging.version import parse\nfrom pyramid.httpexceptions import HTTPMovedPermanently\nfrom pyramid.view import view_config\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import joinedload\n\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.models import JournalEntry, File, Project, Release\n\n\n@view_config(\n    route_name=\"legacy.api.simple.index\",\n    renderer=\"legacy/api/simple/index.html\",\n    decorator=[\n        cache_control(10 * 60),               # 10 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 simple_index(request):\n    # Get the latest serial number\n    serial = request.db.query(func.max(JournalEntry.id)).scalar() or 0\n    request.response.headers[\"X-PyPI-Last-Serial\"] = str(serial)\n\n    # Fetch the name and normalized name for all of our projects\n    projects = (\n        request.db.query(Project.name, Project.normalized_name)\n                  .order_by(Project.normalized_name)\n                  .all()\n    )\n\n    return {\"projects\": projects}\n\n\n@view_config(\n    route_name=\"legacy.api.simple.detail\",\n    renderer=\"legacy/api/simple/detail.html\",\n    decorator=[\n        cache_control(10 * 60),               # 10 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 simple_detail(project, request):\n    # TODO: Handle files which are not hosted on PyPI\n\n    # Make sure that we're using the normalized version of the URL.\n    if (project.normalized_name !=\n            request.matchdict.get(\"name\", project.normalized_name)):\n        return HTTPMovedPermanently(\n            request.current_route_path(name=project.normalized_name),\n        )\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 files for this project.\n    files = sorted(\n        request.db.query(File)\n        .options(joinedload(File.release))\n        .filter(\n            File.name == project.name,\n            File.version.in_(\n                request.db.query(Release)\n                          .filter(Release.project == project)\n                          .with_entities(Release.version)\n            )\n        )\n        .all(),\n        key=lambda f: (parse(f.version), f.filename)\n    )\n\n    return {\"project\": project, \"files\": files}\n", "path": "warehouse/legacy/api/simple.py"}]}