Skip to content

Commit 19485e4

Browse files
authored
Merge pull request #6012 from readthedocs/davidfischer/handle-x-in-version-sorting
Handle .x in version sorting
2 parents f8fac48 + a2e66e3 commit 19485e4

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

readthedocs/projects/version_handling.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ def parse_version_failsafe(version_string):
1616
"""
1717
Parse a version in string form and return Version object.
1818
19-
If there is an error parsing the string, ``None`` is returned.
19+
If there is an error parsing the string
20+
or the version doesn't have a "comparable" version number,
21+
``None`` is returned.
2022
2123
:param version_string: version as string object (e.g. '3.10.1')
2224
:type version_string: str or unicode
@@ -30,13 +32,21 @@ def parse_version_failsafe(version_string):
3032
else:
3133
uni_version = version_string
3234

35+
final_form = ''
36+
3337
try:
3438
normalized_version = unicodedata.normalize('NFKD', uni_version)
3539
ascii_version = normalized_version.encode('ascii', 'ignore')
3640
final_form = ascii_version.decode('ascii')
3741
return Version(final_form)
38-
except (UnicodeError, InvalidVersion):
39-
return None
42+
except InvalidVersion:
43+
# Handle the special case of 1.x, 2.x or 1.0.x, 1.1.x
44+
if final_form and '.x' in final_form:
45+
return parse_version_failsafe(final_form.replace('.x', '.0'))
46+
except UnicodeError:
47+
pass
48+
49+
return None
4050

4151

4252
def comparable_version(version_string):
@@ -69,6 +79,9 @@ def sort_versions(version_list):
6979
"""
7080
Take a list of Version models and return a sorted list.
7181
82+
This only considers versions with comparable version numbers.
83+
It excludes versions like "latest" and "stable".
84+
7285
:param version_list: list of Version models
7386
:type version_list: list(readthedocs.builds.models.Version)
7487
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from django.test import TestCase
2+
from django_dynamic_fixture import get
3+
4+
from readthedocs.builds.constants import BRANCH
5+
from readthedocs.builds.models import Version
6+
from readthedocs.projects.models import Project
7+
from readthedocs.projects.templatetags.projects_tags import sort_version_aware
8+
9+
10+
class SortVersionsTest(TestCase):
11+
12+
def setUp(self):
13+
self.project = get(Project)
14+
15+
def test_basic_sort(self):
16+
identifiers = ['1.0', '2.0', '1.1', '1.9', '1.10']
17+
for identifier in identifiers:
18+
get(
19+
Version,
20+
project=self.project,
21+
type=BRANCH,
22+
identifier=identifier,
23+
verbose_name=identifier,
24+
slug=identifier,
25+
)
26+
27+
versions = list(Version.objects.filter(project=self.project))
28+
self.assertEqual(
29+
['latest', '2.0', '1.10', '1.9', '1.1', '1.0'],
30+
[v.slug for v in sort_version_aware(versions)],
31+
)
32+
33+
def test_sort_wildcard(self):
34+
identifiers = ['1.0.x', '2.0.x', '1.1.x', '1.9.x', '1.10.x']
35+
for identifier in identifiers:
36+
get(
37+
Version,
38+
project=self.project,
39+
type=BRANCH,
40+
identifier=identifier,
41+
verbose_name=identifier,
42+
slug=identifier,
43+
)
44+
45+
versions = list(Version.objects.filter(project=self.project))
46+
self.assertEqual(
47+
['latest', '2.0.x', '1.10.x', '1.9.x', '1.1.x', '1.0.x'],
48+
[v.slug for v in sort_version_aware(versions)],
49+
)
50+
51+
def test_sort_alpha(self):
52+
identifiers = ['banana', 'apple', 'carrot']
53+
for identifier in identifiers:
54+
get(
55+
Version,
56+
project=self.project,
57+
type=BRANCH,
58+
identifier=identifier,
59+
verbose_name=identifier,
60+
slug=identifier,
61+
)
62+
63+
versions = list(Version.objects.filter(project=self.project))
64+
self.assertEqual(
65+
['latest', 'carrot', 'banana', 'apple'],
66+
[v.slug for v in sort_version_aware(versions)],
67+
)

0 commit comments

Comments
 (0)