From 5ce8a9ae5613505cae629aa9e2a32ba2af74efb2 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Mon, 11 Aug 2025 14:47:13 +0800 Subject: [PATCH 1/3] Modernizing `calendar.HTMLCalendar` for HTML Output --- Doc/whatsnew/3.15.rst | 7 +++ Lib/calendar.py | 44 +++++++------- Lib/test/test_calendar.py | 60 +++++++++++-------- ...-08-11-14-18-32.gh-issue-137634.M7iBG6.rst | 2 + 4 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 9f01b52f1aff3b..3adf1925925942 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -214,6 +214,13 @@ New modules Improved modules ================ +calendar +-------- + +* Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support + dark mode, and migrated the output to the HTML5 standard. + (Contributed by Jiahao Li in :gh:`137634`.) + dbm --- diff --git a/Lib/calendar.py b/Lib/calendar.py index 45bb265a65602c..dd59de1f337569 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -490,30 +490,29 @@ def formatday(self, day, weekday): """ if day == 0: # day outside month - return ' ' % self.cssclass_noday + return f' ' else: - return '%d' % (self.cssclasses[weekday], day) + return f'{day}' def formatweek(self, theweek): """ Return a complete week as a table row. """ s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) - return '%s' % s + return f'{s}' def formatweekday(self, day): """ Return a weekday name as a table header. """ - return '%s' % ( - self.cssclasses_weekday_head[day], day_abbr[day]) + return f'{day_abbr[day]}' def formatweekheader(self): """ Return a header for a week as a table row. """ s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) - return '%s' % s + return f'{s}' def formatmonthname(self, theyear, themonth, withyear=True): """ @@ -521,11 +520,10 @@ def formatmonthname(self, theyear, themonth, withyear=True): """ _validate_month(themonth) if withyear: - s = '%s %s' % (standalone_month_name[themonth], theyear) + s = f'{standalone_month_name[themonth]} {theyear}' else: s = standalone_month_name[themonth] - return '%s' % ( - self.cssclass_month_head, s) + return f'{s}' def formatmonth(self, theyear, themonth, withyear=True): """ @@ -533,8 +531,7 @@ def formatmonth(self, theyear, themonth, withyear=True): """ v = [] a = v.append - a('' % ( - self.cssclass_month)) + a(f'
') a('\n') a(self.formatmonthname(theyear, themonth, withyear=withyear)) a('\n') @@ -554,11 +551,9 @@ def formatyear(self, theyear, width=3): v = [] a = v.append width = max(width, 1) - a('
' % - self.cssclass_year) + a(f'
') a('\n') - a('' % ( - width, self.cssclass_year_head, theyear)) + a(f'') for i in range(JANUARY, JANUARY+12, width): # months in this row months = range(i, min(i+width, 13)) @@ -579,14 +574,21 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): encoding = 'utf-8' v = [] a = v.append - a('\n' % encoding) - a('\n') - a('\n') + a('\n') + a('\n') a('\n') - a('\n' % encoding) + a(f'\n') + a('\n') + a(f'Calendar for {theyear}\n') + a('\n') if css is not None: - a('\n' % css) - a('Calendar for %d\n' % theyear) + a(f'\n') a('\n') a('\n') a(self.formatyear(theyear, width)) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 589a21baf7bd61..e62ad13278f8d1 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -113,18 +113,27 @@ default_format = dict(year="year", month="month", encoding="ascii") +result_2004_css = """""" + result_2004_html = """\ - - - + + - - + +Calendar for 2004 +{css_styles} + -
%s
{theyear}
-
2004
+
+
2004
@@ -133,7 +142,7 @@
January
MonTueWedThuFriSatSun
   1234
19202122232425
262728293031 
-
+
@@ -142,7 +151,7 @@
February
MonTueWedThuFriSatSun
      1
16171819202122
23242526272829
-
+
@@ -151,7 +160,7 @@
March
MonTueWedThuFriSatSun
1234567
22232425262728
293031    
-
+
@@ -160,7 +169,7 @@
April
MonTueWedThuFriSatSun
   1234
19202122232425
2627282930  
-
+
@@ -170,7 +179,7 @@
May
MonTueWedThuFriSatSun
     12
24252627282930
31      
-
+
@@ -179,7 +188,7 @@
June
MonTueWedThuFriSatSun
 123456
21222324252627
282930    
-
+
@@ -188,7 +197,7 @@
July
MonTueWedThuFriSatSun
   1234
19202122232425
262728293031 
-
+
@@ -198,7 +207,7 @@
August
MonTueWedThuFriSatSun
      1
23242526272829
3031     
-
+
@@ -207,7 +216,7 @@
September
MonTueWedThuFriSatSun
  12345
20212223242526
27282930   
-
+
@@ -216,7 +225,7 @@
October
MonTueWedThuFriSatSun
    123
18192021222324
25262728293031
-
+
@@ -225,7 +234,7 @@
November
MonTueWedThuFriSatSun
1234567
22232425262728
2930     
-
+
@@ -385,10 +394,12 @@ def check_htmlcalendar_encoding(self, req, res): cal = calendar.HTMLCalendar() format_ = default_format.copy() format_["encoding"] = req or 'utf-8' + format_with_css = {**format_, "css_styles": result_2004_css} + formatted_html = result_2004_html.format(**format_with_css) output = cal.formatyearpage(2004, encoding=req) self.assertEqual( output, - result_2004_html.format(**format_).encode(res) + formatted_html.encode(res) ) def test_output(self): @@ -1132,7 +1143,7 @@ def test_option_type(self): output = run('--type', 'text', '2004') self.assertEqual(output, conv(result_2004_text)) output = run('--type', 'html', '2004') - self.assertStartsWith(output, b'') self.assertIn(b'Calendar for 2004', output) def test_html_output_current_year(self): @@ -1145,15 +1156,16 @@ def test_html_output_current_year(self): def test_html_output_year_encoding(self): for run in self.runners: output = run('-t', 'html', '--encoding', 'ascii', '2004') - self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii')) + format_with_css = default_format.copy() + format_with_css["css_styles"] = result_2004_css + self.assertEqual(output, result_2004_html.format(**format_with_css).encode('ascii')) def test_html_output_year_css(self): self.assertFailure('-t', 'html', '-c') self.assertFailure('-t', 'html', '--css') for run in self.runners: output = run('-t', 'html', '--css', 'custom.css', '2004') - self.assertIn(b'', output) + self.assertIn(b'', output) class MiscTestCase(unittest.TestCase): @@ -1207,7 +1219,7 @@ def test_formatweek_head(self): def test_format_year(self): self.assertIn( - ('
December
MonTueWedThuFriSatSun
  12345
' % + ('
' % self.cal.cssclass_year), self.cal.formatyear(2017)) def test_format_year_head(self): diff --git a/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst new file mode 100644 index 00000000000000..4d708de4d4b307 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst @@ -0,0 +1,2 @@ +Calendar pages generated by the :class:`calendar.HTMLCalendar` class now +support dark mode, and migrated the output to the HTML5 standard. From 18486409650f47bb1ef3c5eba0c75d73950ddf15 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Tue, 12 Aug 2025 08:39:06 +0800 Subject: [PATCH 2/3] polish --- Lib/calendar.py | 3 +-- Lib/test/test_calendar.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/calendar.py b/Lib/calendar.py index dd59de1f337569..80e43bed653a3f 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -583,8 +583,7 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): a('\n') if css is not None: diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index e62ad13278f8d1..863fa748b3eef1 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -116,8 +116,7 @@ result_2004_css = """""" From e365fe145a36519d509c44dd2e325eb49c0dc5b6 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Sun, 17 Aug 2025 14:49:48 +0800 Subject: [PATCH 3/3] remove darkmode --- Doc/whatsnew/3.15.rst | 4 ++-- Lib/calendar.py | 6 ------ Lib/test/test_calendar.py | 16 ++-------------- ...025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst | 4 ++-- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 3adf1925925942..79a99fd126b4e0 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -217,8 +217,8 @@ Improved modules calendar -------- -* Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support - dark mode, and migrated the output to the HTML5 standard. +* The calendar pages generated by the :class:`calendar.HTMLCalendar` class now + use the HTML5 standard. (Contributed by Jiahao Li in :gh:`137634`.) dbm diff --git a/Lib/calendar.py b/Lib/calendar.py index 80e43bed653a3f..65e322a70dd4d1 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -580,12 +580,6 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): a(f'\n') a('\n') a(f'Calendar for {theyear}\n') - a('\n') if css is not None: a(f'\n') a('\n') diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 863fa748b3eef1..3f6b7922cd5641 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -113,13 +113,6 @@ default_format = dict(year="year", month="month", encoding="ascii") -result_2004_css = """""" - result_2004_html = """\ @@ -127,7 +120,6 @@ Calendar for 2004 -{css_styles} @@ -393,12 +385,10 @@ def check_htmlcalendar_encoding(self, req, res): cal = calendar.HTMLCalendar() format_ = default_format.copy() format_["encoding"] = req or 'utf-8' - format_with_css = {**format_, "css_styles": result_2004_css} - formatted_html = result_2004_html.format(**format_with_css) output = cal.formatyearpage(2004, encoding=req) self.assertEqual( output, - formatted_html.encode(res) + result_2004_html.format(**format_).encode(res) ) def test_output(self): @@ -1155,9 +1145,7 @@ def test_html_output_current_year(self): def test_html_output_year_encoding(self): for run in self.runners: output = run('-t', 'html', '--encoding', 'ascii', '2004') - format_with_css = default_format.copy() - format_with_css["css_styles"] = result_2004_css - self.assertEqual(output, result_2004_html.format(**format_with_css).encode('ascii')) + self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii')) def test_html_output_year_css(self): self.assertFailure('-t', 'html', '-c') diff --git a/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst index 4d708de4d4b307..e07561c050cd77 100644 --- a/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst +++ b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst @@ -1,2 +1,2 @@ -Calendar pages generated by the :class:`calendar.HTMLCalendar` class now -support dark mode, and migrated the output to the HTML5 standard. +The calendar pages generated by the :class:`calendar.HTMLCalendar` class now +use the HTML5 standard.