88
99TYPE_CHECKING = False
1010if TYPE_CHECKING :
11- from release_management import VersionMetadata
11+ from release_management import ReleaseInfo , VersionMetadata
1212
1313# Seven years captures the full lifecycle from prereleases to end-of-life
1414TODAY = dt .date .today ()
1515SEVEN_YEARS_AGO = TODAY .replace (year = TODAY .year - 7 )
1616
17+ # https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.11
18+ CALENDAR_ESCAPE_TEXT = str .maketrans ({
19+ '\\ ' : r'\\' ,
20+ ';' : r'\;' ,
21+ ',' : r'\,' ,
22+ '\n ' : r'\n' ,
23+ })
24+
1725
1826def create_release_json () -> str :
1927 python_releases = dataclasses .asdict (load_python_releases ())
@@ -57,26 +65,37 @@ def version_info(metadata: VersionMetadata, /) -> dict[str, str | int]:
5765
5866def create_release_schedule_calendar () -> str :
5967 python_releases = load_python_releases ()
60- all_releases = [
61- (version , release )
62- for version , releases in python_releases .releases .items ()
63- for release in releases
64- # Keep size reasonable by omitting releases older than 7 years
65- if release .date >= SEVEN_YEARS_AGO
66- ]
67- all_releases .sort (key = lambda r : r [1 ].date )
68+ releases = []
69+ for version , all_releases in python_releases .releases .items ():
70+ pep_number = python_releases .metadata [version ].pep
71+ for release in all_releases :
72+ # Keep size reasonable by omitting releases older than 7 years
73+ if release .date < SEVEN_YEARS_AGO :
74+ continue
75+ releases .append ((pep_number , release ))
76+ releases .sort (key = lambda r : r [1 ].date )
77+ lines = release_schedule_calendar_lines (releases )
78+ return '\r \n ' .join (lines )
6879
80+
81+ def release_schedule_calendar_lines (
82+ releases : list [tuple [int , ReleaseInfo ]], /
83+ ) -> list [str ]:
6984 lines = [
7085 'BEGIN:VCALENDAR' ,
7186 'VERSION:2.0' ,
7287 'PRODID:-//Python Software Foundation//Python release schedule//EN' ,
7388 'X-WR-CALDESC:Python releases schedule from https://peps.python.org' ,
7489 'X-WR-CALNAME:Python releases schedule' ,
7590 ]
76- for version , release in all_releases :
91+ for pep_number , release in releases :
7792 normalised_stage = release .stage .casefold ().replace (' ' , '' )
78- note = (f'DESCRIPTION:Note: { release .note } ' ,) if release .note else ()
79- pep_number = python_releases .metadata [version ].pep
93+ normalised_stage = normalised_stage .translate (CALENDAR_ESCAPE_TEXT )
94+ if release .note :
95+ normalised_note = release .note .translate (CALENDAR_ESCAPE_TEXT )
96+ note = (f'DESCRIPTION:Note: { normalised_note } ' ,)
97+ else :
98+ note = ()
8099 lines += (
81100 'BEGIN:VEVENT' ,
82101 f'SUMMARY:Python { release .stage } ' ,
@@ -90,4 +109,4 @@ def create_release_schedule_calendar() -> str:
90109 'END:VCALENDAR' ,
91110 '' ,
92111 )
93- return ' \n ' . join ( lines )
112+ return lines
0 commit comments