From 9e95c430d77a21125729cae4afa0f8b3b7f50ec7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:35:39 +0300 Subject: [PATCH 1/5] Add more colour to calendar --- Lib/_colorize.py | 12 ++++++++ Lib/calendar.py | 65 ++++++++++++++++++++++++++++++--------- Lib/test/test_calendar.py | 1 + 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 478f81894911e7..6849a75ce47291 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -188,6 +188,14 @@ class Argparse(ThemeSection): message: str = ANSIColors.MAGENTA +@dataclass(frozen=True, kw_only=True) +class Calendar(ThemeSection): + header: str = ANSIColors.BOLD + highlight: str = ANSIColors.BLACK + ANSIColors.BACKGROUND_YELLOW + weekday: str = ANSIColors.CYAN + reset: str = ANSIColors.RESET + + @dataclass(frozen=True, kw_only=True) class Difflib(ThemeSection): """A 'git diff'-like theme for `difflib.unified_diff`.""" @@ -404,6 +412,7 @@ class Theme: below. """ argparse: Argparse = field(default_factory=Argparse) + calendar: Calendar = field(default_factory=Calendar) difflib: Difflib = field(default_factory=Difflib) fancycompleter: FancyCompleter = field(default_factory=FancyCompleter) http_server: HttpServer = field(default_factory=HttpServer) @@ -417,6 +426,7 @@ def copy_with( self, *, argparse: Argparse | None = None, + calendar: Calendar | None = None, difflib: Difflib | None = None, fancycompleter: FancyCompleter | None = None, http_server: HttpServer | None = None, @@ -433,6 +443,7 @@ def copy_with( """ return type(self)( argparse=argparse or self.argparse, + calendar=calendar or self.calendar, difflib=difflib or self.difflib, fancycompleter=fancycompleter or self.fancycompleter, http_server=http_server or self.http_server, @@ -453,6 +464,7 @@ def no_colors(cls) -> Self: """ return cls( argparse=Argparse.no_colors(), + calendar=Calendar.no_colors(), difflib=Difflib.no_colors(), fancycompleter=FancyCompleter.no_colors(), http_server=HttpServer.no_colors(), diff --git a/Lib/calendar.py b/Lib/calendar.py index d80c3fd9524776..08c5d22680f49e 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -686,28 +686,61 @@ def __init__(self, highlight_day=None, *args, **kwargs): super().__init__(*args, **kwargs) self.highlight_day = highlight_day - def formatweek(self, theweek, width, *, highlight_day=None): + def _get_theme(self): + from _colorize import get_theme + + return get_theme(tty_file=sys.stdout) + + def formatday(self, day, weekday, width, *, highlight_day=None): """ - Returns a single week in a string (no newline). + Returns a formatted day. """ - if highlight_day: - from _colorize import get_colors - - ansi = get_colors() - highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" - reset = ansi.RESET + if day == 0: + s = '' else: - highlight = reset = "" + s = f'{day:2}' + s = s.center(width) + if day == highlight_day: + theme = self._get_theme().calendar + s = f"{theme.highlight}{s}{theme.reset}" + return s + def formatweek(self, theweek, width, *, highlight_day=None): + """ + Returns a single week in a string (no newline). + """ return ' '.join( - ( - f"{highlight}{self.formatday(d, wd, width)}{reset}" - if d == highlight_day - else self.formatday(d, wd, width) - ) + self.formatday(d, wd, width, highlight_day=highlight_day) for (d, wd) in theweek ) + def formatweekheader(self, width): + """ + Return a header for a week. + """ + header = super().formatweekheader(width) + theme = self._get_theme().calendar + return f"{theme.weekday}{header}{theme.reset}" + + def formatmonthname(self, theyear, themonth, width, withyear=True): + """ + Return a formatted month name. + """ + name = super().formatmonthname(theyear, themonth, width, withyear) + theme = self._get_theme().calendar + if ( + self.highlight_day + and self.highlight_day.year == theyear + and self.highlight_day.month == themonth + ): + color = theme.highlight + name_only = name.strip() + colored_name = f"{color}{name_only}{theme.reset}" + return name.replace(name_only, colored_name, 1) + else: + color = theme.header + return f"{color}{name}{theme.reset}" + def formatmonth(self, theyear, themonth, w=0, l=0): """ Return a month's calendar string (multi-line). @@ -742,7 +775,9 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3): colwidth = (w + 1) * 7 - 1 v = [] a = v.append - a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) + theme = self._get_theme().calendar + year = repr(theyear).center(colwidth*m+c*(m-1)).rstrip() + a(f"{theme.header}{year}{theme.reset}") a('\n'*l) header = self.formatweekheader(w) for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index fe9a59d335b6b0..57c1a787d82983 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -1063,6 +1063,7 @@ def test_several_leapyears_in_range(self): def conv(s): return s.replace('\n', os.linesep).encode() +@support.force_not_colorized_test_class class CommandLineTestCase(unittest.TestCase): def setUp(self): self.runners = [self.run_cli_ok, self.run_cmd_ok] From 1fe0d93cd470351851ad462b82dcd36166f69d8f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:35:43 +0300 Subject: [PATCH 2/5] Use ArgumentDefaultsHelpFormatter for better help --- Lib/calendar.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Lib/calendar.py b/Lib/calendar.py index 08c5d22680f49e..fa9775ab040b14 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -878,28 +878,30 @@ def timegm(tuple): def main(args=None): import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) textgroup = parser.add_argument_group('text only arguments') htmlgroup = parser.add_argument_group('html only arguments') textgroup.add_argument( "-w", "--width", type=int, default=2, - help="width of date column (default 2)" + help="width of date column" ) textgroup.add_argument( "-l", "--lines", type=int, default=1, - help="number of lines for each week (default 1)" + help="number of lines for each week" ) textgroup.add_argument( "-s", "--spacing", type=int, default=6, - help="spacing between months (default 6)" + help="spacing between months" ) textgroup.add_argument( "-m", "--months", type=int, default=3, - help="months per row (default 3)" + help="months per row" ) htmlgroup.add_argument( "-c", "--css", @@ -914,7 +916,7 @@ def main(args=None): parser.add_argument( "-e", "--encoding", default=None, - help="encoding to use for output (default utf-8)" + help="encoding to use for output" ) parser.add_argument( "-t", "--type", @@ -925,7 +927,7 @@ def main(args=None): parser.add_argument( "-f", "--first-weekday", type=int, default=0, - help="weekday (0 is Monday, 6 is Sunday) to start each week (default 0)" + help="weekday (0 is Monday, 6 is Sunday) to start each week" ) parser.add_argument( "year", From 3343a36b8eb7793ca56bf6ef2a5fa7cc69e1587f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 10 Apr 2026 20:20:22 +0300 Subject: [PATCH 3/5] Re-order calendar what's new --- Doc/whatsnew/3.15.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 927d6035c8c4bc..fbc6605c4eac51 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -713,14 +713,14 @@ binascii calendar -------- -* Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support - 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 ` 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`.) +* Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support + dark mode and have been migrated to the HTML5 standard for improved accessibility. + (Contributed by Jiahao Li and Hugo van Kemenade in :gh:`137634`.) + collections ----------- From 0843e5b1314ad60cb959c65ca1d04a26cae33dbe Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 10 Apr 2026 20:23:43 +0300 Subject: [PATCH 4/5] Docs --- Doc/library/calendar.rst | 4 ++++ Doc/whatsnew/3.15.rst | 5 +++++ .../Library/2026-04-10-20-23-22.gh-issue-148352.lrec3W.rst | 1 + 3 files changed, 10 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-04-10-20-23-22.gh-issue-148352.lrec3W.rst diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 54cafaf4fe47d8..af8fe98ae21634 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -751,6 +751,10 @@ The following options are accepted: By default, today's date is highlighted in color and can be :ref:`controlled using environment variables `. +.. versionchanged:: next + By default, the calendar is in color and can be + :ref:`controlled using environment variables `. + *HTML-mode options:* .. option:: --css CSS, -c CSS diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index fbc6605c4eac51..f2bd4c77c7faab 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -713,6 +713,11 @@ binascii calendar -------- +* :mod:`calendar`'s :ref:`command-line ` text output has more + color. This can be controlled with :ref:`environment variables + `. + (Contributed by Hugo van Kemenade in :gh:`148352`.) + * The :mod:`calendar`'s :ref:`command-line ` 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`.) diff --git a/Misc/NEWS.d/next/Library/2026-04-10-20-23-22.gh-issue-148352.lrec3W.rst b/Misc/NEWS.d/next/Library/2026-04-10-20-23-22.gh-issue-148352.lrec3W.rst new file mode 100644 index 00000000000000..c757ad9a14dea9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-10-20-23-22.gh-issue-148352.lrec3W.rst @@ -0,0 +1 @@ +Add more colour to :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade. From 4b202f060973b252461e7006491c3681d87e2d9a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:47:52 +0300 Subject: [PATCH 5/5] Improve docs Co-authored-by: Rihaan Meher --- Doc/library/calendar.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index af8fe98ae21634..7b94341fe2979b 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -752,7 +752,8 @@ The following options are accepted: :ref:`controlled using environment variables `. .. versionchanged:: next - By default, the calendar is in color and can be + By default, the month is now also highlighted in color, and + the days of the week are also in color. This behavior can be :ref:`controlled using environment variables `. *HTML-mode options:*