Skip to content

Commit 269051d

Browse files
gh-90535: Fix support of interval>1 in logging.TimedRotatingFileHandler (GH-116220)
Fix support of interval values > 1 in logging.TimedRotatingFileHandler for when='MIDNIGHT' and when='Wx'.
1 parent 20578a1 commit 269051d

File tree

3 files changed

+173
-35
lines changed

3 files changed

+173
-35
lines changed

Lib/logging/handlers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,10 @@ def computeRollover(self, currentTime):
340340
daysToWait = self.dayOfWeek - day
341341
else:
342342
daysToWait = 6 - day + self.dayOfWeek + 1
343-
result += daysToWait * (60 * 60 * 24)
343+
result += daysToWait * _MIDNIGHT
344+
result += self.interval - _MIDNIGHT * 7
345+
else:
346+
result += self.interval - _MIDNIGHT
344347
if not self.utc:
345348
dstNow = t[-1]
346349
dstAtRollover = time.localtime(result)[-1]

Lib/test/test_logging.py

Lines changed: 166 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6577,6 +6577,129 @@ def test(current, expected):
65776577

65786578
fh.close()
65796579

6580+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6581+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6582+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6583+
def test_compute_rollover_MIDNIGHT_local_interval(self):
6584+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6585+
DT = datetime.datetime
6586+
def test(current, expected):
6587+
actual = fh.computeRollover(current.timestamp())
6588+
diff = actual - expected.timestamp()
6589+
if diff:
6590+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6591+
6592+
fh = logging.handlers.TimedRotatingFileHandler(
6593+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3)
6594+
6595+
test(DT(2012, 3, 8, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6596+
test(DT(2012, 3, 9, 0, 0), DT(2012, 3, 12, 0, 0))
6597+
test(DT(2012, 3, 9, 1, 0), DT(2012, 3, 12, 0, 0))
6598+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 13, 0, 0))
6599+
test(DT(2012, 3, 11, 0, 0), DT(2012, 3, 14, 0, 0))
6600+
test(DT(2012, 3, 11, 1, 0), DT(2012, 3, 14, 0, 0))
6601+
6602+
test(DT(2012, 11, 1, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6603+
test(DT(2012, 11, 2, 0, 0), DT(2012, 11, 5, 0, 0))
6604+
test(DT(2012, 11, 2, 1, 0), DT(2012, 11, 5, 0, 0))
6605+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 6, 0, 0))
6606+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 7, 0, 0))
6607+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 7, 0, 0))
6608+
6609+
fh.close()
6610+
6611+
fh = logging.handlers.TimedRotatingFileHandler(
6612+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3,
6613+
atTime=datetime.time(12, 0, 0))
6614+
6615+
test(DT(2012, 3, 8, 11, 59, 59), DT(2012, 3, 10, 12, 0))
6616+
test(DT(2012, 3, 8, 12, 0), DT(2012, 3, 11, 12, 0))
6617+
test(DT(2012, 3, 8, 13, 0), DT(2012, 3, 11, 12, 0))
6618+
test(DT(2012, 3, 10, 11, 59, 59), DT(2012, 3, 12, 12, 0))
6619+
test(DT(2012, 3, 10, 12, 0), DT(2012, 3, 13, 12, 0))
6620+
test(DT(2012, 3, 10, 13, 0), DT(2012, 3, 13, 12, 0))
6621+
6622+
test(DT(2012, 11, 1, 11, 59, 59), DT(2012, 11, 3, 12, 0))
6623+
test(DT(2012, 11, 1, 12, 0), DT(2012, 11, 4, 12, 0))
6624+
test(DT(2012, 11, 1, 13, 0), DT(2012, 11, 4, 12, 0))
6625+
test(DT(2012, 11, 3, 11, 59, 59), DT(2012, 11, 5, 12, 0))
6626+
test(DT(2012, 11, 3, 12, 0), DT(2012, 11, 6, 12, 0))
6627+
test(DT(2012, 11, 3, 13, 0), DT(2012, 11, 6, 12, 0))
6628+
6629+
fh.close()
6630+
6631+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6632+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6633+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6634+
def test_compute_rollover_W6_local_interval(self):
6635+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6636+
DT = datetime.datetime
6637+
def test(current, expected):
6638+
actual = fh.computeRollover(current.timestamp())
6639+
diff = actual - expected.timestamp()
6640+
if diff:
6641+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6642+
6643+
fh = logging.handlers.TimedRotatingFileHandler(
6644+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3)
6645+
6646+
test(DT(2012, 2, 19, 23, 59, 59), DT(2012, 3, 5, 0, 0))
6647+
test(DT(2012, 2, 20, 0, 0), DT(2012, 3, 12, 0, 0))
6648+
test(DT(2012, 2, 20, 1, 0), DT(2012, 3, 12, 0, 0))
6649+
test(DT(2012, 3, 4, 23, 59, 59), DT(2012, 3, 19, 0, 0))
6650+
test(DT(2012, 3, 5, 0, 0), DT(2012, 3, 26, 0, 0))
6651+
test(DT(2012, 3, 5, 1, 0), DT(2012, 3, 26, 0, 0))
6652+
6653+
test(DT(2012, 10, 14, 23, 59, 59), DT(2012, 10, 29, 0, 0))
6654+
test(DT(2012, 10, 15, 0, 0), DT(2012, 11, 5, 0, 0))
6655+
test(DT(2012, 10, 15, 1, 0), DT(2012, 11, 5, 0, 0))
6656+
test(DT(2012, 10, 28, 23, 59, 59), DT(2012, 11, 12, 0, 0))
6657+
test(DT(2012, 10, 29, 0, 0), DT(2012, 11, 19, 0, 0))
6658+
test(DT(2012, 10, 29, 1, 0), DT(2012, 11, 19, 0, 0))
6659+
6660+
fh.close()
6661+
6662+
fh = logging.handlers.TimedRotatingFileHandler(
6663+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6664+
atTime=datetime.time(0, 0, 0))
6665+
6666+
test(DT(2012, 2, 25, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6667+
test(DT(2012, 2, 26, 0, 0), DT(2012, 3, 18, 0, 0))
6668+
test(DT(2012, 2, 26, 1, 0), DT(2012, 3, 18, 0, 0))
6669+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 25, 0, 0))
6670+
test(DT(2012, 3, 11, 0, 0), DT(2012, 4, 1, 0, 0))
6671+
test(DT(2012, 3, 11, 1, 0), DT(2012, 4, 1, 0, 0))
6672+
6673+
test(DT(2012, 10, 20, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6674+
test(DT(2012, 10, 21, 0, 0), DT(2012, 11, 11, 0, 0))
6675+
test(DT(2012, 10, 21, 1, 0), DT(2012, 11, 11, 0, 0))
6676+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 18, 0, 0))
6677+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 25, 0, 0))
6678+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 25, 0, 0))
6679+
6680+
fh.close()
6681+
6682+
fh = logging.handlers.TimedRotatingFileHandler(
6683+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6684+
atTime=datetime.time(12, 0, 0))
6685+
6686+
test(DT(2012, 2, 18, 11, 59, 59), DT(2012, 3, 4, 12, 0))
6687+
test(DT(2012, 2, 19, 12, 0), DT(2012, 3, 11, 12, 0))
6688+
test(DT(2012, 2, 19, 13, 0), DT(2012, 3, 11, 12, 0))
6689+
test(DT(2012, 3, 4, 11, 59, 59), DT(2012, 3, 18, 12, 0))
6690+
test(DT(2012, 3, 4, 12, 0), DT(2012, 3, 25, 12, 0))
6691+
test(DT(2012, 3, 4, 13, 0), DT(2012, 3, 25, 12, 0))
6692+
6693+
test(DT(2012, 10, 14, 11, 59, 59), DT(2012, 10, 28, 12, 0))
6694+
test(DT(2012, 10, 14, 12, 0), DT(2012, 11, 4, 12, 0))
6695+
test(DT(2012, 10, 14, 13, 0), DT(2012, 11, 4, 12, 0))
6696+
test(DT(2012, 10, 28, 11, 59, 59), DT(2012, 11, 11, 12, 0))
6697+
test(DT(2012, 10, 28, 12, 0), DT(2012, 11, 18, 12, 0))
6698+
test(DT(2012, 10, 28, 13, 0), DT(2012, 11, 18, 12, 0))
6699+
6700+
fh.close()
6701+
6702+
65806703
def secs(**kw):
65816704
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
65826705

@@ -6588,40 +6711,49 @@ def secs(**kw):
65886711
# current time (epoch start) is a Thursday, W0 means Monday
65896712
('W0', secs(days=4, hours=24)),
65906713
):
6591-
def test_compute_rollover(self, when=when, exp=exp):
6592-
rh = logging.handlers.TimedRotatingFileHandler(
6593-
self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
6594-
currentTime = 0.0
6595-
actual = rh.computeRollover(currentTime)
6596-
if exp != actual:
6597-
# Failures occur on some systems for MIDNIGHT and W0.
6598-
# Print detailed calculation for MIDNIGHT so we can try to see
6599-
# what's going on
6600-
if when == 'MIDNIGHT':
6601-
try:
6602-
if rh.utc:
6603-
t = time.gmtime(currentTime)
6604-
else:
6605-
t = time.localtime(currentTime)
6606-
currentHour = t[3]
6607-
currentMinute = t[4]
6608-
currentSecond = t[5]
6609-
# r is the number of seconds left between now and midnight
6610-
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6611-
currentMinute) * 60 +
6612-
currentSecond)
6613-
result = currentTime + r
6614-
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6615-
print('currentHour: %s' % currentHour, file=sys.stderr)
6616-
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6617-
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6618-
print('r: %s' % r, file=sys.stderr)
6619-
print('result: %s' % result, file=sys.stderr)
6620-
except Exception as e:
6621-
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6622-
self.assertEqual(exp, actual)
6623-
rh.close()
6624-
setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
6714+
for interval in 1, 3:
6715+
def test_compute_rollover(self, when=when, interval=interval, exp=exp):
6716+
rh = logging.handlers.TimedRotatingFileHandler(
6717+
self.fn, encoding="utf-8", when=when, interval=interval, backupCount=0, utc=True)
6718+
currentTime = 0.0
6719+
actual = rh.computeRollover(currentTime)
6720+
if when.startswith('W'):
6721+
exp += secs(days=7*(interval-1))
6722+
else:
6723+
exp *= interval
6724+
if exp != actual:
6725+
# Failures occur on some systems for MIDNIGHT and W0.
6726+
# Print detailed calculation for MIDNIGHT so we can try to see
6727+
# what's going on
6728+
if when == 'MIDNIGHT':
6729+
try:
6730+
if rh.utc:
6731+
t = time.gmtime(currentTime)
6732+
else:
6733+
t = time.localtime(currentTime)
6734+
currentHour = t[3]
6735+
currentMinute = t[4]
6736+
currentSecond = t[5]
6737+
# r is the number of seconds left between now and midnight
6738+
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6739+
currentMinute) * 60 +
6740+
currentSecond)
6741+
result = currentTime + r
6742+
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6743+
print('currentHour: %s' % currentHour, file=sys.stderr)
6744+
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6745+
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6746+
print('r: %s' % r, file=sys.stderr)
6747+
print('result: %s' % result, file=sys.stderr)
6748+
except Exception as e:
6749+
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6750+
self.assertEqual(exp, actual)
6751+
rh.close()
6752+
name = "test_compute_rollover_%s" % when
6753+
if interval > 1:
6754+
name += "_interval"
6755+
test_compute_rollover.__name__ = name
6756+
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)
66256757

66266758

66276759
@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix support of *interval* values > 1 in
2+
:class:`logging.TimedRotatingFileHandler` for ``when='MIDNIGHT'`` and
3+
``when='Wx'``.

0 commit comments

Comments
 (0)