Skip to content

Commit 1bccd6c

Browse files
hugovkencukou
andauthored
gh-128317: Move CLI calendar highlighting to private class (#129625)
Co-authored-by: Petr Viktorin <[email protected]>
1 parent a56ead0 commit 1bccd6c

File tree

3 files changed

+120
-58
lines changed

3 files changed

+120
-58
lines changed

Doc/library/calendar.rst

+3-18
Original file line numberDiff line numberDiff line change
@@ -166,18 +166,13 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is
166166
the specified width, representing an empty day. The *weekday* parameter
167167
is unused.
168168

169-
.. method:: formatweek(theweek, w=0, highlight_day=None)
169+
.. method:: formatweek(theweek, w=0)
170170

171171
Return a single week in a string with no newline. If *w* is provided, it
172172
specifies the width of the date columns, which are centered. Depends
173173
on the first weekday as specified in the constructor or set by the
174174
:meth:`setfirstweekday` method.
175175

176-
.. versionchanged:: 3.14
177-
If *highlight_day* is given, this date is highlighted in color.
178-
This can be :ref:`controlled using environment variables
179-
<using-on-controlling-color>`.
180-
181176

182177
.. method:: formatweekday(weekday, width)
183178

@@ -193,19 +188,14 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is
193188
settings and are padded to the specified width.
194189

195190

196-
.. method:: formatmonth(theyear, themonth, w=0, l=0, highlight_day=None)
191+
.. method:: formatmonth(theyear, themonth, w=0, l=0)
197192

198193
Return a month's calendar in a multi-line string. If *w* is provided, it
199194
specifies the width of the date columns, which are centered. If *l* is
200195
given, it specifies the number of lines that each week will use. Depends
201196
on the first weekday as specified in the constructor or set by the
202197
:meth:`setfirstweekday` method.
203198

204-
.. versionchanged:: 3.14
205-
If *highlight_day* is given, this date is highlighted in color.
206-
This can be :ref:`controlled using environment variables
207-
<using-on-controlling-color>`.
208-
209199

210200
.. method:: formatmonthname(theyear, themonth, width=0, withyear=True)
211201

@@ -220,7 +210,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is
220210
Print a month's calendar as returned by :meth:`formatmonth`.
221211

222212

223-
.. method:: formatyear(theyear, w=2, l=1, c=6, m=3, highlight_day=None)
213+
.. method:: formatyear(theyear, w=2, l=1, c=6, m=3)
224214

225215
Return a *m*-column calendar for an entire year as a multi-line string.
226216
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
229219
:meth:`setfirstweekday` method. The earliest year for which a calendar
230220
can be generated is platform-dependent.
231221

232-
.. versionchanged:: 3.14
233-
If *highlight_day* is given, this date is highlighted in color.
234-
This can be :ref:`controlled using environment variables
235-
<using-on-controlling-color>`.
236-
237222

238223
.. method:: pryear(theyear, w=2, l=1, c=6, m=3)
239224

Lib/calendar.py

+115-40
Original file line numberDiff line numberDiff line change
@@ -349,27 +349,11 @@ def formatday(self, day, weekday, width):
349349
s = '%2i' % day # right-align single-digit days
350350
return s.center(width)
351351

352-
def formatweek(self, theweek, width, *, highlight_day=None):
352+
def formatweek(self, theweek, width):
353353
"""
354354
Returns a single week in a string (no newline).
355355
"""
356-
if highlight_day:
357-
from _colorize import get_colors
358-
359-
ansi = get_colors()
360-
highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}"
361-
reset = ansi.RESET
362-
else:
363-
highlight = reset = ""
364-
365-
return ' '.join(
366-
(
367-
f"{highlight}{self.formatday(d, wd, width)}{reset}"
368-
if d == highlight_day
369-
else self.formatday(d, wd, width)
370-
)
371-
for (d, wd) in theweek
372-
)
356+
return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
373357

374358
def formatweekday(self, day, width):
375359
"""
@@ -404,11 +388,10 @@ def prmonth(self, theyear, themonth, w=0, l=0):
404388
"""
405389
print(self.formatmonth(theyear, themonth, w, l), end='')
406390

407-
def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None):
391+
def formatmonth(self, theyear, themonth, w=0, l=0):
408392
"""
409393
Return a month's calendar string (multi-line).
410394
"""
411-
highlight_day = highlight_day.day if highlight_day else None
412395
w = max(2, w)
413396
l = max(1, l)
414397
s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
@@ -417,11 +400,11 @@ def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None):
417400
s += self.formatweekheader(w).rstrip()
418401
s += '\n' * l
419402
for week in self.monthdays2calendar(theyear, themonth):
420-
s += self.formatweek(week, w, highlight_day=highlight_day).rstrip()
403+
s += self.formatweek(week, w).rstrip()
421404
s += '\n' * l
422405
return s
423406

424-
def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None):
407+
def formatyear(self, theyear, w=2, l=1, c=6, m=3):
425408
"""
426409
Returns a year's calendar as a multi-line string.
427410
"""
@@ -446,23 +429,15 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None):
446429
a(formatstring(headers, colwidth, c).rstrip())
447430
a('\n'*l)
448431

449-
if highlight_day and highlight_day.month in months:
450-
month_pos = months.index(highlight_day.month)
451-
else:
452-
month_pos = None
453-
454432
# max number of weeks for this row
455433
height = max(len(cal) for cal in row)
456434
for j in range(height):
457435
weeks = []
458-
for k, cal in enumerate(row):
436+
for cal in row:
459437
if j >= len(cal):
460438
weeks.append('')
461439
else:
462-
day = highlight_day.day if k == month_pos else None
463-
weeks.append(
464-
self.formatweek(cal[j], w, highlight_day=day)
465-
)
440+
weeks.append(self.formatweek(cal[j], w))
466441
a(formatstring(weeks, colwidth, c).rstrip())
467442
a('\n' * l)
468443
return ''.join(v)
@@ -672,6 +647,111 @@ def formatmonthname(self, theyear, themonth, withyear=True):
672647
with different_locale(self.locale):
673648
return super().formatmonthname(theyear, themonth, withyear)
674649

650+
651+
class _CLIDemoCalendar(LocaleTextCalendar):
652+
def __init__(self, highlight_day=None, *args, **kwargs):
653+
super().__init__(*args, **kwargs)
654+
self.highlight_day = highlight_day
655+
656+
def formatweek(self, theweek, width, *, highlight_day=None):
657+
"""
658+
Returns a single week in a string (no newline).
659+
"""
660+
if highlight_day:
661+
from _colorize import get_colors
662+
663+
ansi = get_colors()
664+
highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}"
665+
reset = ansi.RESET
666+
else:
667+
highlight = reset = ""
668+
669+
return ' '.join(
670+
(
671+
f"{highlight}{self.formatday(d, wd, width)}{reset}"
672+
if d == highlight_day
673+
else self.formatday(d, wd, width)
674+
)
675+
for (d, wd) in theweek
676+
)
677+
678+
def formatmonth(self, theyear, themonth, w=0, l=0):
679+
"""
680+
Return a month's calendar string (multi-line).
681+
"""
682+
if (
683+
self.highlight_day
684+
and self.highlight_day.year == theyear
685+
and self.highlight_day.month == themonth
686+
):
687+
highlight_day = self.highlight_day.day
688+
else:
689+
highlight_day = None
690+
w = max(2, w)
691+
l = max(1, l)
692+
s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
693+
s = s.rstrip()
694+
s += '\n' * l
695+
s += self.formatweekheader(w).rstrip()
696+
s += '\n' * l
697+
for week in self.monthdays2calendar(theyear, themonth):
698+
s += self.formatweek(week, w, highlight_day=highlight_day).rstrip()
699+
s += '\n' * l
700+
return s
701+
702+
def formatyear(self, theyear, w=2, l=1, c=6, m=3):
703+
"""
704+
Returns a year's calendar as a multi-line string.
705+
"""
706+
w = max(2, w)
707+
l = max(1, l)
708+
c = max(2, c)
709+
colwidth = (w + 1) * 7 - 1
710+
v = []
711+
a = v.append
712+
a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
713+
a('\n'*l)
714+
header = self.formatweekheader(w)
715+
for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
716+
# months in this row
717+
months = range(m*i+1, min(m*(i+1)+1, 13))
718+
a('\n'*l)
719+
names = (self.formatmonthname(theyear, k, colwidth, False)
720+
for k in months)
721+
a(formatstring(names, colwidth, c).rstrip())
722+
a('\n'*l)
723+
headers = (header for k in months)
724+
a(formatstring(headers, colwidth, c).rstrip())
725+
a('\n'*l)
726+
727+
if (
728+
self.highlight_day
729+
and self.highlight_day.year == theyear
730+
and self.highlight_day.month in months
731+
):
732+
month_pos = months.index(self.highlight_day.month)
733+
else:
734+
month_pos = None
735+
736+
# max number of weeks for this row
737+
height = max(len(cal) for cal in row)
738+
for j in range(height):
739+
weeks = []
740+
for k, cal in enumerate(row):
741+
if j >= len(cal):
742+
weeks.append('')
743+
else:
744+
day = (
745+
self.highlight_day.day if k == month_pos else None
746+
)
747+
weeks.append(
748+
self.formatweek(cal[j], w, highlight_day=day)
749+
)
750+
a(formatstring(weeks, colwidth, c).rstrip())
751+
a('\n' * l)
752+
return ''.join(v)
753+
754+
675755
# Support for old module level interface
676756
c = TextCalendar()
677757

@@ -813,26 +893,21 @@ def main(args=None):
813893
write(cal.formatyearpage(options.year, **optdict))
814894
else:
815895
if options.locale:
816-
cal = LocaleTextCalendar(locale=locale)
896+
cal = _CLIDemoCalendar(highlight_day=today, locale=locale)
817897
else:
818-
cal = TextCalendar()
898+
cal = _CLIDemoCalendar(highlight_day=today)
819899
cal.setfirstweekday(options.first_weekday)
820900
optdict = dict(w=options.width, l=options.lines)
821901
if options.month is None:
822902
optdict["c"] = options.spacing
823903
optdict["m"] = options.months
824-
if options.month is not None:
904+
else:
825905
_validate_month(options.month)
826906
if options.year is None:
827-
optdict["highlight_day"] = today
828907
result = cal.formatyear(today.year, **optdict)
829908
elif options.month is None:
830-
if options.year == today.year:
831-
optdict["highlight_day"] = today
832909
result = cal.formatyear(options.year, **optdict)
833910
else:
834-
if options.year == today.year and options.month == today.month:
835-
optdict["highlight_day"] = today
836911
result = cal.formatmonth(options.year, options.month, **optdict)
837912
write = sys.stdout.write
838913
if options.encoding:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Put CLI calendar highlighting in private class, removing ``highlight_day``
2+
from public :class:`calendar.TextCalendar` API. Patch by Hugo van Kemenade.

0 commit comments

Comments
 (0)