Skip to content

Update calendar.py from CPython 3.11 #4669

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 8, 2023
Merged
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
52 changes: 25 additions & 27 deletions Lib/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 '<th class="%s">%s</th>' % (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 '<tr><th colspan="7" class="month">%s</th></tr>' % s

return super().formatmonthname(theyear, themonth, withyear)

# Support for old module level interface
c = TextCalendar()
Expand Down
57 changes: 51 additions & 6 deletions Lib/test/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)


Expand Down