https://github.com/python/cpython/commit/07912f86323dc5a13f4d070b7e3f2c3cb2850d8b commit: 07912f86323dc5a13f4d070b7e3f2c3cb2850d8b branch: main author: Pål Grønås Drange <[email protected]> committer: hugovk <[email protected]> date: 2025-10-31T17:28:53+02:00 summary:
gh-140212: Add html for year-month option in Calendar (#140230) Co-authored-by: Hugo van Kemenade <[email protected]> files: A Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst M Doc/library/calendar.rst M Doc/whatsnew/3.15.rst M Lib/calendar.py M Lib/test/test_calendar.py diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 3af01b132419a5..f76b1013dfbc66 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -710,8 +710,7 @@ The following options are accepted: .. option:: month The month of the specified :option:`year` to print the calendar for. - Must be a number between 1 and 12, - and may only be used in text mode. + Must be a number between 1 and 12. Defaults to printing a calendar for the full year. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 1294415a220b97..f70345dd2b8d62 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -336,6 +336,10 @@ calendar dark mode and have been migrated to the HTML5 standard for improved accessibility. (Contributed by Jiahao Li and Hugo van Kemenade in :gh:`137634`.) +* The :mod:`calendar`'s :ref:`command-line <calendar-cli>` HTML output now + accepts the year-month option: ``python -m calendar -t html 2009 06``. + (Contributed by Pål Grønås Drange in :gh:`140212`.) + collections ----------- diff --git a/Lib/calendar.py b/Lib/calendar.py index ed6b74b248042e..d80c3fd9524776 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -574,9 +574,9 @@ def formatyear(self, theyear, width=3): a('</table>') return ''.join(v) - def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + def _format_html_page(self, theyear, content, css, encoding): """ - Return a formatted year as a complete HTML page. + Return a complete HTML page with the given content. """ if encoding is None: encoding = 'utf-8' @@ -597,11 +597,25 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): a(f'<link rel="stylesheet" href="{css}">\n') a('</head>\n') a('<body>\n') - a(self.formatyear(theyear, width)) + a(content) a('</body>\n') a('</html>\n') return ''.join(v).encode(encoding, "xmlcharrefreplace") + def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + """ + Return a formatted year as a complete HTML page. + """ + content = self.formatyear(theyear, width) + return self._format_html_page(theyear, content, css, encoding) + + def formatmonthpage(self, theyear, themonth, width=3, css='calendar.css', encoding=None): + """ + Return a formatted month as a complete HTML page. + """ + content = self.formatmonth(theyear, themonth, width) + return self._format_html_page(theyear, content, css, encoding) + class different_locale: def __init__(self, locale): @@ -886,7 +900,7 @@ def main(args=None): parser.add_argument( "month", nargs='?', type=int, - help="month number (1-12, text only)" + help="month number (1-12)" ) options = parser.parse_args(args) @@ -899,9 +913,6 @@ def main(args=None): today = datetime.date.today() if options.type == "html": - if options.month: - parser.error("incorrect number of arguments") - sys.exit(1) if options.locale: cal = LocaleHTMLCalendar(locale=locale) else: @@ -912,10 +923,14 @@ def main(args=None): encoding = 'utf-8' optdict = dict(encoding=encoding, css=options.css) write = sys.stdout.buffer.write + if options.year is None: write(cal.formatyearpage(today.year, **optdict)) else: - write(cal.formatyearpage(options.year, **optdict)) + if options.month: + write(cal.formatmonthpage(options.year, options.month, **optdict)) + else: + write(cal.formatyearpage(options.year, **optdict)) else: if options.locale: cal = _CLIDemoLocaleCalendar(highlight_day=today, locale=locale) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 020f9d61cae3cf..fe9a59d335b6b0 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -245,6 +245,34 @@ </html> """ +result_2009_6_html = """\ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Calendar for 2009</title> +<style> +:root { color-scheme: light dark; } +table.year { border: solid; } +table.year > tbody > tr > td { border: solid; vertical-align: top; } +</style> +<link rel="stylesheet" href="calendar.css"> +</head> +<body> +<table class="month"> +<tr><th colspan="7" class="month">June 2009</th></tr> +<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> +<tr><td class="mon">1</td><td class="tue">2</td><td class="wed">3</td><td class="thu">4</td><td class="fri">5</td><td class="sat">6</td><td class="sun">7</td></tr> +<tr><td class="mon">8</td><td class="tue">9</td><td class="wed">10</td><td class="thu">11</td><td class="fri">12</td><td class="sat">13</td><td class="sun">14</td></tr> +<tr><td class="mon">15</td><td class="tue">16</td><td class="wed">17</td><td class="thu">18</td><td class="fri">19</td><td class="sat">20</td><td class="sun">21</td></tr> +<tr><td class="mon">22</td><td class="tue">23</td><td class="wed">24</td><td class="thu">25</td><td class="fri">26</td><td class="sat">27</td><td class="sun">28</td></tr> +<tr><td class="mon">29</td><td class="tue">30</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> +</table> +</body> +</html> +""" + result_2004_days = [ [[[0, 0, 0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], @@ -506,6 +534,13 @@ def test_format(self): calendar.format(["1", "2", "3"], colwidth=3, spacing=1) self.assertEqual(out.getvalue().strip(), "1 2 3") + def test_format_html_year_with_month(self): + self.assertEqual( + calendar.HTMLCalendar().formatmonthpage(2009, 6).decode("ascii"), + result_2009_6_html + ) + + class CalendarTestCase(unittest.TestCase): def test_deprecation_warning(self): @@ -1102,7 +1137,6 @@ def test_illegal_arguments(self): self.assertFailure('2004', '1', 'spam') self.assertFailure('2004', '1', '1') self.assertFailure('2004', '1', '1', 'spam') - self.assertFailure('-t', 'html', '2004', '1') def test_output_current_year(self): for run in self.runners: diff --git a/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst b/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst new file mode 100644 index 00000000000000..5563d07717117e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst @@ -0,0 +1,5 @@ +Calendar's HTML formatting now accepts year and month as options. +Previously, running ``python -m calendar -t html 2025 10`` would result in an +error message. It now generates an HTML document displaying the calendar for +the specified month. +Contributed by Pål Grønås Drange. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
