From e690607fd86765e05b014d2bf807177782af5cb5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:28:09 +0200 Subject: [PATCH 1/4] Highlight today in colour in calendar CLI output --- Doc/library/calendar.rst | 5 +- Doc/whatsnew/3.14.rst | 13 +++++ Lib/_colorize.py | 2 + Lib/calendar.py | 50 +++++++++++++++---- ...-12-29-00-33-34.gh-issue-128317.WgFina.rst | 2 + 5 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index eafc038d6cb722..239aa4547b8f4a 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -516,7 +516,7 @@ The :mod:`calendar` module defines the following exceptions: .. _calendar-cli: -Command-Line Usage +Command-line usage ------------------ .. versionadded:: 2.5 @@ -654,6 +654,9 @@ The following options are accepted: The number of months printed per row. Defaults to 3. +.. versionchanged:: 3.14 + By default, today's date is highlighted in color and can be + :ref:`controlled using environment variables `. *HTML-mode options:* diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2767fd3ca48b29..0d5d6923eedd2c 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -292,6 +292,19 @@ ast * The ``repr()`` output for AST nodes now includes more information. (Contributed by Tomas R in :gh:`116022`.) + +calendar +-------- + +* By default, today's date is highlighted in color in :mod:`calendar`'s + :ref:`command-line ` text output. + This can be controlled via the :envvar:`PYTHON_COLORS` environment + variable as well as the canonical |NO_COLOR|_ + and |FORCE_COLOR|_ environment variables. + See also :ref:`using-on-controlling-color`. + (Contributed by Hugo van Kemenade in :gh:`128317`.) + + concurrent.futures ------------------ diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 709081e25ec59b..f609901887a26b 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -6,9 +6,11 @@ class ANSIColors: + BACKGROUND_YELLOW = "\x1b[43m" BOLD_GREEN = "\x1b[1;32m" BOLD_MAGENTA = "\x1b[1;35m" BOLD_RED = "\x1b[1;31m" + BLACK = "\x1b[30m" GREEN = "\x1b[32m" GREY = "\x1b[90m" MAGENTA = "\x1b[35m" diff --git a/Lib/calendar.py b/Lib/calendar.py index 8c1c646da46a98..d2e5e08d02dbb9 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -349,11 +349,27 @@ def formatday(self, day, weekday, width): s = '%2i' % day # right-align single-digit days return s.center(width) - def formatweek(self, theweek, width): + def formatweek(self, theweek, width, *, highlight_day=None): """ Returns a single week in a string (no newline). """ - return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) + if highlight_day: + from _colorize import get_colors + + ansi = get_colors() + highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" + reset = ansi.RESET + else: + highlight = reset = "" + + return ' '.join( + ( + f"{highlight}{self.formatday(d, wd, width)}{reset}" + if d == highlight_day + else self.formatday(d, wd, width) + ) + for (d, wd) in theweek + ) def formatweekday(self, day, width): """ @@ -388,10 +404,11 @@ def prmonth(self, theyear, themonth, w=0, l=0): """ print(self.formatmonth(theyear, themonth, w, l), end='') - def formatmonth(self, theyear, themonth, w=0, l=0): + def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None): """ Return a month's calendar string (multi-line). """ + highlight_day = highlight_day.day if highlight_day else None w = max(2, w) l = max(1, l) s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) @@ -400,11 +417,11 @@ def formatmonth(self, theyear, themonth, w=0, l=0): s += self.formatweekheader(w).rstrip() s += '\n' * l for week in self.monthdays2calendar(theyear, themonth): - s += self.formatweek(week, w).rstrip() + s += self.formatweek(week, w, highlight_day=highlight_day).rstrip() s += '\n' * l return s - def formatyear(self, theyear, w=2, l=1, c=6, m=3): + def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None): """ Returns a year's calendar as a multi-line string. """ @@ -428,15 +445,24 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3): headers = (header for k in months) a(formatstring(headers, colwidth, c).rstrip()) a('\n'*l) + + if highlight_day and highlight_day.month in months: + month_pos = months.index(highlight_day.month) + else: + month_pos = None + # max number of weeks for this row height = max(len(cal) for cal in row) for j in range(height): weeks = [] - for cal in row: + for k, cal in enumerate(row): if j >= len(cal): weeks.append('') else: - weeks.append(self.formatweek(cal[j], w)) + day = highlight_day.day if k == month_pos else None + weeks.append( + self.formatweek(cal[j], w, highlight_day=day) + ) a(formatstring(weeks, colwidth, c).rstrip()) a('\n' * l) return ''.join(v) @@ -765,6 +791,7 @@ def main(args=None): sys.exit(1) locale = options.locale, options.encoding + today = datetime.date.today() if options.type == "html": if options.month: @@ -781,7 +808,7 @@ def main(args=None): optdict = dict(encoding=encoding, css=options.css) write = sys.stdout.buffer.write if options.year is None: - write(cal.formatyearpage(datetime.date.today().year, **optdict)) + write(cal.formatyearpage(today.year, **optdict)) else: write(cal.formatyearpage(options.year, **optdict)) else: @@ -797,10 +824,15 @@ def main(args=None): if options.month is not None: _validate_month(options.month) if options.year is None: - result = cal.formatyear(datetime.date.today().year, **optdict) + optdict["highlight_day"] = today + result = cal.formatyear(today.year, **optdict) elif options.month is None: + if options.year == today.year: + optdict["highlight_day"] = today result = cal.formatyear(options.year, **optdict) else: + if options.year == today.year and options.month == today.month: + optdict["highlight_day"] = today result = cal.formatmonth(options.year, options.month, **optdict) write = sys.stdout.write if options.encoding: diff --git a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst new file mode 100644 index 00000000000000..a64616756dc9ac --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst @@ -0,0 +1,2 @@ +Highlight today in colour in calendar's CLI output. Patch by Hugo van +Kemenade. From 3608954af16083a17b6214c13393068e1fc161f1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 29 Dec 2024 12:43:46 +0200 Subject: [PATCH 2/4] Improve docs Co-authored-by: Peter Bierma --- Doc/library/calendar.rst | 2 +- .../next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 239aa4547b8f4a..430d6090cd32db 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -654,7 +654,7 @@ The following options are accepted: The number of months printed per row. Defaults to 3. -.. versionchanged:: 3.14 +.. versionchanged:: next By default, today's date is highlighted in color and can be :ref:`controlled using environment variables `. diff --git a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst index a64616756dc9ac..4441108014569e 100644 --- a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst +++ b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst @@ -1,2 +1,2 @@ -Highlight today in colour in calendar's CLI output. Patch by Hugo van +Highlight today in colour in :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade. From ad2921cfabb20887f8e769403b47a30fe8c08a84 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 29 Dec 2024 14:47:28 +0200 Subject: [PATCH 3/4] Document the new highlight_day argument --- Doc/library/calendar.rst | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 430d6090cd32db..ff221ce98434e1 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -138,7 +138,20 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`TextCalendar` instances have the following methods: - .. method:: formatmonth(theyear, themonth, w=0, l=0) + .. method:: formatweek(theweek, w=0, highlight_day=None) + + Return a single week in a string with no newline. If *w* is provided, it + specifies the width of the date columns, which are centered. Depends + on the first weekday as specified in the constructor or set by the + :meth:`setfirstweekday` method. + + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + + + .. method:: formatmonth(theyear, themonth, w=0, l=0, highlight_day=None) Return a month's calendar in a multi-line string. If *w* is provided, it specifies the width of the date columns, which are centered. If *l* is @@ -146,13 +159,18 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + .. method:: prmonth(theyear, themonth, w=0, l=0) Print a month's calendar as returned by :meth:`formatmonth`. - .. method:: formatyear(theyear, w=2, l=1, c=6, m=3) + .. method:: formatyear(theyear, w=2, l=1, c=6, m=3, highlight_day=None) Return a *m*-column calendar for an entire year as a multi-line string. Optional parameters *w*, *l*, and *c* are for date column width, lines per @@ -161,6 +179,11 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :meth:`setfirstweekday` method. The earliest year for which a calendar can be generated is platform-dependent. + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + .. method:: pryear(theyear, w=2, l=1, c=6, m=3) From a565d976aa3fdea96fba8c5572b964658af5e01f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:32:40 +0200 Subject: [PATCH 4/4] Re-add whitespace --- Doc/library/calendar.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 888e52fa5755d5..ace8529d6e7e0c 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -138,6 +138,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`TextCalendar` instances have the following methods: + .. method:: formatday(theday, weekday, width) Return a string representing a single day formatted with the given *width*.