We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Improve sorting on simple page
I'd like to submit a patch for this but I have a few questions :)

First I'll describe what I'd like to do...

## sort by version number

See https://pypi.org/simple/pre-commit/

You'll notice that `0.10.0` erroneously sorts *before* `0.2.0` (I'd like to fix this)

## investigation

I've found the code which does this sorting [here](https://github.com/pypa/warehouse/blob/3bdfe5a89cc9a922ee97304c98384c24822a09ee/warehouse/legacy/api/simple.py#L76-L89)

This seems to just sort by filename, but by inspecting and viewing [this page](https://pypi.org/simple/pre-commit-mirror-maker/) I notice it seems to ignore `_` vs. `-` (which is good, that's what I want to continue to happen but I'm just not seeing it from the code!)

## other questions

The `File` objects which come back from the database contain a `.version` attribute that I'd like to use to participate in sorting, my main question is: **Can I depend on this version to be a valid [PEP440](https://www.python.org/dev/peps/pep-0440/) version and use something like `pkg_resources.parse_version`?**

I'd basically like to replicate something close to the sorting which @chriskuehl's [dumb-pypi](https://github.com/chriskuehl/dumb-pypi) does [here](https://github.com/chriskuehl/dumb-pypi/blob/fd0f93fc2e82cbd9bae41b3c60c5f006b2319c60/dumb_pypi/main.py#L77-L91).

Thanks in advance :)

---

**Good First Issue**: This issue is good for first time contributors. If there is not a corresponding pull request for this issue, it is up for grabs. For directions for getting set up, see our [Getting Started Guide](https://warehouse.pypa.io/development/getting-started/). If you are working on this issue and have questions, please feel free to ask them here, [`#pypa-dev` on Freenode](https://webchat.freenode.net/?channels=%23pypa-dev), or the [pypa-dev mailing list](https://groups.google.com/forum/#!forum/pypa-dev).

--- 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 from pyramid.httpexceptions import HTTPMovedPermanently
14 from pyramid.view import view_config
15 from sqlalchemy import func
16 from sqlalchemy.orm import joinedload
17 
18 from warehouse.cache.http import cache_control
19 from warehouse.cache.origin import origin_cache
20 from warehouse.packaging.models import JournalEntry, File, Project, Release
21 
22 
23 @view_config(
24     route_name="legacy.api.simple.index",
25     renderer="legacy/api/simple/index.html",
26     decorator=[
27         cache_control(10 * 60),               # 10 minutes
28         origin_cache(
29             1 * 24 * 60 * 60,                 # 1 day
30             stale_while_revalidate=5 * 60,    # 5 minutes
31             stale_if_error=1 * 24 * 60 * 60,  # 1 day
32         ),
33     ],
34 )
35 def simple_index(request):
36     # Get the latest serial number
37     serial = request.db.query(func.max(JournalEntry.id)).scalar() or 0
38     request.response.headers["X-PyPI-Last-Serial"] = str(serial)
39 
40     # Fetch the name and normalized name for all of our projects
41     projects = (
42         request.db.query(Project.name, Project.normalized_name)
43                   .order_by(Project.normalized_name)
44                   .all()
45     )
46 
47     return {"projects": projects}
48 
49 
50 @view_config(
51     route_name="legacy.api.simple.detail",
52     renderer="legacy/api/simple/detail.html",
53     decorator=[
54         cache_control(10 * 60),               # 10 minutes
55         origin_cache(
56             1 * 24 * 60 * 60,                 # 1 day
57             stale_while_revalidate=5 * 60,    # 5 minutes
58             stale_if_error=1 * 24 * 60 * 60,  # 1 day
59         ),
60     ],
61 )
62 def simple_detail(project, request):
63     # TODO: Handle files which are not hosted on PyPI
64 
65     # Make sure that we're using the normalized version of the URL.
66     if (project.normalized_name !=
67             request.matchdict.get("name", project.normalized_name)):
68         return HTTPMovedPermanently(
69             request.current_route_path(name=project.normalized_name),
70         )
71 
72     # Get the latest serial number for this project.
73     request.response.headers["X-PyPI-Last-Serial"] = str(project.last_serial)
74 
75     # Get all of the files for this project.
76     files = (
77         request.db.query(File)
78         .options(joinedload(File.release))
79         .filter(
80             File.name == project.name,
81             File.version.in_(
82                 request.db.query(Release)
83                           .filter(Release.project == project)
84                           .with_entities(Release.version)
85             )
86         )
87         .order_by(File.filename)
88         .all()
89     )
90 
91     return {"project": project, "files": files}
92
```
--- 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/simple.py b/warehouse/legacy/api/simple.py
--- a/warehouse/legacy/api/simple.py
+++ b/warehouse/legacy/api/simple.py
@@ -10,6 +10,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
+from packaging.version import parse
 from pyramid.httpexceptions import HTTPMovedPermanently
 from pyramid.view import view_config
 from sqlalchemy import func
@@ -73,7 +75,7 @@
     request.response.headers["X-PyPI-Last-Serial"] = str(project.last_serial)
 
     # Get all of the files for this project.
-    files = (
+    files = sorted(
         request.db.query(File)
         .options(joinedload(File.release))
         .filter(
@@ -84,8 +86,8 @@
                           .with_entities(Release.version)
             )
         )
-        .order_by(File.filename)
-        .all()
+        .all(),
+        key=lambda f: (parse(f.version), f.packagetype)
     )
 
     return {"project": project, "files": files}

{"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@@ -10,6 +10,8 @@\n # See the License for the specific language governing permissions and\n # limitations under the License.\n \n+\n+from packaging.version import parse\n from pyramid.httpexceptions import HTTPMovedPermanently\n from pyramid.view import view_config\n from sqlalchemy import func\n@@ -73,7 +75,7 @@\n     request.response.headers[\"X-PyPI-Last-Serial\"] = str(project.last_serial)\n \n     # Get all of the files for this project.\n-    files = (\n+    files = sorted(\n         request.db.query(File)\n         .options(joinedload(File.release))\n         .filter(\n@@ -84,8 +86,8 @@\n                           .with_entities(Release.version)\n             )\n         )\n-        .order_by(File.filename)\n-        .all()\n+        .all(),\n+        key=lambda f: (parse(f.version), f.packagetype)\n     )\n \n     return {\"project\": project, \"files\": files}\n", "issue": "Improve sorting on simple page\nI'd like to submit a patch for this but I have a few questions :)\r\n\r\nFirst I'll describe what I'd like to do...\r\n\r\n## sort by version number\r\n\r\nSee https://pypi.org/simple/pre-commit/\r\n\r\nYou'll notice that `0.10.0` erroneously sorts *before* `0.2.0` (I'd like to fix this)\r\n\r\n## investigation\r\n\r\nI've found the code which does this sorting [here](https://github.com/pypa/warehouse/blob/3bdfe5a89cc9a922ee97304c98384c24822a09ee/warehouse/legacy/api/simple.py#L76-L89)\r\n\r\nThis seems to just sort by filename, but by inspecting and viewing [this page](https://pypi.org/simple/pre-commit-mirror-maker/) I notice it seems to ignore `_` vs. `-` (which is good, that's what I want to continue to happen but I'm just not seeing it from the code!)\r\n\r\n## other questions\r\n\r\nThe `File` objects which come back from the database contain a `.version` attribute that I'd like to use to participate in sorting, my main question is: **Can I depend on this version to be a valid [PEP440](https://www.python.org/dev/peps/pep-0440/) version and use something like `pkg_resources.parse_version`?**\r\n\r\nI'd basically like to replicate something close to the sorting which @chriskuehl's [dumb-pypi](https://github.com/chriskuehl/dumb-pypi) does [here](https://github.com/chriskuehl/dumb-pypi/blob/fd0f93fc2e82cbd9bae41b3c60c5f006b2319c60/dumb_pypi/main.py#L77-L91).\r\n\r\nThanks in advance :)\r\n\r\n---\r\n\r\n**Good First Issue**: This issue is good for first time contributors. If there is not a corresponding pull request for this issue, it is up for grabs. For directions for getting set up, see our [Getting Started Guide](https://warehouse.pypa.io/development/getting-started/). If you are working on this issue and have questions, please feel free to ask them here, [`#pypa-dev` on Freenode](https://webchat.freenode.net/?channels=%23pypa-dev), or the [pypa-dev mailing list](https://groups.google.com/forum/#!forum/pypa-dev).\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 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 = (\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        .order_by(File.filename)\n        .all()\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.packagetype)\n    )\n\n    return {\"project\": project, \"files\": files}\n", "path": "warehouse/legacy/api/simple.py"}]}