diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 41e9e28e0c0..a415b4792a6 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -147,7 +147,7 @@ it's the base calendar for all computations. This class can be used to generate HTML calendars. - :class:`HTMLCalendar` instances have the following methods: + :class:`!HTMLCalendar` instances have the following methods: .. method:: formatmonth(theyear, themonth, withyear=True) @@ -171,6 +171,85 @@ it's the base calendar for all computations. output (defaulting to the system default encoding). + :class:`!HTMLCalendar` has the following attributes you can override to + customize the CSS classes used by the calendar: + + .. attribute:: cssclasses + + A list of CSS classes used for each weekday. The default class list is:: + + cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] + + more styles can be added for each day:: + + cssclasses = ["mon text-bold", "tue", "wed", "thu", "fri", "sat", "sun red"] + + Note that the length of this list must be seven items. + + + .. attribute:: cssclass_noday + + The CSS class for a weekday occurring in the previous or coming month. + + .. versionadded:: 3.7 + + + .. attribute:: cssclasses_weekday_head + + A list of CSS classes used for weekday names in the header row. + The default is the same as :attr:`cssclasses`. + + .. versionadded:: 3.7 + + + .. attribute:: cssclass_month_head + + The month's head CSS class (used by :meth:`formatmonthname`). + The default value is ``"month"``. + + .. versionadded:: 3.7 + + + .. attribute:: cssclass_month + + The CSS class for the whole month's table (used by :meth:`formatmonth`). + The default value is ``"month"``. + + .. versionadded:: 3.7 + + + .. attribute:: cssclass_year + + The CSS class for the whole year's table of tables (used by + :meth:`formatyear`). The default value is ``"year"``. + + .. versionadded:: 3.7 + + + .. attribute:: cssclass_year_head + + The CSS class for the table head for the whole year (used by + :meth:`formatyear`). The default value is ``"year"``. + + .. versionadded:: 3.7 + + + Note that although the naming for the above described class attributes is + singular (e.g. ``cssclass_month`` ``cssclass_noday``), one can replace the + single CSS class with a space separated list of CSS classes, for example:: + + "text-bold text-red" + + Here is an example how :class:`!HTMLCalendar` can be customized:: + + class CustomHTMLCal(calendar.HTMLCalendar): + cssclasses = [style + " text-nowrap" for style in + calendar.HTMLCalendar.cssclasses] + cssclass_month_head = "text-center month-head" + cssclass_month = "text-center month" + cssclass_year = "text-italic lead" + + .. class:: LocaleTextCalendar(firstweekday=0, locale=None) This subclass of :class:`TextCalendar` can be passed a locale name in the diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 761c85fd220..602db8c0ac8 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -103,13 +103,6 @@ New Modules Improved Modules ================ -cgi ---- - -:func:`~cgi.parse_multipart` returns the same results as -:class:`~FieldStorage` : for non-file fields, the value associated to a key -is a list of strings, not bytes. -(Contributed by Pierre Quentel in :issue:`29979`.) binascii -------- @@ -118,6 +111,22 @@ The :func:`~binascii.b2a_uu` function now accepts an optional *backtick* keyword argument. When it's true, zeros are represented by ``'`'`` instead of spaces. (Contributed by Xiang Zhang in :issue:`30103`.) + +calendar +-------- + +The :class:`~calendar.HTMLCalendar` has added new class attribute which ease the +customisation the CSS classes in the produced HTML calendar. +(Contributed by Oz Tiram in :issue:`30095`.) + +cgi +--- + +:func:`~cgi.parse_multipart` returns the same results as +:class:`~FieldStorage` : for non-file fields, the value associated to a key +is a list of strings, not bytes. +(Contributed by Pierre Quentel in :issue:`29979`.) + contextlib ---------- diff --git a/Lib/calendar.py b/Lib/calendar.py index 28ac56fdbe8..0218e2d3977 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -382,12 +382,31 @@ class HTMLCalendar(Calendar): # CSS classes for the day s cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] + # CSS classes for the day s + cssclasses_weekday_head = cssclasses + + # CSS class for the days before and after current month + cssclass_noday = "noday" + + # CSS class for the month's head + cssclass_month_head = "month" + + # CSS class for the month + cssclass_month = "month" + + # CSS class for the year's table head + cssclass_year_head = "year" + + # CSS class for the whole year table + cssclass_year = "year" + def formatday(self, day, weekday): """ Return a day as a table cell. """ if day == 0: - return ' ' # day outside month + # day outside month + return ' ' % self.cssclass_noday else: return '%d' % (self.cssclasses[weekday], day) @@ -402,7 +421,8 @@ def formatweekday(self, day): """ Return a weekday name as a table header. """ - return '%s' % (self.cssclasses[day], day_abbr[day]) + return '%s' % ( + self.cssclasses_weekday_head[day], day_abbr[day]) def formatweekheader(self): """ @@ -419,7 +439,8 @@ def formatmonthname(self, theyear, themonth, withyear=True): s = '%s %s' % (month_name[themonth], theyear) else: s = '%s' % month_name[themonth] - return '%s' % s + return '%s' % ( + self.cssclass_month_head, s) def formatmonth(self, theyear, themonth, withyear=True): """ @@ -427,7 +448,8 @@ def formatmonth(self, theyear, themonth, withyear=True): """ v = [] a = v.append - a('') + a('
' % ( + self.cssclass_month)) a('\n') a(self.formatmonthname(theyear, themonth, withyear=withyear)) a('\n') @@ -447,9 +469,11 @@ def formatyear(self, theyear, width=3): v = [] a = v.append width = max(width, 1) - a('
') + a('
' % + self.cssclass_year) a('\n') - a('' % (width, theyear)) + a('' % ( + width, self.cssclass_year_head, theyear)) for i in range(January, January+12, width): # months in this row months = range(i, min(i+width, 13)) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index bd57653ffad..c777f648356 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -57,19 +57,22 @@ 25 26 27 28 29 30 31 29 30 27 28 29 30 31 """ + +default_format = dict(year="year", month="month", encoding="ascii") + result_2004_html = """\ - + - +Calendar for 2004 -
%s
%s
-
2004
- +
January
+
2004
+ @@ -77,8 +80,8 @@
January
MonTueWedThuFriSatSun
   1234
567891011
19202122232425
262728293031 
-
- +
February
+ @@ -86,8 +89,8 @@
February
MonTueWedThuFriSatSun
      1
2345678
16171819202122
23242526272829
-
- +
March
+ @@ -95,8 +98,8 @@
March
MonTueWedThuFriSatSun
1234567
891011121314
22232425262728
293031    
-
- +
April
+ @@ -104,8 +107,8 @@
April
MonTueWedThuFriSatSun
   1234
567891011
19202122232425
2627282930  
-
- +
May
+ @@ -114,8 +117,8 @@
May
MonTueWedThuFriSatSun
     12
3456789
24252627282930
31      
-
- +
June
+ @@ -123,8 +126,8 @@
June
MonTueWedThuFriSatSun
 123456
78910111213
21222324252627
282930    
-
- +
July
+ @@ -132,8 +135,8 @@
July
MonTueWedThuFriSatSun
   1234
567891011
19202122232425
262728293031 
-
- +
August
+ @@ -142,8 +145,8 @@
August
MonTueWedThuFriSatSun
      1
2345678
23242526272829
3031     
-
- +
September
+ @@ -151,8 +154,8 @@
September
MonTueWedThuFriSatSun
  12345
6789101112
20212223242526
27282930   
-
- +
October
+ @@ -160,8 +163,8 @@
October
MonTueWedThuFriSatSun
    123
45678910
18192021222324
25262728293031
-
- +
November
+ @@ -169,8 +172,8 @@
November
MonTueWedThuFriSatSun
1234567
891011121314
22232425262728
2930     
-
- +
December
+ @@ -327,9 +330,12 @@ def neitherspacenordigit(c): def check_htmlcalendar_encoding(self, req, res): cal = calendar.HTMLCalendar() + format_ = default_format.copy() + format_["encoding"] = req or 'utf-8' + output = cal.formatyearpage(2004, encoding=req) self.assertEqual( - cal.formatyearpage(2004, encoding=req), - (result_2004_html % {'e': res}).encode(res) + output, + result_2004_html.format(**format_).encode(res) ) def test_output(self): @@ -825,7 +831,7 @@ def test_html_output_current_year(self): def test_html_output_year_encoding(self): stdout = self.run_ok('-t', 'html', '--encoding', 'ascii', '2004') self.assertEqual(stdout, - (result_2004_html % {'e': 'ascii'}).encode('ascii')) + result_2004_html.format(**default_format).encode('ascii')) def test_html_output_year_css(self): self.assertFailure('-t', 'html', '-c') @@ -844,5 +850,47 @@ def test__all__(self): support.check__all__(self, calendar, blacklist=blacklist) +class TestSubClassingCase(unittest.TestCase): + + def setUp(self): + + class CustomHTMLCal(calendar.HTMLCalendar): + cssclasses = [style + " text-nowrap" for style in + calendar.HTMLCalendar.cssclasses] + cssclasses_weekday_head = ["red", "blue", "green", "lilac", + "yellow", "orange", "pink"] + cssclass_month_head = "text-center month-head" + cssclass_month = "text-center month" + cssclass_year = "text-italic " + cssclass_year_head = "lead " + + self.cal = CustomHTMLCal() + + def test_formatmonthname(self): + self.assertIn('class="text-center month-head"', + self.cal.formatmonthname(2017, 5)) + + def test_formatmonth(self): + self.assertIn('class="text-center month"', + self.cal.formatmonth(2017, 5)) + + def test_formatweek(self): + weeks = self.cal.monthdays2calendar(2017, 5) + self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0])) + + def test_formatweek_head(self): + header = self.cal.formatweekheader() + for color in self.cal.cssclasses_weekday_head: + self.assertIn('
December
MonTueWedThuFriSatSun
  12345
6789101112
' % color, header) + + def test_format_year(self): + self.assertIn( + ('' % + self.cal.cssclass_year), self.cal.formatyear(2017)) + + def test_format_year_head(self): + self.assertIn('' % ( + 3, self.cal.cssclass_year_head, 2017), self.cal.formatyear(2017)) + if __name__ == "__main__": unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index 3000bc36d2c..85d2fc65c47 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1556,6 +1556,7 @@ Jeremy Thurgood Eric Tiedemann July Tikhonov Tracy Tims +Oz Tiram Oren Tirosh Tim Tisdall Jason Tishler
%s