Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/calendar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,11 @@ The following options are accepted:
By default, today's date is highlighted in color and can be
:ref:`controlled using environment variables <using-on-controlling-color>`.

.. versionchanged:: next
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 <using-on-controlling-color>`.

*HTML-mode options:*

.. option:: --css CSS, -c CSS
Expand Down
11 changes: 8 additions & 3 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -713,14 +713,19 @@ 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`.)
* :mod:`calendar`'s :ref:`command-line <calendar-cli>` text output has more
color. This can be controlled with :ref:`environment variables
<using-on-controlling-color>`.
(Contributed by Hugo van Kemenade in :gh:`148352`.)

* The :mod:`calendar`'s :ref:`command-line <calendar-cli>` 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
-----------
Expand Down
12 changes: 12 additions & 0 deletions Lib/_colorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`."""
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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(),
Expand Down
81 changes: 59 additions & 22 deletions Lib/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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)):
Expand Down Expand Up @@ -843,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",
Expand All @@ -879,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",
Expand All @@ -890,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)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why stop telling the user the defaults? I think it adds clarity in certain situations :) up to you however

help="weekday (0 is Monday, 6 is Sunday) to start each week"
)
parser.add_argument(
"year",
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add more colour to :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Add more colour to :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade.
Add more color to :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade.

I’m in favor of using American English since that is how most of the docs are written, but up to you ;)

Loading