]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128317: Move CLI calendar highlighting to private class (#129625)
authorHugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Sat, 8 Feb 2025 15:56:57 +0000 (17:56 +0200)
committerGitHub <noreply@github.com>
Sat, 8 Feb 2025 15:56:57 +0000 (15:56 +0000)
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Doc/library/calendar.rst
Lib/calendar.py
Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst [new file with mode: 0644]

index 1c6b5e03af3560ec70f74fc70b814000dba63a6a..39090e36ed9c0d994b51e28032a9a4574f350f8a 100644 (file)
@@ -166,18 +166,13 @@ interpreted as prescribed by the ISO 8601 standard.  Year 0 is 1 BC, year -1 is
       the specified width, representing an empty day. The *weekday* parameter
       is unused.
 
-   .. method:: formatweek(theweek, w=0, highlight_day=None)
+   .. method:: formatweek(theweek, w=0)
 
       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:: 3.14
-         If *highlight_day* is given, this date is highlighted in color.
-         This can be :ref:`controlled using environment variables
-         <using-on-controlling-color>`.
-
 
    .. method:: formatweekday(weekday, width)
 
@@ -193,7 +188,7 @@ interpreted as prescribed by the ISO 8601 standard.  Year 0 is 1 BC, year -1 is
       settings and are padded to the specified width.
 
 
-   .. method:: formatmonth(theyear, themonth, w=0, l=0, highlight_day=None)
+   .. method:: formatmonth(theyear, themonth, w=0, l=0)
 
       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
@@ -201,11 +196,6 @@ 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:: 3.14
-         If *highlight_day* is given, this date is highlighted in color.
-         This can be :ref:`controlled using environment variables
-         <using-on-controlling-color>`.
-
 
    .. method:: formatmonthname(theyear, themonth, width=0, withyear=True)
 
@@ -220,7 +210,7 @@ interpreted as prescribed by the ISO 8601 standard.  Year 0 is 1 BC, year -1 is
       Print a month's calendar as returned by :meth:`formatmonth`.
 
 
-   .. method:: formatyear(theyear, w=2, l=1, c=6, m=3, highlight_day=None)
+   .. method:: formatyear(theyear, w=2, l=1, c=6, m=3)
 
       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
@@ -229,11 +219,6 @@ 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:: 3.14
-         If *highlight_day* is given, this date is highlighted in color.
-         This can be :ref:`controlled using environment variables
-         <using-on-controlling-color>`.
-
 
    .. method:: pryear(theyear, w=2, l=1, c=6, m=3)
 
index d2e5e08d02dbb9887f5ccf2e13bc0ea979589fbc..1105a705a8036abe0098440d4bd3cb7355837b2b 100644 (file)
@@ -349,27 +349,11 @@ class TextCalendar(Calendar):
             s = '%2i' % day             # right-align single-digit days
         return s.center(width)
 
-    def formatweek(self, theweek, width, *, highlight_day=None):
+    def formatweek(self, theweek, width):
         """
         Returns a single week in a string (no newline).
         """
-        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
-        )
+        return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
 
     def formatweekday(self, day, width):
         """
@@ -404,11 +388,10 @@ class TextCalendar(Calendar):
         """
         print(self.formatmonth(theyear, themonth, w, l), end='')
 
-    def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None):
+    def formatmonth(self, theyear, themonth, w=0, l=0):
         """
         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)
@@ -417,11 +400,11 @@ class TextCalendar(Calendar):
         s += self.formatweekheader(w).rstrip()
         s += '\n' * l
         for week in self.monthdays2calendar(theyear, themonth):
-            s += self.formatweek(week, w, highlight_day=highlight_day).rstrip()
+            s += self.formatweek(week, w).rstrip()
             s += '\n' * l
         return s
 
-    def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None):
+    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
         """
         Returns a year's calendar as a multi-line string.
         """
@@ -446,23 +429,15 @@ class TextCalendar(Calendar):
             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 k, cal in enumerate(row):
+                for cal in row:
                     if j >= len(cal):
                         weeks.append('')
                     else:
-                        day = highlight_day.day if k == month_pos else None
-                        weeks.append(
-                            self.formatweek(cal[j], w, highlight_day=day)
-                        )
+                        weeks.append(self.formatweek(cal[j], w))
                 a(formatstring(weeks, colwidth, c).rstrip())
                 a('\n' * l)
         return ''.join(v)
@@ -672,6 +647,111 @@ class LocaleHTMLCalendar(HTMLCalendar):
         with different_locale(self.locale):
             return super().formatmonthname(theyear, themonth, withyear)
 
+
+class _CLIDemoCalendar(LocaleTextCalendar):
+    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):
+        """
+        Returns a single week in a string (no newline).
+        """
+        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 formatmonth(self, theyear, themonth, w=0, l=0):
+        """
+        Return a month's calendar string (multi-line).
+        """
+        if (
+            self.highlight_day
+            and self.highlight_day.year == theyear
+            and self.highlight_day.month == themonth
+        ):
+            highlight_day = self.highlight_day.day
+        else:
+            highlight_day = None
+        w = max(2, w)
+        l = max(1, l)
+        s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
+        s = s.rstrip()
+        s += '\n' * l
+        s += self.formatweekheader(w).rstrip()
+        s += '\n' * l
+        for week in self.monthdays2calendar(theyear, themonth):
+            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):
+        """
+        Returns a year's calendar as a multi-line string.
+        """
+        w = max(2, w)
+        l = max(1, l)
+        c = max(2, c)
+        colwidth = (w + 1) * 7 - 1
+        v = []
+        a = v.append
+        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
+        a('\n'*l)
+        header = self.formatweekheader(w)
+        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
+            # months in this row
+            months = range(m*i+1, min(m*(i+1)+1, 13))
+            a('\n'*l)
+            names = (self.formatmonthname(theyear, k, colwidth, False)
+                     for k in months)
+            a(formatstring(names, colwidth, c).rstrip())
+            a('\n'*l)
+            headers = (header for k in months)
+            a(formatstring(headers, colwidth, c).rstrip())
+            a('\n'*l)
+
+            if (
+                self.highlight_day
+                and self.highlight_day.year == theyear
+                and self.highlight_day.month in months
+            ):
+                month_pos = months.index(self.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 k, cal in enumerate(row):
+                    if j >= len(cal):
+                        weeks.append('')
+                    else:
+                        day = (
+                            self.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)
+
+
 # Support for old module level interface
 c = TextCalendar()
 
@@ -813,26 +893,21 @@ def main(args=None):
             write(cal.formatyearpage(options.year, **optdict))
     else:
         if options.locale:
-            cal = LocaleTextCalendar(locale=locale)
+            cal = _CLIDemoCalendar(highlight_day=today, locale=locale)
         else:
-            cal = TextCalendar()
+            cal = _CLIDemoCalendar(highlight_day=today)
         cal.setfirstweekday(options.first_weekday)
         optdict = dict(w=options.width, l=options.lines)
         if options.month is None:
             optdict["c"] = options.spacing
             optdict["m"] = options.months
-        if options.month is not None:
+        else:
             _validate_month(options.month)
         if options.year is None:
-            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/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst b/Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst
new file mode 100644 (file)
index 0000000..eaff033
--- /dev/null
@@ -0,0 +1,2 @@
+Put CLI calendar highlighting in private class, removing ``highlight_day``
+from public :class:`calendar.TextCalendar` API. Patch by Hugo van Kemenade.