Skip to content

Commit e287d64

Browse files
serhiy-storchakamiss-islington
authored andcommitted
pythongh-90535: Fix support of interval>1 in logging.TimedRotatingFileHandler (pythonGH-116220)
Fix support of interval values > 1 in logging.TimedRotatingFileHandler for when='MIDNIGHT' and when='Wx'. (cherry picked from commit 269051d) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 32c7751 commit e287d64

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
@@ -334,7 +334,10 @@ def computeRollover(self, currentTime):
334334
daysToWait = self.dayOfWeek - day
335335
else:
336336
daysToWait = 6 - day + self.dayOfWeek + 1
337-
result += daysToWait * (60 * 60 * 24)
337+
result += daysToWait * _MIDNIGHT
338+
result += self.interval - _MIDNIGHT * 7
339+
else:
340+
result += self.interval - _MIDNIGHT
338341
if not self.utc:
339342
dstNow = t[-1]
340343
dstAtRollover = time.localtime(result)[-1]

Lib/test/test_logging.py

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

65196519
fh.close()
65206520

6521+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6522+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6523+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6524+
def test_compute_rollover_MIDNIGHT_local_interval(self):
6525+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6526+
DT = datetime.datetime
6527+
def test(current, expected):
6528+
actual = fh.computeRollover(current.timestamp())
6529+
diff = actual - expected.timestamp()
6530+
if diff:
6531+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6532+
6533+
fh = logging.handlers.TimedRotatingFileHandler(
6534+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3)
6535+
6536+
test(DT(2012, 3, 8, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6537+
test(DT(2012, 3, 9, 0, 0), DT(2012, 3, 12, 0, 0))
6538+
test(DT(2012, 3, 9, 1, 0), DT(2012, 3, 12, 0, 0))
6539+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 13, 0, 0))
6540+
test(DT(2012, 3, 11, 0, 0), DT(2012, 3, 14, 0, 0))
6541+
test(DT(2012, 3, 11, 1, 0), DT(2012, 3, 14, 0, 0))
6542+
6543+
test(DT(2012, 11, 1, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6544+
test(DT(2012, 11, 2, 0, 0), DT(2012, 11, 5, 0, 0))
6545+
test(DT(2012, 11, 2, 1, 0), DT(2012, 11, 5, 0, 0))
6546+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 6, 0, 0))
6547+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 7, 0, 0))
6548+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 7, 0, 0))
6549+
6550+
fh.close()
6551+
6552+
fh = logging.handlers.TimedRotatingFileHandler(
6553+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3,
6554+
atTime=datetime.time(12, 0, 0))
6555+
6556+
test(DT(2012, 3, 8, 11, 59, 59), DT(2012, 3, 10, 12, 0))
6557+
test(DT(2012, 3, 8, 12, 0), DT(2012, 3, 11, 12, 0))
6558+
test(DT(2012, 3, 8, 13, 0), DT(2012, 3, 11, 12, 0))
6559+
test(DT(2012, 3, 10, 11, 59, 59), DT(2012, 3, 12, 12, 0))
6560+
test(DT(2012, 3, 10, 12, 0), DT(2012, 3, 13, 12, 0))
6561+
test(DT(2012, 3, 10, 13, 0), DT(2012, 3, 13, 12, 0))
6562+
6563+
test(DT(2012, 11, 1, 11, 59, 59), DT(2012, 11, 3, 12, 0))
6564+
test(DT(2012, 11, 1, 12, 0), DT(2012, 11, 4, 12, 0))
6565+
test(DT(2012, 11, 1, 13, 0), DT(2012, 11, 4, 12, 0))
6566+
test(DT(2012, 11, 3, 11, 59, 59), DT(2012, 11, 5, 12, 0))
6567+
test(DT(2012, 11, 3, 12, 0), DT(2012, 11, 6, 12, 0))
6568+
test(DT(2012, 11, 3, 13, 0), DT(2012, 11, 6, 12, 0))
6569+
6570+
fh.close()
6571+
6572+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6573+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6574+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6575+
def test_compute_rollover_W6_local_interval(self):
6576+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6577+
DT = datetime.datetime
6578+
def test(current, expected):
6579+
actual = fh.computeRollover(current.timestamp())
6580+
diff = actual - expected.timestamp()
6581+
if diff:
6582+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6583+
6584+
fh = logging.handlers.TimedRotatingFileHandler(
6585+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3)
6586+
6587+
test(DT(2012, 2, 19, 23, 59, 59), DT(2012, 3, 5, 0, 0))
6588+
test(DT(2012, 2, 20, 0, 0), DT(2012, 3, 12, 0, 0))
6589+
test(DT(2012, 2, 20, 1, 0), DT(2012, 3, 12, 0, 0))
6590+
test(DT(2012, 3, 4, 23, 59, 59), DT(2012, 3, 19, 0, 0))
6591+
test(DT(2012, 3, 5, 0, 0), DT(2012, 3, 26, 0, 0))
6592+
test(DT(2012, 3, 5, 1, 0), DT(2012, 3, 26, 0, 0))
6593+
6594+
test(DT(2012, 10, 14, 23, 59, 59), DT(2012, 10, 29, 0, 0))
6595+
test(DT(2012, 10, 15, 0, 0), DT(2012, 11, 5, 0, 0))
6596+
test(DT(2012, 10, 15, 1, 0), DT(2012, 11, 5, 0, 0))
6597+
test(DT(2012, 10, 28, 23, 59, 59), DT(2012, 11, 12, 0, 0))
6598+
test(DT(2012, 10, 29, 0, 0), DT(2012, 11, 19, 0, 0))
6599+
test(DT(2012, 10, 29, 1, 0), DT(2012, 11, 19, 0, 0))
6600+
6601+
fh.close()
6602+
6603+
fh = logging.handlers.TimedRotatingFileHandler(
6604+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6605+
atTime=datetime.time(0, 0, 0))
6606+
6607+
test(DT(2012, 2, 25, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6608+
test(DT(2012, 2, 26, 0, 0), DT(2012, 3, 18, 0, 0))
6609+
test(DT(2012, 2, 26, 1, 0), DT(2012, 3, 18, 0, 0))
6610+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 25, 0, 0))
6611+
test(DT(2012, 3, 11, 0, 0), DT(2012, 4, 1, 0, 0))
6612+
test(DT(2012, 3, 11, 1, 0), DT(2012, 4, 1, 0, 0))
6613+
6614+
test(DT(2012, 10, 20, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6615+
test(DT(2012, 10, 21, 0, 0), DT(2012, 11, 11, 0, 0))
6616+
test(DT(2012, 10, 21, 1, 0), DT(2012, 11, 11, 0, 0))
6617+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 18, 0, 0))
6618+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 25, 0, 0))
6619+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 25, 0, 0))
6620+
6621+
fh.close()
6622+
6623+
fh = logging.handlers.TimedRotatingFileHandler(
6624+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6625+
atTime=datetime.time(12, 0, 0))
6626+
6627+
test(DT(2012, 2, 18, 11, 59, 59), DT(2012, 3, 4, 12, 0))
6628+
test(DT(2012, 2, 19, 12, 0), DT(2012, 3, 11, 12, 0))
6629+
test(DT(2012, 2, 19, 13, 0), DT(2012, 3, 11, 12, 0))
6630+
test(DT(2012, 3, 4, 11, 59, 59), DT(2012, 3, 18, 12, 0))
6631+
test(DT(2012, 3, 4, 12, 0), DT(2012, 3, 25, 12, 0))
6632+
test(DT(2012, 3, 4, 13, 0), DT(2012, 3, 25, 12, 0))
6633+
6634+
test(DT(2012, 10, 14, 11, 59, 59), DT(2012, 10, 28, 12, 0))
6635+
test(DT(2012, 10, 14, 12, 0), DT(2012, 11, 4, 12, 0))
6636+
test(DT(2012, 10, 14, 13, 0), DT(2012, 11, 4, 12, 0))
6637+
test(DT(2012, 10, 28, 11, 59, 59), DT(2012, 11, 11, 12, 0))
6638+
test(DT(2012, 10, 28, 12, 0), DT(2012, 11, 18, 12, 0))
6639+
test(DT(2012, 10, 28, 13, 0), DT(2012, 11, 18, 12, 0))
6640+
6641+
fh.close()
6642+
6643+
65216644
def secs(**kw):
65226645
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
65236646

@@ -6529,40 +6652,49 @@ def secs(**kw):
65296652
# current time (epoch start) is a Thursday, W0 means Monday
65306653
('W0', secs(days=4, hours=24)),
65316654
):
6532-
def test_compute_rollover(self, when=when, exp=exp):
6533-
rh = logging.handlers.TimedRotatingFileHandler(
6534-
self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
6535-
currentTime = 0.0
6536-
actual = rh.computeRollover(currentTime)
6537-
if exp != actual:
6538-
# Failures occur on some systems for MIDNIGHT and W0.
6539-
# Print detailed calculation for MIDNIGHT so we can try to see
6540-
# what's going on
6541-
if when == 'MIDNIGHT':
6542-
try:
6543-
if rh.utc:
6544-
t = time.gmtime(currentTime)
6545-
else:
6546-
t = time.localtime(currentTime)
6547-
currentHour = t[3]
6548-
currentMinute = t[4]
6549-
currentSecond = t[5]
6550-
# r is the number of seconds left between now and midnight
6551-
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6552-
currentMinute) * 60 +
6553-
currentSecond)
6554-
result = currentTime + r
6555-
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6556-
print('currentHour: %s' % currentHour, file=sys.stderr)
6557-
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6558-
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6559-
print('r: %s' % r, file=sys.stderr)
6560-
print('result: %s' % result, file=sys.stderr)
6561-
except Exception as e:
6562-
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6563-
self.assertEqual(exp, actual)
6564-
rh.close()
6565-
setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
6655+
for interval in 1, 3:
6656+
def test_compute_rollover(self, when=when, interval=interval, exp=exp):
6657+
rh = logging.handlers.TimedRotatingFileHandler(
6658+
self.fn, encoding="utf-8", when=when, interval=interval, backupCount=0, utc=True)
6659+
currentTime = 0.0
6660+
actual = rh.computeRollover(currentTime)
6661+
if when.startswith('W'):
6662+
exp += secs(days=7*(interval-1))
6663+
else:
6664+
exp *= interval
6665+
if exp != actual:
6666+
# Failures occur on some systems for MIDNIGHT and W0.
6667+
# Print detailed calculation for MIDNIGHT so we can try to see
6668+
# what's going on
6669+
if when == 'MIDNIGHT':
6670+
try:
6671+
if rh.utc:
6672+
t = time.gmtime(currentTime)
6673+
else:
6674+
t = time.localtime(currentTime)
6675+
currentHour = t[3]
6676+
currentMinute = t[4]
6677+
currentSecond = t[5]
6678+
# r is the number of seconds left between now and midnight
6679+
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6680+
currentMinute) * 60 +
6681+
currentSecond)
6682+
result = currentTime + r
6683+
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6684+
print('currentHour: %s' % currentHour, file=sys.stderr)
6685+
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6686+
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6687+
print('r: %s' % r, file=sys.stderr)
6688+
print('result: %s' % result, file=sys.stderr)
6689+
except Exception as e:
6690+
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6691+
self.assertEqual(exp, actual)
6692+
rh.close()
6693+
name = "test_compute_rollover_%s" % when
6694+
if interval > 1:
6695+
name += "_interval"
6696+
test_compute_rollover.__name__ = name
6697+
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)
65666698

65676699

65686700
@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)