Skip to content

Commit 58c1b42

Browse files
authored
Sort release filenames naturally on details page (pypi#10438)
* add natsort dependency to project Signed-off-by: Mike Fiedler <[email protected]> * add failing test to express desired behavior Signed-off-by: Mike Fiedler <[email protected]> * add natural sorting to files passed to template Signed-off-by: Mike Fiedler <[email protected]>
1 parent edd39ff commit 58c1b42

File tree

4 files changed

+30
-1
lines changed

4 files changed

+30
-1
lines changed

requirements/main.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ limits
2424
lxml
2525
mistune
2626
msgpack
27+
natsort
2728
packaging>=15.2
2829
paginate>=0.5.2
2930
paginate_sqlalchemy

requirements/main.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,10 @@ msgpack==1.0.2 \
711711
--hash=sha256:fae04496f5bc150eefad4e9571d1a76c55d021325dcd484ce45065ebbdd00984 \
712712
--hash=sha256:fe07bc6735d08e492a327f496b7850e98cb4d112c56df69b0c844dbebcbb47f6
713713
# via -r requirements/main.in
714+
natsort==8.0.0 \
715+
--hash=sha256:5f5f4ea471d655b1b1611eef1cf0c6d3397095d2d3a1aab7098d6a50e4c3901a \
716+
--hash=sha256:a0a4fd71aee20a6d648da61e01180a63f7268e69983d0440bd3ad80ef1ba6981
717+
# via -r requirements/main.in
714718
packaging==21.3 \
715719
--hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
716720
--hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522

tests/unit/packaging/test_views.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import pretend
1414
import pytest
1515

16+
from natsort import natsorted
1617
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
1718

1819
from warehouse.packaging import views
@@ -271,6 +272,27 @@ def test_detail_renders(self, monkeypatch, db_request):
271272
pretend.call("unrendered description", "text/plain")
272273
]
273274

275+
def test_detail_renders_files_natural_sort(self, db_request):
276+
"""Tests that when a release has multiple versions of Python,
277+
the sort order is most recent Python version first."""
278+
project = ProjectFactory.create()
279+
release = ReleaseFactory.create(project=project, version="3.0")
280+
files = [
281+
FileFactory.create(
282+
release=release,
283+
filename="{}-{}-{}.tar.gz".format(
284+
project.name, release.version, py_ver
285+
),
286+
python_version="source",
287+
)
288+
for py_ver in ["cp27", "cp310", "cp39"] # intentionally out of order
289+
]
290+
sorted_files = natsorted(files, reverse=True, key=lambda f: f.filename)
291+
292+
result = views.release_detail(release, db_request)
293+
294+
assert result["files"] == sorted_files
295+
274296
def test_license_from_classifier(self, db_request):
275297
"""A license label is added when a license classifier exists."""
276298
other_classifier = ClassifierFactory.create(

warehouse/packaging/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
from natsort import natsorted
1314
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
1415
from pyramid.view import view_config
1516
from sqlalchemy.orm.exc import NoResultFound
@@ -125,7 +126,8 @@ def release_detail(release, request):
125126
"project": project,
126127
"release": release,
127128
"description": description,
128-
"files": release.files.all(),
129+
# We cannot easily sort naturally in SQL, sort here and pass to template
130+
"files": natsorted(release.files.all(), reverse=True, key=lambda f: f.filename),
129131
"latest_version": project.latest_version,
130132
"all_versions": project.all_versions,
131133
"maintainers": maintainers,

0 commit comments

Comments
 (0)