Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 10 additions & 19 deletions downloads/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ def pymanager(self):
def latest_python2(self):
return self.python2().filter(is_latest=True)

def latest_python3(self):
return self.python3().filter(is_latest=True)
def latest_python3(self, minor_version: int | None = None):
if minor_version is None:
return self.python3().filter(is_latest=True)
pattern = rf"^Python 3\.{minor_version}\."
return self.python3().filter(name__regex=pattern).order_by("-release_date")

def latest_pymanager(self):
return self.pymanager().filter(is_latest=True)
Expand All @@ -44,22 +47,10 @@ def released(self):

class ReleaseManager(Manager.from_queryset(ReleaseQuerySet)):
def latest_python2(self):
qs = self.get_queryset().latest_python2()
if qs:
return qs[0]
else:
return None

def latest_python3(self):
qs = self.get_queryset().latest_python3()
if qs:
return qs[0]
else:
return None
return self.get_queryset().latest_python2().first()

def latest_python3(self, minor_version: int | None = None):
return self.get_queryset().latest_python3(minor_version).first()

def latest_pymanager(self):
qs = self.get_queryset().latest_pymanager()
if qs:
return qs[0]
else:
return None
return self.get_queryset().latest_pymanager().first()
6 changes: 6 additions & 0 deletions downloads/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ def purge_fastly_download_pages(sender, instance, **kwargs):
purge_url('/downloads/feed.rss')
purge_url('/downloads/latest/python2/')
purge_url('/downloads/latest/python3/')
# Purge minor version specific URLs (like /downloads/latest/python3.14/)
version = instance.get_version()
if instance.version == Release.PYTHON3 and version:
match = re.match(r'^3\.(\d+)', version)
if match:
purge_url(f'/downloads/latest/python3.{match.group(1)}/')
purge_url('/downloads/latest/pymanager/')
purge_url('/downloads/macos/')
purge_url('/downloads/source/')
Expand Down
1 change: 1 addition & 0 deletions downloads/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
urlpatterns = [
re_path(r'latest/python2/?$', views.DownloadLatestPython2.as_view(), name='download_latest_python2'),
re_path(r'latest/python3/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'),
re_path(r'latest/python3\.(?P<minor>\d+)/?$', views.DownloadLatestPython3x.as_view(), name='download_latest_python3x'),
re_path(r'latest/pymanager/?$', views.DownloadLatestPyManager.as_view(), name='download_latest_pymanager'),
re_path(r'latest/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'),
path('operating-systems/', views.DownloadFullOSList.as_view(), name='download_full_os_list'),
Expand Down
21 changes: 21 additions & 0 deletions downloads/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ def get_redirect_url(self, **kwargs):
return reverse('download')


class DownloadLatestPython3x(RedirectView):
""" Redirect to latest Python 3.x release for a specific minor version """
permanent = False

def get_redirect_url(self, **kwargs):
minor_version = kwargs.get('minor')
if not minor_version:
return reverse('downloads:download')

try:
minor_version_int = int(minor_version)
latest_release = Release.objects.latest_python3(minor_version_int)
except (ValueError, Release.DoesNotExist):
latest_release = None

if latest_release:
return latest_release.get_absolute_url()
else:
return reverse('downloads:download')


class DownloadLatestPyManager(RedirectView):
""" Redirect to latest Python install manager release """
permanent = False
Expand Down
8 changes: 5 additions & 3 deletions static/sass/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@

.release-version,
.release-status,
.release-dl,
.release-start,
.release-end,
.release-pep {
Expand All @@ -458,10 +459,11 @@
vertical-align: middle;
}

.release-version { width: 15%; }
.release-version { width: 10%; }
.release-status { width: 20%; }
.release-start { width: 25%; }
.release-end { width: 25%; }
.release-dl { width: 15%; }
.release-start { width: 20%; }
.release-end { width: 20%; }
.release-pep { width: 15%; }

/* Previous Next pattern */
Expand Down
10 changes: 7 additions & 3 deletions static/sass/mq.css
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,7 @@ html[xmlns] .slides { display: block; }

.release-version,
.release-status,
.release-dl,
.release-start,
.release-end,
.release-pep {
Expand All @@ -1514,16 +1515,19 @@ html[xmlns] .slides { display: block; }
vertical-align: middle; }

.release-version {
width: 15%; }
width: 10%; }

.release-status {
width: 20%; }

.release-dl {
width: 15%; }

.release-start {
width: 25%; }
width: 20%; }

.release-end {
width: 25%; }
width: 20%; }

.release-pep {
width: 15%; }
Expand Down
10 changes: 7 additions & 3 deletions static/sass/no-mq.css
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,7 @@ a.button {

.release-version,
.release-status,
.release-dl,
.release-start,
.release-end,
.release-pep {
Expand All @@ -1228,16 +1229,19 @@ a.button {
vertical-align: middle; }

.release-version {
width: 15%; }
width: 10%; }

.release-status {
width: 20%; }

.release-dl {
width: 15%; }

.release-start {
width: 25%; }
width: 20%; }

.release-end {
width: 25%; }
width: 20%; }

.release-pep {
width: 15%; }
Expand Down