From 87837badd2de342bcc6e046285d9b434274552e7 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 12 Mar 2025 14:54:53 -0400 Subject: [PATCH 01/32] Python version status: show when bugfix releases become security releases --- _tools/generate_release_cycle.py | 3 +- _tools/release_cycle_template.svg.jinja | 100 +++++++++++++++++++----- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 3a8fefec0..38b365cfd 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -35,7 +35,8 @@ def __init__(self) -> None: # Generate a few additional fields for key, version in self.versions.items(): version["key"] = key - version["first_release_date"] = parse_date(version["first_release"]) + version["first_release_date"] = r1 = parse_date(version["first_release"]) + version["start_security_date"] = r1 + dt.timedelta(days=2 * 365) version["end_of_life_date"] = parse_date(version["end_of_life"]) self.sorted_versions = sorted( self.versions.values(), diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 5d39d307a..6c3b6129d 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -58,25 +58,87 @@ {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} - {% set mid_x = (start_x + end_x) / 2 %} - - - {{ version.status }} - + + {% if version.status == "bugfix" %} + + {% set half_x = date_to_x(version.start_security_date) %} + {% set height = 1.25 * SCALE %} + {% set left_width = (half_x - start_x) * SCALE %} + {% set right_width = (end_x - half_x) * SCALE %} + {% set left_x = start_x * SCALE %} + {% set middle_x = half_x * SCALE %} + {% set right_x = half_x * SCALE %} + {% set recty = (y - 1) * SCALE %} + {% set radius_value = 0.25 * SCALE %} + + + + bugfix + + + + + security + + {% else %} + + + + {{ version.status }} + + {% endif %} {% endfor %} From 0f24453a46dae05e4a86b675dd93c46c3b31e83d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 11:08:43 +0100 Subject: [PATCH 02/32] Make them look all the same --- _static/devguide_overrides.css | 58 +++++--- _tools/release_cycle_template.svg.jinja | 170 ++++++++++++++---------- 2 files changed, 140 insertions(+), 88 deletions(-) diff --git a/_static/devguide_overrides.css b/_static/devguide_overrides.css index 8e2c7c6fc..a92243205 100644 --- a/_static/devguide_overrides.css +++ b/_static/devguide_overrides.css @@ -45,38 +45,60 @@ .release-cycle-chart .release-cycle-blob-label { /* white looks good on both light & dark */ - fill: white; + fill: var(--color-foreground-primary); } -.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-status-end-of-life { + --status-bg-color: #DD2200; + --status-border-color: #FF8888; +} + +.release-cycle-chart .release-cycle-status-security { + --status-bg-color: #FFDD44; + --status-border-color: #FF8800; } -.release-cycle-chart .release-cycle-blob.release-cycle-blob-security { - fill: #FFDD44; - stroke: #FF8800; +.release-cycle-chart .release-cycle-status-bugfix { + --status-bg-color: #00DD22; + --status-border-color: #008844; } -.release-cycle-chart .release-cycle-blob.release-cycle-blob-bugfix { - fill: #00DD22; - stroke: #008844; +.release-cycle-chart .release-cycle-status-prerelease { + --status-bg-color: teal; + --status-border-color: darkgreen; } -.release-cycle-chart .release-cycle-blob.release-cycle-blob-prerelease { - fill: teal; - stroke: darkgreen; +.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.release-cycle-blob-feature { - fill: #2222EE; - stroke: #008888; +.release-cycle-chart .release-cycle-border { + fill: transparent; + stroke: var(--status-border-color); + stroke-width: 1.6px; +} + +.release-cycle-chart .release-cycle-shade { + fill: transparent; + stroke: transparent; + opacity: 50%; + + &.release-cycle-status-end-of-life, + &.release-cycle-status-feature { + fill: var(--color-background-primary); + } } .good pre { diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 6c3b6129d..6e6325c90 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -59,86 +59,116 @@ {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} - {% if version.status == "bugfix" %} - - {% set half_x = date_to_x(version.start_security_date) %} - {% set height = 1.25 * SCALE %} - {% set left_width = (half_x - start_x) * SCALE %} - {% set right_width = (end_x - half_x) * SCALE %} - {% set left_x = start_x * SCALE %} - {% set middle_x = half_x * SCALE %} - {% set right_x = half_x * SCALE %} - {% set recty = (y - 1) * SCALE %} - {% set radius_value = 0.25 * SCALE %} + + {% set half_x = [end_x, date_to_x(version.start_security_date)]|min %} + {% set height = 1.25 * SCALE %} + {% set left_width = (half_x - start_x) * SCALE %} + {% set right_width = (end_x - half_x) * SCALE %} + {% set left_x = start_x * SCALE %} + {% set middle_x = half_x * SCALE %} + {% set right_x = half_x * SCALE %} + {% set recty = (y - 1) * SCALE %} + {% set radius_value = 0.25 * SCALE %} - - - bugfix - + - - - security - - {% else %} - - - - {{ version.status }} - + " + /> {% endif %} + + {% if version.status == "bugfix" %} + + bugfix + + {% elif version.status == "security" %} + + security + + {% elif version.status == "end-of-life" %} + + end-of-life + + {% elif version.status == "feature" %} + + feature + + {% endif %} + + {% endfor %} From 2926a88441da6b14f8c32313dd870e639e725952 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 11:24:51 +0100 Subject: [PATCH 03/32] Show EOL ones as red --- _static/devguide_overrides.css | 14 ++- _tools/release_cycle_template.svg.jinja | 144 ++++++++++++------------ 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/_static/devguide_overrides.css b/_static/devguide_overrides.css index a92243205..ac8f9489a 100644 --- a/_static/devguide_overrides.css +++ b/_static/devguide_overrides.css @@ -45,7 +45,7 @@ .release-cycle-chart .release-cycle-blob-label { /* white looks good on both light & dark */ - fill: var(--color-foreground-primary); + fill: white; } .release-cycle-chart .release-cycle-blob-label.release-cycle-status-security, @@ -54,6 +54,11 @@ fill: black; } +.release-cycle-chart .release-cycle-blob-label.release-cycle-status-feature { + /* and FG when it's not in a pill */ + fill: var(--color-foreground-primary); +} + .release-cycle-chart .release-cycle-status-end-of-life { --status-bg-color: #DD2200; --status-border-color: #FF8888; @@ -93,11 +98,14 @@ .release-cycle-chart .release-cycle-shade { fill: transparent; stroke: transparent; - opacity: 50%; - &.release-cycle-status-end-of-life, + &.release-cycle-status-end-of-life { + fill: #DD2200; + stroke: #FF8888; + } &.release-cycle-status-feature { fill: var(--color-background-primary); + opacity: 50%; } } diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 6e6325c90..030de987f 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -73,83 +73,33 @@ {% set recty = (y - 1) * SCALE %} {% set radius_value = 0.25 * SCALE %} - - - {% if version.key != "3.0" %} - + - {% endif %} - - {% if version.status == "bugfix" %} - - bugfix - - {% elif version.status == "security" %} - - security - - {% elif version.status == "end-of-life" %} - - end-of-life - - {% elif version.status == "feature" %} - - feature - + " + /> + {% endif %} + {% if version.status == "bugfix" %} + + bugfix + + {% elif version.status == "security" %} + + security + + {% elif version.status == "end-of-life" %} + + end-of-life + + {% elif version.status == "feature" %} + + feature + + {% endif %} {% endfor %} From dc4f2792b0813df3af3949ff3506f5f5be380a2e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 11:43:59 +0100 Subject: [PATCH 04/32] Two graphs --- .gitignore | 1 + Makefile | 1 + _tools/generate_release_cycle.py | 27 +++++++++++++++++++++++---- make.ps1 | 3 ++- versions.rst | 16 ++++++++++++---- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index df4dc9415..b71249201 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,4 @@ celerybeat-schedule include/branches.csv include/end-of-life.csv include/release-cycle.svg +include/release-cycle-all.svg diff --git a/Makefile b/Makefile index 5a33d5089..6baf33b32 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 38b365cfd..e92ccdf38 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -28,7 +28,7 @@ def parse_date(date_str: str) -> dt.date: class Versions: """For converting JSON to CSV and SVG.""" - def __init__(self) -> None: + def __init__(self, limit_to_active=False) -> None: with open("include/release-cycle.json", encoding="UTF-8") as in_file: self.versions = json.load(in_file) @@ -38,6 +38,20 @@ def __init__(self) -> None: version["first_release_date"] = r1 = parse_date(version["first_release"]) version["start_security_date"] = r1 + dt.timedelta(days=2 * 365) version["end_of_life_date"] = parse_date(version["end_of_life"]) + + if limit_to_active: + 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"] >= cutoff + } + + self.sorted_versions = sorted( self.versions.values(), key=lambda v: [int(i) for i in v["key"].split(".")], @@ -69,7 +83,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/"), @@ -117,7 +131,7 @@ def format_year(year: int) -> str: return f"'{year % 100:02}" with open( - "include/release-cycle.svg", "w", encoding="UTF-8", newline="\n" + out_path, "w", encoding="UTF-8", newline="\n" ) as f: template.stream( SCALE=SCALE, @@ -146,8 +160,13 @@ def main() -> None: args = parser.parse_args() versions = Versions() + print(versions.versions.keys()) + 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) + versions.write_svg(args.today, "include/release-cycle.svg") if __name__ == "__main__": diff --git a/make.ps1 b/make.ps1 index 71a8f56f4..4cdc6b20c 100644 --- a/make.ps1 +++ b/make.ps1 @@ -64,7 +64,8 @@ if ($target -Eq "clean") { $ToClean = @( $BUILDDIR, $_VENV_DIR, - "include/branches.csv", "include/end-of-life.csv", "include/release-cycle.svg" + "include/branches.csv", "include/end-of-life.csv", + "include/release-cycle.svg", "include/release-cycle-all.svg" ) foreach ($item in $ToClean) { if (Test-Path -Path $item) { diff --git a/versions.rst b/versions.rst index db7f94682..d6aac12b3 100644 --- a/versions.rst +++ b/versions.rst @@ -10,13 +10,12 @@ branch that accepts new features. The latest release for each Python version can be found on the `download page `_. -Python release cycle -==================== - .. raw:: html :file: include/release-cycle.svg -Another useful visualization is `endoflife.date/python `_. +(See :ref:`below ` for a chart with older versions. +Another useful visualization is `endoflife.date/python `_.) + Supported versions ================== @@ -40,6 +39,15 @@ Unsupported versions :file: include/end-of-life.csv +.. _versions-chart-all: + +Full chart +========== + +.. raw:: html + :file: include/release-cycle-all.svg + + Status key ========== From 8850c25e50d80a454a4475a714349754373c14a4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 11:48:49 +0100 Subject: [PATCH 05/32] Remove unacceptable newline --- _tools/generate_release_cycle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index e92ccdf38..7e0e33005 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -51,7 +51,6 @@ def __init__(self, limit_to_active=False) -> None: if version["end_of_life_date"] >= cutoff } - self.sorted_versions = sorted( self.versions.values(), key=lambda v: [int(i) for i in v["key"].split(".")], From 223f1782031d8af8f0e8b5607ca07a2e8a66eb87 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 11:52:34 +0100 Subject: [PATCH 06/32] Join an unacceptably split line --- _tools/generate_release_cycle.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 7e0e33005..ecc1c300d 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -129,9 +129,7 @@ def format_year(year: int) -> str: """Format year number for display""" return f"'{year % 100:02}" - with open( - out_path, "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, From 5813c1cd071419bbc75aba2f962cafb1c0e9942d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 13:16:44 +0100 Subject: [PATCH 07/32] Hide the starts of EOL versions --- _static/devguide_overrides.css | 3 +- _tools/generate_release_cycle.py | 15 ++++++-- _tools/release_cycle_template.svg.jinja | 48 +++++++++++++++++++------ 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/_static/devguide_overrides.css b/_static/devguide_overrides.css index ac8f9489a..a048e1c36 100644 --- a/_static/devguide_overrides.css +++ b/_static/devguide_overrides.css @@ -54,8 +54,9 @@ fill: black; } +.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 pill */ + /* and FG when it's not in a blob */ fill: var(--color-foreground-primary); } diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index ecc1c300d..f1a16407e 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -39,8 +39,10 @@ def __init__(self, limit_to_active=False) -> None: version["start_security_date"] = r1 + dt.timedelta(days=2 * 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: - cutoff = min( + self.cutoff = min( version["first_release_date"] for version in self.versions.values() if version["status"] != 'end-of-life' @@ -48,8 +50,11 @@ def __init__(self, limit_to_active=False) -> None: self.versions = { key: version for key, version in self.versions.items() - if version["end_of_life_date"] >= cutoff + if version["end_of_life_date"] >= self.cutoff } + self.id_key='active' + else: + self.id_key='all' self.sorted_versions = sorted( self.versions.values(), @@ -57,6 +62,7 @@ def __init__(self, limit_to_active=False) -> None: reverse=True, ) + def write_csv(self) -> None: """Output CSV files.""" now_str = str(dt.datetime.now(dt.timezone.utc)) @@ -110,7 +116,7 @@ def write_svg(self, today: str, out_path: 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: @@ -136,11 +142,14 @@ def format_year(year: int) -> str: diagram_height=(len(self.sorted_versions) + 2) * LINE_HEIGHT, years=range(first_date.year, last_date.year + 1), LINE_HEIGHT=LINE_HEIGHT, + LEGEND_WIDTH=LEGEND_WIDTH, + RIGHT_MARGIN=RIGHT_MARGIN, 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) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 030de987f..f7b38ea67 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -4,6 +4,12 @@ class="release-cycle-chart" viewBox="0 0 {{ diagram_width * SCALE }} {{ diagram_height * SCALE }}" > + + + + + + {% for version in versions %} {% set y = loop.index * LINE_HEIGHT %} @@ -42,6 +48,31 @@ {% endif %} {% endfor %} + + + + + + + {% for version in versions %} {% set y = loop.index * LINE_HEIGHT %} @@ -74,7 +105,7 @@ {% set radius_value = 0.25 * SCALE %} {% if version.status != "end-of-life" %} - + {% if version.status == "bugfix" %} end-of-life - {% elif version.status == "feature" %} + {% else %} - feature + {{ version.status }} {% endif %} {% endfor %} From 2cc3a3517645bd1b54b0332d8ca6e78eec0eaa19 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 14:26:01 +0100 Subject: [PATCH 08/32] Special-case 2.7 --- _tools/generate_release_cycle.py | 18 +++++++++++++++--- _tools/release_cycle_template.svg.jinja | 6 +++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index f1a16407e..87b4f39a0 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -28,7 +28,7 @@ def parse_date(date_str: str) -> dt.date: class Versions: """For converting JSON to CSV and SVG.""" - def __init__(self, limit_to_active=False) -> 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) @@ -47,10 +47,13 @@ def __init__(self, limit_to_active=False) -> None: for version in self.versions.values() if version["status"] != 'end-of-life' ) + if special_py27: + self.cutoff = min(self.cutoff, dt.date(2019, 8, 1)) 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') } self.id_key='active' else: @@ -62,6 +65,15 @@ def __init__(self, limit_to_active=False) -> None: 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.""" @@ -139,7 +151,7 @@ def format_year(year: int) -> str: template.stream( SCALE=SCALE, diagram_width=DIAGRAM_WIDTH, - diagram_height=(len(self.sorted_versions) + 2) * LINE_HEIGHT, + diagram_height=(self.sorted_versions[0]["y"] + 2) * LINE_HEIGHT, years=range(first_date.year, last_date.year + 1), LINE_HEIGHT=LINE_HEIGHT, LEGEND_WIDTH=LEGEND_WIDTH, @@ -171,7 +183,7 @@ def main() -> None: versions.write_csv() versions.write_svg(args.today, "include/release-cycle-all.svg") - versions = Versions(limit_to_active=True) + versions = Versions(limit_to_active=True, special_py27=True) versions.write_svg(args.today, "include/release-cycle.svg") diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index f7b38ea67..5fa1f825e 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -12,9 +12,9 @@ {% for version in versions %} - {% set y = loop.index * LINE_HEIGHT %} + {% set y = version.y * LINE_HEIGHT %} - {% if loop.index % 2 %} + {% if version.y % 2 %} {% for version in versions %} - {% set y = loop.index * LINE_HEIGHT %} + {% set y = version.y * LINE_HEIGHT %} Date: Thu, 13 Mar 2025 14:27:20 +0100 Subject: [PATCH 09/32] Remove 3.5 --- _tools/generate_release_cycle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 87b4f39a0..4457dacf9 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -47,14 +47,14 @@ def __init__(self, limit_to_active=False, special_py27=False) -> None: for version in self.versions.values() if version["status"] != 'end-of-life' ) - if special_py27: - self.cutoff = min(self.cutoff, dt.date(2019, 8, 1)) 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' From d2e56e6720d555ff90c2f14e4b42a517991f8033 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 14:28:33 +0100 Subject: [PATCH 10/32] Format --- _tools/generate_release_cycle.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 4457dacf9..0c401ebbe 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -55,9 +55,9 @@ def __init__(self, limit_to_active=False, special_py27=False) -> None: } if special_py27: self.cutoff = min(self.cutoff, dt.date(2019, 8, 1)) - self.id_key='active' + self.id_key = 'active' else: - self.id_key='all' + self.id_key = 'all' self.sorted_versions = sorted( self.versions.values(), @@ -74,7 +74,6 @@ def __init__(self, limit_to_active=False, special_py27=False) -> None: version["y"] = y y -= 1 - def write_csv(self) -> None: """Output CSV files.""" now_str = str(dt.datetime.now(dt.timezone.utc)) From 3a4fe83b4ce48dee9135531c5cd73754f1e1842d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 13 Mar 2025 15:22:27 +0100 Subject: [PATCH 11/32] Put labels on top, if the mask doesn't work --- _tools/release_cycle_template.svg.jinja | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 5fa1f825e..b39d425f8 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -76,16 +76,6 @@ {% for version in versions %} {% set y = version.y * LINE_HEIGHT %} - - - Python {{ version.key }} - - {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} @@ -193,6 +183,16 @@ {{ version.status }} {% endif %} + + + + Python {{ version.key }} + {% endfor %} From df95f8f648db2e8d6ae041723715c18bb66272fa Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 13 Mar 2025 12:51:33 -0400 Subject: [PATCH 12/32] simplify the svg paths --- _tools/release_cycle_template.svg.jinja | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index b39d425f8..3da80fff0 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -102,7 +102,7 @@ M{{ left_x + radius_value }},{{ recty }} q{{ -radius_value }},0 {{ -radius_value }},{{ radius_value }} v{{ height - 2*radius_value }} - Q{{ left_x }},{{ recty + height }} {{ left_x + radius_value }},{{ recty + height }} + q0,{{ radius_value }} {{ radius_value }},{{ radius_value }} H{{ middle_x }} V{{ recty }} Z @@ -111,14 +111,14 @@ {% endif %} From 583f4c5357a57d2db8a498700ae17d1a303796d6 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 15:44:12 -0400 Subject: [PATCH 13/32] Add more explanation to the Python versions Status key --- versions.rst | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/versions.rst b/versions.rst index d6aac12b3..af3016f8d 100644 --- a/versions.rst +++ b/versions.rst @@ -51,14 +51,24 @@ Full chart Status key ========== -:feature: new features, bugfixes, and security fixes are accepted. -:prerelease: feature fixes, bugfixes, and security fixes are accepted for the - upcoming feature release. -:bugfix: bugfixes and security fixes are accepted, new binaries are still - released. (Also called **maintenance** mode or **stable** release) -:security: only security fixes are accepted and no more binaries are released, - but new source-only versions can be released -:end-of-life: release cycle is frozen; no further changes can be pushed to it. +Python releases go through five phases: + +:feature: Before the first beta, the next full release can accept new features, + bug fixes, and security fixes. + +:prerelease: After the first beta, no new features can go in, but feature + fixes, bug fixes, and security fixes are accepted for the upcoming feature + release. + +:bugfix: Once a version has been released, bug fixes and security fixes are + accepted. New binaries are built and released. (Also called **maintenance** + mode or **stable** release) + +:security: After two years, only security fixes are accepted and no more + binaries are released. New source-only versions can be released as needed. + +:end-of-life: Five years after a release, support ends. The release cycle is + frozen; no further changes can be pushed to it. See also the :ref:`devcycle` page for more information about branches and backporting. From e43873f4fb095de4f9461ee8e217ef4b74866097 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 16:08:52 -0400 Subject: [PATCH 14/32] clarify what a feature fix is. --- versions.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/versions.rst b/versions.rst index af3016f8d..e5cce3d6d 100644 --- a/versions.rst +++ b/versions.rst @@ -56,9 +56,9 @@ Python releases go through five phases: :feature: Before the first beta, the next full release can accept new features, bug fixes, and security fixes. -:prerelease: After the first beta, no new features can go in, but feature - fixes, bug fixes, and security fixes are accepted for the upcoming feature - release. +:prerelease: After the first beta, no new features can go in, but feature fixes + (including significant changes to new features), bug fixes, and security fixes + are accepted for the upcoming feature release. :bugfix: Once a version has been released, bug fixes and security fixes are accepted. New binaries are built and released. (Also called **maintenance** From 2bb7cd8c215c3831b07fe1e9c9d02d1fbccf76ee Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:00:59 -0400 Subject: [PATCH 15/32] Update _tools/generate_release_cycle.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 0c401ebbe..d79908e30 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -28,7 +28,7 @@ def parse_date(date_str: str) -> dt.date: class Versions: """For converting JSON to CSV and SVG.""" - def __init__(self, limit_to_active=False, special_py27=False) -> 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) From e769654898dfadc1986e46e37edc413b684b4882 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:01:32 -0400 Subject: [PATCH 16/32] Update _tools/generate_release_cycle.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index d79908e30..932c4d3a3 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -45,7 +45,7 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: self.cutoff = min( version["first_release_date"] for version in self.versions.values() - if version["status"] != 'end-of-life' + if version["status"] != "end-of-life" ) self.versions = { key: version From 73c7d45f0c5c77b440e8869c67d840b29e0e8340 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:01:47 -0400 Subject: [PATCH 17/32] Update _tools/generate_release_cycle.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 932c4d3a3..eeec38b2a 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -51,7 +51,7 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: key: version for key, version in self.versions.items() if version["end_of_life_date"] >= self.cutoff - or (special_py27 and key == '2.7') + or (special_py27 and key == "2.7") } if special_py27: self.cutoff = min(self.cutoff, dt.date(2019, 8, 1)) From b2975bc962a73ca1944be950627a27e7c679a236 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:01:59 -0400 Subject: [PATCH 18/32] Update _tools/generate_release_cycle.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index eeec38b2a..2f67699fc 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -55,9 +55,9 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: } if special_py27: self.cutoff = min(self.cutoff, dt.date(2019, 8, 1)) - self.id_key = 'active' + self.id_key = "active" else: - self.id_key = 'all' + self.id_key = "all" self.sorted_versions = sorted( self.versions.values(), From 45a454bedaf409ce89da6f29cc1ef26dd1530afd Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:02:17 -0400 Subject: [PATCH 19/32] Update _tools/generate_release_cycle.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 2f67699fc..b5c7670fd 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -65,7 +65,7 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: reverse=True, ) - # Set the row (y-coordinate) for the chart, to allow a gap between 2.7 + # 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: From 33b5f37a47d8a60f0e78df648d170dae64186bbb Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:03:58 -0400 Subject: [PATCH 20/32] Apply suggestions from code review Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- _tools/generate_release_cycle.py | 2 +- _tools/release_cycle_template.svg.jinja | 3 +-- make.ps1 | 2 +- versions.rst | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index b5c7670fd..ca0cf3cb6 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -69,7 +69,7 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: # 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': + if special_py27 and version["key"] == "2.7": y -= 1 version["y"] = y y -= 1 diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 3da80fff0..3a69d1657 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -80,9 +80,8 @@ {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} - {% set half_x = [end_x, date_to_x(version.start_security_date)]|min %} {% set height = 1.25 * SCALE %} diff --git a/make.ps1 b/make.ps1 index 4cdc6b20c..d6b5f3293 100644 --- a/make.ps1 +++ b/make.ps1 @@ -65,7 +65,7 @@ if ($target -Eq "clean") { $BUILDDIR, $_VENV_DIR, "include/branches.csv", "include/end-of-life.csv", - "include/release-cycle.svg", "include/release-cycle-all.svg" + "include/release-cycle.svg", "include/release-cycle-all.svg" ) foreach ($item in $ToClean) { if (Test-Path -Path $item) { diff --git a/versions.rst b/versions.rst index e5cce3d6d..85414ceb7 100644 --- a/versions.rst +++ b/versions.rst @@ -60,9 +60,9 @@ Python releases go through five phases: (including significant changes to new features), bug fixes, and security fixes are accepted for the upcoming feature release. -:bugfix: Once a version has been released, bug fixes and security fixes are +:bugfix: Once a version has been fully released, bug fixes and security fixes are accepted. New binaries are built and released. (Also called **maintenance** - mode or **stable** release) + mode or **stable** release.) :security: After two years, only security fixes are accepted and no more binaries are released. New source-only versions can be released as needed. From 3f36d68b61d168bf70ec39d45c66ff4d1e9cdb4d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:16:32 -0400 Subject: [PATCH 21/32] 3.13 gets two years of bug fixes, earlier gets 1.5 years --- _tools/generate_release_cycle.py | 13 +++++++++++-- versions.rst | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index ca0cf3cb6..64abf32a0 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -25,6 +25,10 @@ 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.""" @@ -35,8 +39,13 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: # Generate a few additional fields for key, version in self.versions.items(): version["key"] = key + 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=2 * 365) + 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()) @@ -61,7 +70,7 @@ def __init__(self, *, limit_to_active=False, special_py27=False) -> None: self.sorted_versions = sorted( self.versions.values(), - key=lambda v: [int(i) for i in v["key"].split(".")], + key=parse_version, reverse=True, ) diff --git a/versions.rst b/versions.rst index 85414ceb7..d463b2bd4 100644 --- a/versions.rst +++ b/versions.rst @@ -64,7 +64,7 @@ Python releases go through five phases: accepted. New binaries are built and released. (Also called **maintenance** mode or **stable** release.) -:security: After two years, only security fixes are accepted and no more +:security: After a few years, only security fixes are accepted and no more binaries are released. New source-only versions can be released as needed. :end-of-life: Five years after a release, support ends. The release cycle is From 47df7c42308f4479022f772629f83aa028b55e87 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 17 Mar 2025 18:23:51 -0400 Subject: [PATCH 22/32] last changes from the review --- _tools/generate_release_cycle.py | 1 - _tools/release_cycle_template.svg.jinja | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 64abf32a0..fd43948a3 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -186,7 +186,6 @@ def main() -> None: args = parser.parse_args() versions = Versions() - print(versions.versions.keys()) assert len(versions.versions) > 10 versions.write_csv() versions.write_svg(args.today, "include/release-cycle-all.svg") diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 3a69d1657..735c63102 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -80,8 +80,9 @@ {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} - {% set half_x = [end_x, date_to_x(version.start_security_date)]|min %} {% set height = 1.25 * SCALE %} @@ -90,7 +91,7 @@ {% set left_x = start_x * SCALE %} {% set middle_x = half_x * SCALE %} {% set right_x = half_x * SCALE %} - {% set recty = (y - 1) * SCALE %} + {% set rect_y = (y - 1) * SCALE %} {% set radius_value = 0.25 * SCALE %} {% if version.status != "end-of-life" %} @@ -98,25 +99,25 @@ From 80a44b747d121ae70a5277802cf9c1ddde032d20 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 18 Mar 2025 07:43:19 -0400 Subject: [PATCH 23/32] use a consistent anchor for the full chart --- versions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions.rst b/versions.rst index d463b2bd4..ee5d2fbe5 100644 --- a/versions.rst +++ b/versions.rst @@ -13,7 +13,7 @@ version can be found on the `download page `_ .. raw:: html :file: include/release-cycle.svg -(See :ref:`below ` for a chart with older versions. +(See :ref:`below ` for a chart with older versions. Another useful visualization is `endoflife.date/python `_.) @@ -39,7 +39,7 @@ Unsupported versions :file: include/end-of-life.csv -.. _versions-chart-all: +.. _full-chart: Full chart ========== From 584d48a0eab0a74d7aa7144a84eb4bcf16b599ac Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 18 Mar 2025 07:56:57 -0400 Subject: [PATCH 24/32] more tweaking of the phase descriptions --- versions.rst | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/versions.rst b/versions.rst index ee5d2fbe5..8cfd259f8 100644 --- a/versions.rst +++ b/versions.rst @@ -51,7 +51,8 @@ Full chart Status key ========== -Python releases go through five phases: +Python releases go through five phases, as described in :pep:`602`. Release +managers can adjust specific dates as needed. :feature: Before the first beta, the next full release can accept new features, bug fixes, and security fixes. @@ -61,17 +62,14 @@ Python releases go through five phases: are accepted for the upcoming feature release. :bugfix: Once a version has been fully released, bug fixes and security fixes are - accepted. New binaries are built and released. (Also called **maintenance** - mode or **stable** release.) + accepted. New binaries are built and released roughly every two months. This + phase is also called **maintenance** mode or **stable** release. -:security: After a few years, only security fixes are accepted and no more - binaries are released. New source-only versions can be released as needed. +:security: After two years (18 months for versions before 3.13), only security + fixes are accepted and no more binaries are released. New source-only versions + can be released as needed. :end-of-life: Five years after a release, support ends. The release cycle is - frozen; no further changes can be pushed to it. + frozen; no further changes are allowed. See also the :ref:`devcycle` page for more information about branches and backporting. - -By default, the end-of-life is scheduled 5 years after the first release, -but can be adjusted by the release manager of each branch. All Python 2 -versions have reached end-of-life. From 213f5fc0e2ef6cf0a42966a568b93ead3bb885ae Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 10:22:02 +0100 Subject: [PATCH 25/32] Use `height` consistently --- _tools/release_cycle_template.svg.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 735c63102..91bdfe34e 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -127,7 +127,7 @@ x="{{ start_x * SCALE }}" y="{{ (y - 1) * SCALE }}" width="{{ (end_x - start_x) * SCALE }}" - height="{{ 1.25 * SCALE }}" + height="{{ height }}" rx="0.25em" ry="0.25em" mask="url(#release-cycle-mask-{{ id_key }})" @@ -137,7 +137,7 @@ x="{{ start_x * SCALE }}" y="{{ (y - 1) * SCALE }}" width="{{ (end_x - start_x) * SCALE }}" - height="{{ 1.25 * SCALE }}" + height="{{ height }}" rx="0.25em" ry="0.25em" mask="url(#release-cycle-mask-{{ id_key }})" From 6ae8f9f0682cbb68dac215a99516a3881f725743 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 10:59:28 +0100 Subject: [PATCH 26/32] Clean up the path code --- _tools/generate_release_cycle.py | 14 +- _tools/release_cycle_template.svg.jinja | 165 +++++++++++------------- 2 files changed, 83 insertions(+), 96 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index fd43948a3..63d98cfce 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -125,6 +125,8 @@ def write_svg(self, today: str, out_path: 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 @@ -145,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""" @@ -158,12 +160,12 @@ def format_year(year: int) -> str: with open(out_path, "w", encoding="UTF-8", newline="\n") as f: template.stream( SCALE=SCALE, - diagram_width=DIAGRAM_WIDTH, - diagram_height=(self.sorted_versions[0]["y"] + 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, - LEGEND_WIDTH=LEGEND_WIDTH, - RIGHT_MARGIN=RIGHT_MARGIN, + 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, diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 91bdfe34e..1c5259c48 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -2,7 +2,7 @@ @@ -12,16 +12,16 @@ {% for version in versions %} - {% set y = version.y * LINE_HEIGHT %} + {% set y = version.y * line_height %} {% if version.y % 2 %} {% endif %} {% endfor %} @@ -29,8 +29,8 @@ {% for year in years %} @@ -39,10 +39,10 @@ {% if not loop.last %} {% endif %} @@ -53,80 +53,86 @@ {% for version in versions %} - {% set y = version.y * LINE_HEIGHT %} + {% set top_y = version.y * line_height - 1 * SCALE %} + {% set small_text_y = version.y * line_height - 0.1 * SCALE %} + + - {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} - - {% set half_x = [end_x, date_to_x(version.start_security_date)]|min %} + {% set middle_x = ([end_x, date_to_x(version.start_security_date)]|min) %} {% set height = 1.25 * SCALE %} - {% set left_width = (half_x - start_x) * SCALE %} - {% set right_width = (end_x - half_x) * SCALE %} - {% set left_x = start_x * SCALE %} - {% set middle_x = half_x * SCALE %} - {% set right_x = half_x * SCALE %} - {% set rect_y = (y - 1) * SCALE %} - {% set radius_value = 0.25 * SCALE %} + {% set left_width = (middle_x - start_x) %} + {% set right_width = (end_x - middle_x) %} + {% set radius = 0.25 * SCALE %} {% if version.status != "end-of-life" %} - + {% endif %} - {% if version.status == "bugfix" %} - - bugfix - - {% elif version.status == "security" %} - - security - - {% elif version.status == "end-of-life" %} - - end-of-life - - {% else %} - {{ version.status }} - {% endif %} Python {{ version.key }} @@ -198,10 +183,10 @@ From 70a2fe515524a7d187c756217416f36f98ecab70 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 11:05:33 +0100 Subject: [PATCH 27/32] Remove the "shade" rectangle, keep left+right+border or a single rect --- _static/devguide_overrides.css | 23 ++++------- _tools/release_cycle_template.svg.jinja | 52 ++++++++++++++----------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/_static/devguide_overrides.css b/_static/devguide_overrides.css index a048e1c36..622a8077f 100644 --- a/_static/devguide_overrides.css +++ b/_static/devguide_overrides.css @@ -61,8 +61,8 @@ } .release-cycle-chart .release-cycle-status-end-of-life { - --status-bg-color: #DD2200; - --status-border-color: #FF8888; + --status-bg-color: #FF8888; + --status-border-color: #DD2200; } .release-cycle-chart .release-cycle-status-security { @@ -90,24 +90,15 @@ stroke: transparent; } -.release-cycle-chart .release-cycle-border { - fill: transparent; +.release-cycle-chart .release-cycle-blob-full { + fill: var(--status-bg-color); stroke: var(--status-border-color); - stroke-width: 1.6px; } -.release-cycle-chart .release-cycle-shade { +.release-cycle-chart .release-cycle-border { fill: transparent; - stroke: transparent; - - &.release-cycle-status-end-of-life { - fill: #DD2200; - stroke: #FF8888; - } - &.release-cycle-status-feature { - fill: var(--color-background-primary); - opacity: 50%; - } + stroke: var(--status-border-color); + stroke-width: 1.6px; } .good pre { diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 1c5259c48..697215fd9 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -127,27 +127,33 @@ Z {#- left -#} " /> + + + {% else %} + + {% endif %} - - + + - {{ version.status }} - + > + {{ version.status }} + Date: Tue, 18 Mar 2025 11:07:10 +0100 Subject: [PATCH 28/32] Use the same radius everywhere --- _tools/release_cycle_template.svg.jinja | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 697215fd9..72fb780d5 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -76,6 +76,7 @@ {% for version in versions %} {% set top_y = version.y * line_height - 1 * SCALE %} {% set small_text_y = version.y * line_height - 0.1 * SCALE %} + {% set height = 1.25 * SCALE %} @@ -87,7 +88,6 @@ Thanks Claude.ai for the initial conversion. --> {% set middle_x = ([end_x, date_to_x(version.start_security_date)]|min) %} - {% set height = 1.25 * SCALE %} {% set left_width = (middle_x - start_x) %} {% set right_width = (end_x - middle_x) %} {% set radius = 0.25 * SCALE %} @@ -134,8 +134,8 @@ y="{{ top_y }}" width="{{ (end_x - start_x) }}" height="{{ height }}" - rx="0.25em" - ry="0.25em" + rx="{{ radius }}" + ry="{{ radius }}" mask="url(#release-cycle-mask-{{ id_key }})" /> {% else %} @@ -147,8 +147,8 @@ y="{{ top_y }}" width="{{ (end_x - start_x) }}" height="{{ height }}" - rx="0.25em" - ry="0.25em" + rx="{{ radius }}" + ry="{{ radius }}" mask="url(#release-cycle-mask-{{ id_key }})" /> {% endif %} From 2ae3700d06e7d0b5a09dce50b1c40fbc23398c16 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 11:09:07 +0100 Subject: [PATCH 29/32] Rearrange comments/assignments --- _tools/release_cycle_template.svg.jinja | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 72fb780d5..7995e6e73 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -74,14 +74,15 @@ {% for version in versions %} - {% set top_y = version.y * line_height - 1 * SCALE %} - {% set small_text_y = version.y * line_height - 0.1 * SCALE %} - {% set height = 1.25 * SCALE %} - + {% set top_y = version.y * line_height - 1 * SCALE %} + {% set height = 1.25 * SCALE %} {% set start_x = date_to_x(version.first_release_date) %} {% set end_x = date_to_x(version.end_of_life_date) %} + {% set radius = 0.25 * SCALE %} + + {% set small_text_y = version.y * line_height - 0.1 * SCALE %} From a83cd0fd4de788d814c4d4ee2ed9a1ce06be8030 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 11:11:15 +0100 Subject: [PATCH 31/32] Restore bold red color --- _static/devguide_overrides.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_static/devguide_overrides.css b/_static/devguide_overrides.css index 622a8077f..d47a1e602 100644 --- a/_static/devguide_overrides.css +++ b/_static/devguide_overrides.css @@ -61,8 +61,8 @@ } .release-cycle-chart .release-cycle-status-end-of-life { - --status-bg-color: #FF8888; - --status-border-color: #DD2200; + --status-bg-color: #DD2200; + --status-border-color: #FF8888; } .release-cycle-chart .release-cycle-status-security { From eb054b23fac72128341a1ff6f819fbd22e0b8b2a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 18 Mar 2025 13:39:23 +0100 Subject: [PATCH 32/32] Update _tools/release_cycle_template.svg.jinja --- _tools/release_cycle_template.svg.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tools/release_cycle_template.svg.jinja b/_tools/release_cycle_template.svg.jinja index 97d2a8e9b..d3d5866a0 100644 --- a/_tools/release_cycle_template.svg.jinja +++ b/_tools/release_cycle_template.svg.jinja @@ -167,7 +167,7 @@ x="{{ end_x + (0.25 * SCALE) }}" text-anchor="start" {% else %} - x="{{ start_x - (0.5 * SCALE) }}" + x="{{ start_x - (0.25 * SCALE) }}" text-anchor="end" {% endif %} >