Skip to content

Updated "Status of Python versions" page #1531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
87837ba
Python version status: show when bugfix releases become security rele…
nedbat Mar 12, 2025
0f24453
Make them look all the same
encukou Mar 13, 2025
2926a88
Show EOL ones as red
encukou Mar 13, 2025
dc4f279
Two graphs
encukou Mar 13, 2025
8850c25
Remove unacceptable newline
encukou Mar 13, 2025
223f178
Join an unacceptably split line
encukou Mar 13, 2025
5813c1c
Hide the starts of EOL versions
encukou Mar 13, 2025
2cc3a35
Special-case 2.7
encukou Mar 13, 2025
06ac26d
Remove 3.5
encukou Mar 13, 2025
d2e56e6
Format
encukou Mar 13, 2025
3a4fe83
Put labels on top, if the mask doesn't work
encukou Mar 13, 2025
df95f8f
simplify the svg paths
nedbat Mar 13, 2025
583f4c5
Add more explanation to the Python versions Status key
nedbat Mar 17, 2025
e43873f
clarify what a feature fix is.
nedbat Mar 17, 2025
2bb7cd8
Update _tools/generate_release_cycle.py
nedbat Mar 17, 2025
e769654
Update _tools/generate_release_cycle.py
nedbat Mar 17, 2025
73c7d45
Update _tools/generate_release_cycle.py
nedbat Mar 17, 2025
b2975bc
Update _tools/generate_release_cycle.py
nedbat Mar 17, 2025
45a454b
Update _tools/generate_release_cycle.py
nedbat Mar 17, 2025
33b5f37
Apply suggestions from code review
nedbat Mar 17, 2025
3f36d68
3.13 gets two years of bug fixes, earlier gets 1.5 years
nedbat Mar 17, 2025
47df7c4
last changes from the review
nedbat Mar 17, 2025
80a44b7
use a consistent anchor for the full chart
nedbat Mar 18, 2025
584d48a
more tweaking of the phase descriptions
nedbat Mar 18, 2025
213f5fc
Use `height` consistently
encukou Mar 18, 2025
6ae8f9f
Clean up the path code
encukou Mar 18, 2025
70a2fe5
Remove the "shade" rectangle, keep left+right+border or a single rect
encukou Mar 18, 2025
38ad862
Use the same radius everywhere
encukou Mar 18, 2025
2ae3700
Rearrange comments/assignments
encukou Mar 18, 2025
5eb9243
No mask for active branches
encukou Mar 18, 2025
a83cd0f
Restore bold red color
encukou Mar 18, 2025
eb054b2
Update _tools/release_cycle_template.svg.jinja
encukou Mar 18, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ celerybeat-schedule
include/branches.csv
include/end-of-life.csv
include/release-cycle.svg
include/release-cycle-all.svg
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ REQUIREMENTS = requirements.txt
_ALL_SPHINX_OPTS = --jobs $(JOBS) $(SPHINXOPTS)
_RELEASE_CYCLE = include/branches.csv \
include/end-of-life.csv \
include/release-cycle-all.svg \
include/release-cycle.svg

.PHONY: help
Expand Down
56 changes: 39 additions & 17 deletions _static/devguide_overrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,57 @@
fill: white;
}

.release-cycle-chart .release-cycle-blob-label.release-cycle-blob-security,
.release-cycle-chart .release-cycle-blob-label.release-cycle-blob-bugfix {
.release-cycle-chart .release-cycle-blob-label.release-cycle-status-security,
.release-cycle-chart .release-cycle-blob-label.release-cycle-status-bugfix {
/* but use black to improve contrast for lighter backgrounds */
fill: black;
}

.release-cycle-chart .release-cycle-blob.release-cycle-blob-end-of-life {
fill: #DD2200;
stroke: #FF8888;
.release-cycle-chart .release-cycle-blob-label.release-cycle-status-end-of-life,
.release-cycle-chart .release-cycle-blob-label.release-cycle-status-feature {
/* and FG when it's not in a blob */
fill: var(--color-foreground-primary);
}

.release-cycle-chart .release-cycle-status-end-of-life {
--status-bg-color: #DD2200;
--status-border-color: #FF8888;
}

.release-cycle-chart .release-cycle-blob.release-cycle-blob-security {
fill: #FFDD44;
stroke: #FF8800;
.release-cycle-chart .release-cycle-status-security {
--status-bg-color: #FFDD44;
--status-border-color: #FF8800;
}

.release-cycle-chart .release-cycle-blob.release-cycle-blob-bugfix {
fill: #00DD22;
stroke: #008844;
.release-cycle-chart .release-cycle-status-bugfix {
--status-bg-color: #00DD22;
--status-border-color: #008844;
}

.release-cycle-chart .release-cycle-blob.release-cycle-blob-prerelease {
fill: teal;
stroke: darkgreen;
.release-cycle-chart .release-cycle-status-prerelease {
--status-bg-color: teal;
--status-border-color: darkgreen;
}

.release-cycle-chart .release-cycle-blob.release-cycle-blob-feature {
fill: #2222EE;
stroke: #008888;
.release-cycle-chart .release-cycle-status-feature {
--status-bg-color: #2222EE;
--status-border-color: #008888;
}

.release-cycle-chart .release-cycle-blob {
fill: var(--status-bg-color);
stroke: transparent;
}

.release-cycle-chart .release-cycle-blob-full {
fill: var(--status-bg-color);
stroke: var(--status-border-color);
}

.release-cycle-chart .release-cycle-border {
fill: transparent;
stroke: var(--status-border-color);
stroke-width: 1.6px;
}

.good pre {
Expand Down
73 changes: 60 additions & 13 deletions _tools/generate_release_cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,64 @@ def parse_date(date_str: str) -> dt.date:
return dt.date.fromisoformat(date_str)


def parse_version(ver: str) -> list[int]:
return [int(i) for i in ver["key"].split(".")]


class Versions:
"""For converting JSON to CSV and SVG."""

def __init__(self) -> None:
def __init__(self, *, limit_to_active=False, special_py27=False) -> None:
with open("include/release-cycle.json", encoding="UTF-8") as in_file:
self.versions = json.load(in_file)

# Generate a few additional fields
for key, version in self.versions.items():
version["key"] = key
version["first_release_date"] = parse_date(version["first_release"])
ver_info = parse_version(version)
if ver_info >= [3, 13]:
full_years = 2
else:
full_years = 1.5
version["first_release_date"] = r1 = parse_date(version["first_release"])
version["start_security_date"] = r1 + dt.timedelta(days=full_years * 365)
version["end_of_life_date"] = parse_date(version["end_of_life"])

self.cutoff = min(ver["first_release_date"] for ver in self.versions.values())

if limit_to_active:
self.cutoff = min(
version["first_release_date"]
for version in self.versions.values()
if version["status"] != "end-of-life"
)
self.versions = {
key: version
for key, version in self.versions.items()
if version["end_of_life_date"] >= self.cutoff
or (special_py27 and key == "2.7")
}
if special_py27:
self.cutoff = min(self.cutoff, dt.date(2019, 8, 1))
self.id_key = "active"
else:
self.id_key = "all"

self.sorted_versions = sorted(
self.versions.values(),
key=lambda v: [int(i) for i in v["key"].split(".")],
key=parse_version,
reverse=True,
)

# Set the row (Y coordinate) for the chart, to allow a gap between 2.7
# and the rest
y = len(self.sorted_versions) + (1 if special_py27 else 0)
for version in self.sorted_versions:
if special_py27 and version["key"] == "2.7":
y -= 1
version["y"] = y
y -= 1

def write_csv(self) -> None:
"""Output CSV files."""
now_str = str(dt.datetime.now(dt.timezone.utc))
Expand All @@ -68,7 +108,7 @@ def write_csv(self) -> None:
csv_file.writeheader()
csv_file.writerows(versions.values())

def write_svg(self, today: str) -> None:
def write_svg(self, today: str, out_path: str) -> None:
"""Output SVG file."""
env = jinja2.Environment(
loader=jinja2.FileSystemLoader("_tools/"),
Expand All @@ -85,6 +125,8 @@ def write_svg(self, today: str) -> None:
# CSS.
# (Ideally we'd actually use `em` units, but SVG viewBox doesn't take
# those.)

# Uppercase sizes are un-scaled
SCALE = 18

# Width of the drawing and main parts
Expand All @@ -96,7 +138,7 @@ def write_svg(self, today: str) -> None:
# some positioning numbers in the template as well.
LINE_HEIGHT = 1.5

first_date = min(ver["first_release_date"] for ver in self.sorted_versions)
first_date = self.cutoff
last_date = max(ver["end_of_life_date"] for ver in self.sorted_versions)

def date_to_x(date: dt.date) -> float:
Expand All @@ -105,7 +147,7 @@ def date_to_x(date: dt.date) -> float:
total_days = (last_date - first_date).days
ratio = num_days / total_days
x = ratio * (DIAGRAM_WIDTH - LEGEND_WIDTH - RIGHT_MARGIN)
return x + LEGEND_WIDTH
return (x + LEGEND_WIDTH) * SCALE

def year_to_x(year: int) -> float:
"""Convert year number to an SVG X coordinate of 1st January"""
Expand All @@ -115,20 +157,21 @@ def format_year(year: int) -> str:
"""Format year number for display"""
return f"'{year % 100:02}"

with open(
"include/release-cycle.svg", "w", encoding="UTF-8", newline="\n"
) as f:
with open(out_path, "w", encoding="UTF-8", newline="\n") as f:
template.stream(
SCALE=SCALE,
diagram_width=DIAGRAM_WIDTH,
diagram_height=(len(self.sorted_versions) + 2) * LINE_HEIGHT,
diagram_width=DIAGRAM_WIDTH * SCALE,
diagram_height=(self.sorted_versions[0]["y"] + 2) * LINE_HEIGHT * SCALE,
years=range(first_date.year, last_date.year + 1),
LINE_HEIGHT=LINE_HEIGHT,
line_height=LINE_HEIGHT * SCALE,
legend_width=LEGEND_WIDTH * SCALE,
right_margin=RIGHT_MARGIN * SCALE,
versions=list(reversed(self.sorted_versions)),
today=dt.datetime.strptime(today, "%Y-%m-%d").date(),
year_to_x=year_to_x,
date_to_x=date_to_x,
format_year=format_year,
id_key=self.id_key,
).dump(f)


Expand All @@ -145,8 +188,12 @@ def main() -> None:
args = parser.parse_args()

versions = Versions()
assert len(versions.versions) > 10
versions.write_csv()
versions.write_svg(args.today)
versions.write_svg(args.today, "include/release-cycle-all.svg")

versions = Versions(limit_to_active=True, special_py27=True)
versions.write_svg(args.today, "include/release-cycle.svg")


if __name__ == "__main__":
Expand Down
Loading
Loading