Skip to content

Commit

Permalink
Highlight today in colour in calendar CLI output
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed Dec 21, 2024
1 parent 2a66dd3 commit d04770a
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 10 deletions.
5 changes: 4 additions & 1 deletion Doc/library/calendar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ The :mod:`calendar` module defines the following exceptions:

.. _calendar-cli:

Command-Line Usage
Command-line usage
------------------

.. versionadded:: 2.5
Expand Down Expand Up @@ -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 <using-on-controlling-color>`.

*HTML-mode options:*

Expand Down
13 changes: 13 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <calendar-cli>` 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:`100000000000000000000`.)


concurrent.futures
------------------

Expand Down
2 changes: 2 additions & 0 deletions Lib/_colorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
49 changes: 40 additions & 9 deletions Lib/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_color = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}"
reset = ansi.RESET
else:
highlight_color = reset = ""

return ' '.join(
(
f"{highlight_color}{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):
"""
Expand Down Expand Up @@ -388,7 +404,7 @@ 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).
"""
Expand All @@ -400,11 +416,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.
"""
Expand All @@ -428,15 +444,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)
Expand Down Expand Up @@ -765,6 +790,7 @@ def main(args=None):
sys.exit(1)

locale = options.locale, options.encoding
today = datetime.date.today()

if options.type == "html":
if options.month:
Expand All @@ -781,7 +807,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:
Expand All @@ -797,10 +823,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:
Expand Down

0 comments on commit d04770a

Please sign in to comment.