Skip to content

Commit 3e7d9a1

Browse files
authored
Merge branch 'main' into django-5.2
2 parents c1646fe + 72aef05 commit 3e7d9a1

29 files changed

Lines changed: 406 additions & 10950 deletions

.github/workflows/ci.yml

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,46 @@
11
name: CI
2-
on: [push, pull_request]
2+
3+
on: [push, pull_request, workflow_dispatch]
4+
35
jobs:
6+
migrations:
7+
if: github.event_name != 'push' || github.event.repository.fork == true
8+
runs-on: ubuntu-latest
9+
services:
10+
postgres:
11+
image: postgres:15.3
12+
env:
13+
POSTGRES_USER: postgres
14+
POSTGRES_PASSWORD: postgres
15+
POSTGRES_DB: pythonorg
16+
ports:
17+
- 5432:5432
18+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
19+
20+
steps:
21+
- uses: actions/checkout@v6
22+
23+
- uses: actions/setup-python@v6
24+
with:
25+
python-version-file: '.python-version'
26+
27+
- name: Cache pip
28+
uses: actions/cache@v5
29+
with:
30+
path: ~/.cache/pip
31+
key: ${{ runner.os }}-pip-${{ hashFiles('*-requirements.txt') }}
32+
restore-keys: ${{ runner.os }}-pip-
33+
34+
- name: Install dependencies
35+
run: pip install -r dev-requirements.txt
36+
37+
- name: Check for ungenerated migrations
38+
run: python manage.py makemigrations --check --dry-run
39+
env:
40+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/pythonorg
41+
442
test:
5-
# Avoid running CI more than once on pushes to main repo open PRs
6-
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
43+
if: github.event_name != 'push' || github.event.repository.fork == true
744
runs-on: ubuntu-latest
845
services:
946
postgres:
@@ -13,52 +50,46 @@ jobs:
1350
POSTGRES_PASSWORD: postgres
1451
POSTGRES_DB: pythonorg
1552
ports:
16-
- 5432:5432
17-
# needed because the postgres container does not provide a healthcheck
53+
- 5432:5432
1854
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
55+
1956
steps:
20-
- name: Check out repository
21-
uses: actions/checkout@v6
57+
- uses: actions/checkout@v6
58+
2259
- name: Install platform dependencies
2360
run: |
2461
sudo apt -y update
2562
sudo apt -y install --no-install-recommends \
26-
texlive-latex-base \
27-
texlive-latex-recommended \
28-
texlive-plain-generic \
29-
lmodern
63+
texlive-latex-base \
64+
texlive-latex-recommended \
65+
texlive-plain-generic \
66+
lmodern
67+
3068
- name: Install pandoc
3169
run: |
3270
wget https://github.com/jgm/pandoc/releases/download/2.17.1.1/pandoc-2.17.1.1-1-amd64.deb
3371
sudo dpkg -i pandoc-2.17.1.1-1-amd64.deb
72+
3473
- uses: actions/setup-python@v6
3574
with:
36-
python-version-file: '.python-version'
37-
- name: Cache Python dependencies
75+
python-version-file: '.python-version'
76+
77+
- name: Cache pip
3878
uses: actions/cache@v5
39-
env:
40-
cache-name: pythondotorg-cache-pip
4179
with:
4280
path: ~/.cache/pip
43-
key: ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-${{ hashFiles('requirements.txt', '*-requirements.txt') }}
44-
restore-keys: |
45-
${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-
46-
${{ runner.os }}-${{ github.job }}-
47-
${{ runner.os }}-
48-
- name: Install Python dependencies
81+
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', '*-requirements.txt') }}
82+
restore-keys: ${{ runner.os }}-pip-
83+
84+
- name: Install dependencies
4985
run: |
5086
pip install -U pip setuptools wheel
5187
pip install -r dev-requirements.txt
52-
- name: Check for ungenerated database migrations
53-
run: |
54-
python manage.py makemigrations --check --dry-run
55-
env:
56-
DATABASE_URL: postgres://postgres:postgres@localhost:5432/pythonorg
57-
- name: Run Tests
58-
run: |
59-
python -Wd -m coverage run manage.py test -v2
88+
89+
- name: Run tests
90+
run: python -Wd -m coverage run manage.py test -v2
6091
env:
6192
DATABASE_URL: postgres://postgres:postgres@localhost:5432/pythonorg
62-
- name: Coverage
63-
run: |
64-
coverage report -m --fail-under=75
93+
94+
- name: Check coverage
95+
run: coverage report -m --fail-under=75

.github/workflows/static.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
name: Check collectstatic
2-
on: [push, pull_request]
2+
3+
on: [push, pull_request, workflow_dispatch]
4+
35
jobs:
46
collectstatic:
5-
# Avoid running CI more than once on pushes to main repo open PRs
6-
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
7+
if: github.event_name != 'push' || github.event.repository.fork == true
78
runs-on: ubuntu-latest
89
steps:
910
- name: Check out repository
1011
uses: actions/checkout@v6
12+
1113
- uses: actions/setup-python@v6
1214
with:
1315
python-version-file: '.python-version'

base-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ requests>=2.26.0
3636
django-honeypot>=1.3.0
3737
django-markupfield==2.0.1
3838

39-
django-allauth==64.2.1
39+
django-allauth==65.13.0
4040

4141
django-waffle==2.2.1
4242

downloads/templatetags/download_tags.py

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,16 @@
44
import requests
55
from django import template
66
from django.core.cache import cache
7+
from django.utils.html import format_html
8+
9+
from downloads.models import Release
710

811
register = template.Library()
912
logger = logging.getLogger(__name__)
1013

11-
PYTHON_RELEASES_URL = "https://peps.python.org/api/python-releases.json"
12-
PYTHON_RELEASES_CACHE_KEY = "python_python_releases"
13-
PYTHON_RELEASES_CACHE_TIMEOUT = 3600 # 1 hour
14-
15-
16-
def get_python_releases_data() -> dict | None:
17-
"""Fetch and cache the Python release cycle data from PEPs API."""
18-
data = cache.get(PYTHON_RELEASES_CACHE_KEY)
19-
if data is not None:
20-
return data
21-
22-
try:
23-
response = requests.get(PYTHON_RELEASES_URL, timeout=5)
24-
response.raise_for_status()
25-
data = response.json()
26-
cache.set(PYTHON_RELEASES_CACHE_KEY, data, PYTHON_RELEASES_CACHE_TIMEOUT)
27-
return data
28-
except (requests.RequestException, ValueError) as e:
29-
logger.warning("Failed to fetch release cycle data: %s", e)
30-
return None
14+
RELEASE_CYCLE_URL = "https://peps.python.org/api/release-cycle.json"
15+
RELEASE_CYCLE_CACHE_KEY = "python_release_cycle"
16+
RELEASE_CYCLE_CACHE_TIMEOUT = 3600 # 1 hour
3117

3218

3319
@register.simple_tag
@@ -52,14 +38,12 @@ def get_eol_info(release) -> dict:
5238
major = int(match.group(1))
5339
minor_version = f"{match.group(1)}.{match.group(2)}"
5440

55-
python_releases = get_python_releases_data()
56-
if python_releases is None:
41+
release_cycle = get_release_cycle_data()
42+
if release_cycle is None:
5743
# Can't determine EOL status, don't show warning
5844
return result
5945

60-
metadata = python_releases.get("metadata", {})
61-
version_info = metadata.get(minor_version)
62-
46+
version_info = release_cycle.get(minor_version)
6347
if version_info is None:
6448
# Python 2 releases not in the list are EOL
6549
if major <= 2:
@@ -128,3 +112,73 @@ def sort_windows(files):
128112
other_files.append(file)
129113

130114
return other_files + windows_files
115+
116+
117+
def get_release_cycle_data() -> dict | None:
118+
"""Fetch and cache the release cycle data from PEPs API."""
119+
data = cache.get(RELEASE_CYCLE_CACHE_KEY)
120+
if data is not None:
121+
return data
122+
123+
try:
124+
response = requests.get(RELEASE_CYCLE_URL, timeout=5)
125+
response.raise_for_status()
126+
data = response.json()
127+
cache.set(RELEASE_CYCLE_CACHE_KEY, data, RELEASE_CYCLE_CACHE_TIMEOUT)
128+
return data
129+
except (requests.RequestException, ValueError) as e:
130+
logger.warning("Failed to fetch release cycle data: %s", e)
131+
return None
132+
133+
134+
@register.inclusion_tag("downloads/active-releases.html")
135+
def render_active_releases():
136+
"""Render the active Python releases table from PEPs API data."""
137+
releases = []
138+
release_cycle = get_release_cycle_data()
139+
140+
if release_cycle:
141+
# Sort releases in descending order (newest first)
142+
sorted_releases = sorted(
143+
release_cycle.keys(),
144+
key=lambda v: [int(x) for x in v.split(".")],
145+
reverse=True,
146+
)
147+
148+
found_eol = False
149+
for release in sorted_releases:
150+
info = release_cycle[release]
151+
status = info.get("status", "")
152+
first_release = info.get("first_release", "")
153+
154+
if status == "feature" and first_release:
155+
first_release = f"{first_release} (planned)"
156+
157+
if status == "feature":
158+
status = "pre-release"
159+
160+
if status == "end-of-life":
161+
# Include only the most recent EOL release
162+
if found_eol:
163+
continue
164+
found_eol = True
165+
166+
# Get last release for EOL versions
167+
minor = int(release.split(".")[1])
168+
last_release = Release.objects.latest_python3(minor)
169+
if last_release:
170+
status = format_html(
171+
'end-of-life, last release was <a href="{}">{}</a>',
172+
last_release.get_absolute_url(),
173+
last_release.get_version(),
174+
)
175+
176+
releases.append({
177+
"version": release,
178+
"status": status,
179+
"first_release": first_release,
180+
"end_of_life": info.get("end_of_life", ""),
181+
"pep": info.get("pep"),
182+
})
183+
184+
return {"releases": releases}

0 commit comments

Comments
 (0)