diff --git a/Lib/calendar.py b/Lib/calendar.py index 7550d52c0a..657396439c 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -15,7 +15,9 @@ "monthcalendar", "prmonth", "month", "prcal", "calendar", "timegm", "month_name", "month_abbr", "day_name", "day_abbr", "Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar", - "LocaleHTMLCalendar", "weekheader"] + "LocaleHTMLCalendar", "weekheader", + "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", + "SATURDAY", "SUNDAY"] # Exception raised for bad input (with string parameter for details) error = ValueError @@ -546,71 +548,67 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): class different_locale: def __init__(self, locale): self.locale = locale + self.oldlocale = None def __enter__(self): - self.oldlocale = _locale.getlocale(_locale.LC_TIME) + self.oldlocale = _locale.setlocale(_locale.LC_TIME, None) _locale.setlocale(_locale.LC_TIME, self.locale) def __exit__(self, *args): + if self.oldlocale is None: + return _locale.setlocale(_locale.LC_TIME, self.oldlocale) +def _get_default_locale(): + locale = _locale.setlocale(_locale.LC_TIME, None) + if locale == "C": + with different_locale(""): + # The LC_TIME locale does not seem to be configured: + # get the user preferred locale. + locale = _locale.setlocale(_locale.LC_TIME, None) + return locale + + class LocaleTextCalendar(TextCalendar): """ This class can be passed a locale name in the constructor and will return - month and weekday names in the specified locale. If this locale includes - an encoding all strings containing month and weekday names will be returned - as unicode. + month and weekday names in the specified locale. """ def __init__(self, firstweekday=0, locale=None): TextCalendar.__init__(self, firstweekday) if locale is None: - locale = _locale.getdefaultlocale() + locale = _get_default_locale() self.locale = locale def formatweekday(self, day, width): with different_locale(self.locale): - if width >= 9: - names = day_name - else: - names = day_abbr - name = names[day] - return name[:width].center(width) + return super().formatweekday(day, width) def formatmonthname(self, theyear, themonth, width, withyear=True): with different_locale(self.locale): - s = month_name[themonth] - if withyear: - s = "%s %r" % (s, theyear) - return s.center(width) + return super().formatmonthname(theyear, themonth, width, withyear) class LocaleHTMLCalendar(HTMLCalendar): """ This class can be passed a locale name in the constructor and will return - month and weekday names in the specified locale. If this locale includes - an encoding all strings containing month and weekday names will be returned - as unicode. + month and weekday names in the specified locale. """ def __init__(self, firstweekday=0, locale=None): HTMLCalendar.__init__(self, firstweekday) if locale is None: - locale = _locale.getdefaultlocale() + locale = _get_default_locale() self.locale = locale def formatweekday(self, day): with different_locale(self.locale): - s = day_abbr[day] - return '%s' % (self.cssclasses[day], s) + return super().formatweekday(day) def formatmonthname(self, theyear, themonth, withyear=True): with different_locale(self.locale): - s = month_name[themonth] - if withyear: - s = '%s %s' % (s, theyear) - return '%s' % s - + return super().formatmonthname(theyear, themonth, withyear) # Support for old module level interface c = TextCalendar() diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 091ab4a4d2..42490c8366 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -564,6 +564,43 @@ def test_locale_calendars(self): new_october = calendar.TextCalendar().formatmonthname(2010, 10, 10) self.assertEqual(old_october, new_october) + def test_locale_calendar_formatweekday(self): + try: + # formatweekday uses different day names based on the available width. + cal = calendar.LocaleTextCalendar(locale='en_US') + # For short widths, a centered, abbreviated name is used. + self.assertEqual(cal.formatweekday(0, 5), " Mon ") + # For really short widths, even the abbreviated name is truncated. + self.assertEqual(cal.formatweekday(0, 2), "Mo") + # For long widths, the full day name is used. + self.assertEqual(cal.formatweekday(0, 10), " Monday ") + except locale.Error: + raise unittest.SkipTest('cannot set the en_US locale') + + def test_locale_html_calendar_custom_css_class_month_name(self): + try: + cal = calendar.LocaleHTMLCalendar(locale='') + local_month = cal.formatmonthname(2010, 10, 10) + except locale.Error: + # cannot set the system default locale -- skip rest of test + raise unittest.SkipTest('cannot set the system default locale') + self.assertIn('class="month"', local_month) + cal.cssclass_month_head = "text-center month" + local_month = cal.formatmonthname(2010, 10, 10) + self.assertIn('class="text-center month"', local_month) + + def test_locale_html_calendar_custom_css_class_weekday(self): + try: + cal = calendar.LocaleHTMLCalendar(locale='') + local_weekday = cal.formatweekday(6) + except locale.Error: + # cannot set the system default locale -- skip rest of test + raise unittest.SkipTest('cannot set the system default locale') + self.assertIn('class="sun"', local_weekday) + cal.cssclasses_weekday_head = ["mon2", "tue2", "wed2", "thu2", "fri2", "sat2", "sun2"] + local_weekday = cal.formatweekday(6) + self.assertIn('class="sun2"', local_weekday) + def test_itermonthdays3(self): # ensure itermonthdays3 doesn't overflow after datetime.MAXYEAR list(calendar.Calendar().itermonthdays3(datetime.MAXYEAR, 12)) @@ -595,6 +632,14 @@ def test_itermonthdays2(self): self.assertEqual(days[0][1], firstweekday) self.assertEqual(days[-1][1], (firstweekday - 1) % 7) + def test_iterweekdays(self): + week0 = list(range(7)) + for firstweekday in range(7): + cal = calendar.Calendar(firstweekday) + week = list(cal.iterweekdays()) + expected = week0[firstweekday:] + week0[:firstweekday] + self.assertEqual(week, expected) + class MonthCalendarTestCase(unittest.TestCase): def setUp(self): @@ -837,7 +882,8 @@ def test_option_locale(self): self.assertFailure('-L') self.assertFailure('--locale') self.assertFailure('-L', 'en') - lang, enc = locale.getdefaultlocale() + + lang, enc = locale.getlocale() lang = lang or 'C' enc = enc or 'UTF-8' try: @@ -912,11 +958,10 @@ def test_html_output_year_css(self): class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {'mdays', 'January', 'February', 'EPOCH', - 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', - 'SATURDAY', 'SUNDAY', 'different_locale', 'c', - 'prweek', 'week', 'format', 'formatstring', 'main', - 'monthlen', 'prevmonth', 'nextmonth'} + not_exported = { + 'mdays', 'January', 'February', 'EPOCH', + 'different_locale', 'c', 'prweek', 'week', 'format', + 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth'} support.check__all__(self, calendar, not_exported=not_exported)