Skip to content

Commit 2a62ef7

Browse files
authored
Merge pull request RustPython#5169 from Blues-star/main
Update `calendar.py` and `test_calendar.py` from CPython v3.12
2 parents 1c93c16 + 258342e commit 2a62ef7

File tree

2 files changed

+68
-25
lines changed

2 files changed

+68
-25
lines changed

Lib/calendar.py

+53-23
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@
77

88
import sys
99
import datetime
10+
from enum import IntEnum, global_enum
1011
import locale as _locale
1112
from itertools import repeat
13+
import warnings
1214

1315
__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
1416
"firstweekday", "isleap", "leapdays", "weekday", "monthrange",
1517
"monthcalendar", "prmonth", "month", "prcal", "calendar",
1618
"timegm", "month_name", "month_abbr", "day_name", "day_abbr",
1719
"Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar",
1820
"LocaleHTMLCalendar", "weekheader",
21+
"Day", "Month", "JANUARY", "FEBRUARY", "MARCH",
22+
"APRIL", "MAY", "JUNE", "JULY",
23+
"AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
1924
"MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY",
2025
"SATURDAY", "SUNDAY"]
2126

@@ -37,9 +42,46 @@ def __str__(self):
3742
return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday
3843

3944

40-
# Constants for months referenced later
41-
January = 1
42-
February = 2
45+
def __getattr__(name):
46+
if name in ('January', 'February'):
47+
warnings.warn(f"The '{name}' attribute is deprecated, use '{name.upper()}' instead",
48+
DeprecationWarning, stacklevel=2)
49+
if name == 'January':
50+
return 1
51+
else:
52+
return 2
53+
54+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
55+
56+
57+
# Constants for months
58+
@global_enum
59+
class Month(IntEnum):
60+
JANUARY = 1
61+
FEBRUARY = 2
62+
MARCH = 3
63+
APRIL = 4
64+
MAY = 5
65+
JUNE = 6
66+
JULY = 7
67+
AUGUST = 8
68+
SEPTEMBER = 9
69+
OCTOBER = 10
70+
NOVEMBER = 11
71+
DECEMBER = 12
72+
73+
74+
# Constants for days
75+
@global_enum
76+
class Day(IntEnum):
77+
MONDAY = 0
78+
TUESDAY = 1
79+
WEDNESDAY = 2
80+
THURSDAY = 3
81+
FRIDAY = 4
82+
SATURDAY = 5
83+
SUNDAY = 6
84+
4385

4486
# Number of days per month (except for February in leap years)
4587
mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -95,9 +137,6 @@ def __len__(self):
95137
month_name = _localized_month('%B')
96138
month_abbr = _localized_month('%b')
97139

98-
# Constants for weekdays
99-
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
100-
101140

102141
def isleap(year):
103142
"""Return True for leap years, False for non-leap years."""
@@ -116,7 +155,7 @@ def weekday(year, month, day):
116155
"""Return weekday (0-6 ~ Mon-Sun) for year, month (1-12), day (1-31)."""
117156
if not datetime.MINYEAR <= year <= datetime.MAXYEAR:
118157
year = 2000 + year % 400
119-
return datetime.date(year, month, day).weekday()
158+
return Day(datetime.date(year, month, day).weekday())
120159

121160

122161
def monthrange(year, month):
@@ -125,12 +164,12 @@ def monthrange(year, month):
125164
if not 1 <= month <= 12:
126165
raise IllegalMonthError(month)
127166
day1 = weekday(year, month, 1)
128-
ndays = mdays[month] + (month == February and isleap(year))
167+
ndays = mdays[month] + (month == FEBRUARY and isleap(year))
129168
return day1, ndays
130169

131170

132171
def _monthlen(year, month):
133-
return mdays[month] + (month == February and isleap(year))
172+
return mdays[month] + (month == FEBRUARY and isleap(year))
134173

135174

136175
def _prevmonth(year, month):
@@ -260,10 +299,7 @@ def yeardatescalendar(self, year, width=3):
260299
Each month contains between 4 and 6 weeks and each week contains 1-7
261300
days. Days are datetime.date objects.
262301
"""
263-
months = [
264-
self.monthdatescalendar(year, i)
265-
for i in range(January, January+12)
266-
]
302+
months = [self.monthdatescalendar(year, m) for m in Month]
267303
return [months[i:i+width] for i in range(0, len(months), width) ]
268304

269305
def yeardays2calendar(self, year, width=3):
@@ -273,10 +309,7 @@ def yeardays2calendar(self, year, width=3):
273309
(day number, weekday number) tuples. Day numbers outside this month are
274310
zero.
275311
"""
276-
months = [
277-
self.monthdays2calendar(year, i)
278-
for i in range(January, January+12)
279-
]
312+
months = [self.monthdays2calendar(year, m) for m in Month]
280313
return [months[i:i+width] for i in range(0, len(months), width) ]
281314

282315
def yeardayscalendar(self, year, width=3):
@@ -285,10 +318,7 @@ def yeardayscalendar(self, year, width=3):
285318
yeardatescalendar()). Entries in the week lists are day numbers.
286319
Day numbers outside this month are zero.
287320
"""
288-
months = [
289-
self.monthdayscalendar(year, i)
290-
for i in range(January, January+12)
291-
]
321+
months = [self.monthdayscalendar(year, m) for m in Month]
292322
return [months[i:i+width] for i in range(0, len(months), width) ]
293323

294324

@@ -509,7 +539,7 @@ def formatyear(self, theyear, width=3):
509539
a('\n')
510540
a('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
511541
width, self.cssclass_year_head, theyear))
512-
for i in range(January, January+12, width):
542+
for i in range(JANUARY, JANUARY+12, width):
513543
# months in this row
514544
months = range(i, min(i+width, 13))
515545
a('<tr>')
@@ -693,7 +723,7 @@ def main(args):
693723
parser.add_argument(
694724
"-L", "--locale",
695725
default=None,
696-
help="locale to be used from month and weekday names"
726+
help="locale to use for month and weekday names"
697727
)
698728
parser.add_argument(
699729
"-e", "--encoding",

Lib/test/test_calendar.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
import datetime
1010
import os
11+
import warnings
1112

1213
# From https://en.wikipedia.org/wiki/Leap_year_starting_on_Saturday
1314
result_0_02_text = """\
@@ -490,6 +491,14 @@ def test_format(self):
490491
self.assertEqual(out.getvalue().strip(), "1 2 3")
491492

492493
class CalendarTestCase(unittest.TestCase):
494+
495+
def test_deprecation_warning(self):
496+
with self.assertWarnsRegex(
497+
DeprecationWarning,
498+
"The 'January' attribute is deprecated, use 'JANUARY' instead"
499+
):
500+
calendar.January
501+
493502
def test_isleap(self):
494503
# Make sure that the return is right for a few years, and
495504
# ensure that the return values are 1 or 0, not just true or
@@ -568,11 +577,15 @@ def test_locale_calendar_formatweekday(self):
568577
try:
569578
# formatweekday uses different day names based on the available width.
570579
cal = calendar.LocaleTextCalendar(locale='en_US')
580+
# For really short widths, the abbreviated name is truncated.
581+
self.assertEqual(cal.formatweekday(0, 1), "M")
582+
self.assertEqual(cal.formatweekday(0, 2), "Mo")
571583
# For short widths, a centered, abbreviated name is used.
584+
self.assertEqual(cal.formatweekday(0, 3), "Mon")
572585
self.assertEqual(cal.formatweekday(0, 5), " Mon ")
573-
# For really short widths, even the abbreviated name is truncated.
574-
self.assertEqual(cal.formatweekday(0, 2), "Mo")
586+
self.assertEqual(cal.formatweekday(0, 8), " Mon ")
575587
# For long widths, the full day name is used.
588+
self.assertEqual(cal.formatweekday(0, 9), " Monday ")
576589
self.assertEqual(cal.formatweekday(0, 10), " Monday ")
577590
except locale.Error:
578591
raise unittest.SkipTest('cannot set the en_US locale')

0 commit comments

Comments
 (0)