Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-114271-remove-tstate_lock
Browse files Browse the repository at this point in the history
  • Loading branch information
pitrou authored Mar 16, 2024
2 parents 9a8ea9b + 86bc40d commit 339b2e6
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 48 deletions.
5 changes: 4 additions & 1 deletion Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,10 @@ def computeRollover(self, currentTime):
daysToWait = self.dayOfWeek - day
else:
daysToWait = 6 - day + self.dayOfWeek + 1
result += daysToWait * (60 * 60 * 24)
result += daysToWait * _MIDNIGHT
result += self.interval - _MIDNIGHT * 7
else:
result += self.interval - _MIDNIGHT
if not self.utc:
dstNow = t[-1]
dstAtRollover = time.localtime(result)[-1]
Expand Down
1 change: 1 addition & 0 deletions Lib/test/libregrtest/tsan.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'test_syslog',
'test_thread',
'test_threadedtempfile',
'test_threading',
'test_threading_local',
'test_threadsignals',
]
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/support/script_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class _PythonRunResult(collections.namedtuple("_PythonRunResult",
"""Helper for reporting Python subprocess run results"""
def fail(self, cmd_line):
"""Provide helpful details about failed subcommand runs"""
# Limit to 80 lines to ASCII characters
maxlen = 80 * 100
# Limit to 300 lines of ASCII characters
maxlen = 300 * 100
out, err = self.out, self.err
if len(out) > maxlen:
out = b'(... truncated stdout ...)' + out[-maxlen:]
Expand Down
200 changes: 166 additions & 34 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6577,6 +6577,129 @@ def test(current, expected):

fh.close()

# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
def test_compute_rollover_MIDNIGHT_local_interval(self):
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
DT = datetime.datetime
def test(current, expected):
actual = fh.computeRollover(current.timestamp())
diff = actual - expected.timestamp()
if diff:
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))

fh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3)

test(DT(2012, 3, 8, 23, 59, 59), DT(2012, 3, 11, 0, 0))
test(DT(2012, 3, 9, 0, 0), DT(2012, 3, 12, 0, 0))
test(DT(2012, 3, 9, 1, 0), DT(2012, 3, 12, 0, 0))
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 13, 0, 0))
test(DT(2012, 3, 11, 0, 0), DT(2012, 3, 14, 0, 0))
test(DT(2012, 3, 11, 1, 0), DT(2012, 3, 14, 0, 0))

test(DT(2012, 11, 1, 23, 59, 59), DT(2012, 11, 4, 0, 0))
test(DT(2012, 11, 2, 0, 0), DT(2012, 11, 5, 0, 0))
test(DT(2012, 11, 2, 1, 0), DT(2012, 11, 5, 0, 0))
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 6, 0, 0))
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 7, 0, 0))
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 7, 0, 0))

fh.close()

fh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3,
atTime=datetime.time(12, 0, 0))

test(DT(2012, 3, 8, 11, 59, 59), DT(2012, 3, 10, 12, 0))
test(DT(2012, 3, 8, 12, 0), DT(2012, 3, 11, 12, 0))
test(DT(2012, 3, 8, 13, 0), DT(2012, 3, 11, 12, 0))
test(DT(2012, 3, 10, 11, 59, 59), DT(2012, 3, 12, 12, 0))
test(DT(2012, 3, 10, 12, 0), DT(2012, 3, 13, 12, 0))
test(DT(2012, 3, 10, 13, 0), DT(2012, 3, 13, 12, 0))

test(DT(2012, 11, 1, 11, 59, 59), DT(2012, 11, 3, 12, 0))
test(DT(2012, 11, 1, 12, 0), DT(2012, 11, 4, 12, 0))
test(DT(2012, 11, 1, 13, 0), DT(2012, 11, 4, 12, 0))
test(DT(2012, 11, 3, 11, 59, 59), DT(2012, 11, 5, 12, 0))
test(DT(2012, 11, 3, 12, 0), DT(2012, 11, 6, 12, 0))
test(DT(2012, 11, 3, 13, 0), DT(2012, 11, 6, 12, 0))

fh.close()

# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
def test_compute_rollover_W6_local_interval(self):
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
DT = datetime.datetime
def test(current, expected):
actual = fh.computeRollover(current.timestamp())
diff = actual - expected.timestamp()
if diff:
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))

fh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when='W6', utc=False, interval=3)

test(DT(2012, 2, 19, 23, 59, 59), DT(2012, 3, 5, 0, 0))
test(DT(2012, 2, 20, 0, 0), DT(2012, 3, 12, 0, 0))
test(DT(2012, 2, 20, 1, 0), DT(2012, 3, 12, 0, 0))
test(DT(2012, 3, 4, 23, 59, 59), DT(2012, 3, 19, 0, 0))
test(DT(2012, 3, 5, 0, 0), DT(2012, 3, 26, 0, 0))
test(DT(2012, 3, 5, 1, 0), DT(2012, 3, 26, 0, 0))

test(DT(2012, 10, 14, 23, 59, 59), DT(2012, 10, 29, 0, 0))
test(DT(2012, 10, 15, 0, 0), DT(2012, 11, 5, 0, 0))
test(DT(2012, 10, 15, 1, 0), DT(2012, 11, 5, 0, 0))
test(DT(2012, 10, 28, 23, 59, 59), DT(2012, 11, 12, 0, 0))
test(DT(2012, 10, 29, 0, 0), DT(2012, 11, 19, 0, 0))
test(DT(2012, 10, 29, 1, 0), DT(2012, 11, 19, 0, 0))

fh.close()

fh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
atTime=datetime.time(0, 0, 0))

test(DT(2012, 2, 25, 23, 59, 59), DT(2012, 3, 11, 0, 0))
test(DT(2012, 2, 26, 0, 0), DT(2012, 3, 18, 0, 0))
test(DT(2012, 2, 26, 1, 0), DT(2012, 3, 18, 0, 0))
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 25, 0, 0))
test(DT(2012, 3, 11, 0, 0), DT(2012, 4, 1, 0, 0))
test(DT(2012, 3, 11, 1, 0), DT(2012, 4, 1, 0, 0))

test(DT(2012, 10, 20, 23, 59, 59), DT(2012, 11, 4, 0, 0))
test(DT(2012, 10, 21, 0, 0), DT(2012, 11, 11, 0, 0))
test(DT(2012, 10, 21, 1, 0), DT(2012, 11, 11, 0, 0))
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 18, 0, 0))
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 25, 0, 0))
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 25, 0, 0))

fh.close()

fh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
atTime=datetime.time(12, 0, 0))

test(DT(2012, 2, 18, 11, 59, 59), DT(2012, 3, 4, 12, 0))
test(DT(2012, 2, 19, 12, 0), DT(2012, 3, 11, 12, 0))
test(DT(2012, 2, 19, 13, 0), DT(2012, 3, 11, 12, 0))
test(DT(2012, 3, 4, 11, 59, 59), DT(2012, 3, 18, 12, 0))
test(DT(2012, 3, 4, 12, 0), DT(2012, 3, 25, 12, 0))
test(DT(2012, 3, 4, 13, 0), DT(2012, 3, 25, 12, 0))

test(DT(2012, 10, 14, 11, 59, 59), DT(2012, 10, 28, 12, 0))
test(DT(2012, 10, 14, 12, 0), DT(2012, 11, 4, 12, 0))
test(DT(2012, 10, 14, 13, 0), DT(2012, 11, 4, 12, 0))
test(DT(2012, 10, 28, 11, 59, 59), DT(2012, 11, 11, 12, 0))
test(DT(2012, 10, 28, 12, 0), DT(2012, 11, 18, 12, 0))
test(DT(2012, 10, 28, 13, 0), DT(2012, 11, 18, 12, 0))

fh.close()


def secs(**kw):
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)

Expand All @@ -6588,40 +6711,49 @@ def secs(**kw):
# current time (epoch start) is a Thursday, W0 means Monday
('W0', secs(days=4, hours=24)),
):
def test_compute_rollover(self, when=when, exp=exp):
rh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
currentTime = 0.0
actual = rh.computeRollover(currentTime)
if exp != actual:
# Failures occur on some systems for MIDNIGHT and W0.
# Print detailed calculation for MIDNIGHT so we can try to see
# what's going on
if when == 'MIDNIGHT':
try:
if rh.utc:
t = time.gmtime(currentTime)
else:
t = time.localtime(currentTime)
currentHour = t[3]
currentMinute = t[4]
currentSecond = t[5]
# r is the number of seconds left between now and midnight
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
currentMinute) * 60 +
currentSecond)
result = currentTime + r
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
print('currentHour: %s' % currentHour, file=sys.stderr)
print('currentMinute: %s' % currentMinute, file=sys.stderr)
print('currentSecond: %s' % currentSecond, file=sys.stderr)
print('r: %s' % r, file=sys.stderr)
print('result: %s' % result, file=sys.stderr)
except Exception as e:
print('exception in diagnostic code: %s' % e, file=sys.stderr)
self.assertEqual(exp, actual)
rh.close()
setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
for interval in 1, 3:
def test_compute_rollover(self, when=when, interval=interval, exp=exp):
rh = logging.handlers.TimedRotatingFileHandler(
self.fn, encoding="utf-8", when=when, interval=interval, backupCount=0, utc=True)
currentTime = 0.0
actual = rh.computeRollover(currentTime)
if when.startswith('W'):
exp += secs(days=7*(interval-1))
else:
exp *= interval
if exp != actual:
# Failures occur on some systems for MIDNIGHT and W0.
# Print detailed calculation for MIDNIGHT so we can try to see
# what's going on
if when == 'MIDNIGHT':
try:
if rh.utc:
t = time.gmtime(currentTime)
else:
t = time.localtime(currentTime)
currentHour = t[3]
currentMinute = t[4]
currentSecond = t[5]
# r is the number of seconds left between now and midnight
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
currentMinute) * 60 +
currentSecond)
result = currentTime + r
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
print('currentHour: %s' % currentHour, file=sys.stderr)
print('currentMinute: %s' % currentMinute, file=sys.stderr)
print('currentSecond: %s' % currentSecond, file=sys.stderr)
print('r: %s' % r, file=sys.stderr)
print('result: %s' % result, file=sys.stderr)
except Exception as e:
print('exception in diagnostic code: %s' % e, file=sys.stderr)
self.assertEqual(exp, actual)
rh.close()
name = "test_compute_rollover_%s" % when
if interval > 1:
name += "_interval"
test_compute_rollover.__name__ = name
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)


@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Expand Down
24 changes: 16 additions & 8 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,11 @@ def skip_unless_reliable_fork(test):
return unittest.skip("due to known OS bug related to thread+fork")(test)
if support.HAVE_ASAN_FORK_BUG:
return unittest.skip("libasan has a pthread_create() dead lock related to thread+fork")(test)
if support.check_sanitizer(thread=True):
return unittest.skip("TSAN doesn't support threads after fork")
return test


skip_if_tsan_fork = unittest.skipIf(
support.check_sanitizer(thread=True),
"TSAN doesn't support threads after fork")


def requires_subinterpreters(meth):
"""Decorator to skip a test if subinterpreters are not supported."""
return unittest.skipIf(interpreters is None,
Expand Down Expand Up @@ -428,6 +425,10 @@ def test_finalize_running_thread(self):
# Issue 1402: the PyGILState_Ensure / _Release functions may be called
# very late on python exit: on deallocation of a running thread for
# example.
if support.check_sanitizer(thread=True):
# the thread running `time.sleep(100)` below will still be alive
# at process exit
self.skipTest("TSAN would report thread leak")
import_module("ctypes")

rc, out, err = assert_python_failure("-c", """if 1:
Expand Down Expand Up @@ -460,6 +461,11 @@ def waitingThread():
def test_finalize_with_trace(self):
# Issue1733757
# Avoid a deadlock when sys.settrace steps into threading._shutdown
if support.check_sanitizer(thread=True):
# the thread running `time.sleep(2)` below will still be alive
# at process exit
self.skipTest("TSAN would report thread leak")

assert_python_ok("-c", """if 1:
import sys, threading
Expand Down Expand Up @@ -639,7 +645,6 @@ def test_daemon_param(self):
self.assertTrue(t.daemon)

@skip_unless_reliable_fork
@skip_if_tsan_fork
def test_dummy_thread_after_fork(self):
# Issue #14308: a dummy thread in the active list doesn't mess up
# the after-fork mechanism.
Expand Down Expand Up @@ -709,7 +714,6 @@ def f():

@skip_unless_reliable_fork
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
@skip_if_tsan_fork
def test_main_thread_after_fork(self):
code = """if 1:
import os, threading
Expand Down Expand Up @@ -1219,7 +1223,6 @@ def test_2_join_in_forked_process(self):
self._run_and_join(script)

@skip_unless_reliable_fork
@skip_if_tsan_fork
def test_3_join_in_forked_from_thread(self):
# Like the test above, but fork() was called from a worker thread
# In the forked process, the main Thread object must be marked as stopped.
Expand Down Expand Up @@ -1252,6 +1255,11 @@ def test_4_daemon_threads(self):
# Check that a daemon thread cannot crash the interpreter on shutdown
# by manipulating internal structures that are being disposed of in
# the main thread.
if support.check_sanitizer(thread=True):
# some of the threads running `random_io` below will still be alive
# at process exit
self.skipTest("TSAN would report thread leak")

script = """if True:
import os
import random
Expand Down
22 changes: 21 additions & 1 deletion Lib/test/test_ttk/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,29 @@ def test_unique_variables(self):
b.pack()
buttons.append(b)
variables = [str(b['variable']) for b in buttons]
print(variables)
self.assertEqual(len(set(variables)), 4, variables)

def test_unique_variables2(self):
buttons = []
f = ttk.Frame(self.root)
f.pack()
f = ttk.Frame(self.root)
f.pack()
for j in 'AB':
b = tkinter.Checkbutton(f, text=j)
b.pack()
buttons.append(b)
# Should be larger than the number of all previously created
# tkinter.Checkbutton widgets:
for j in range(100):
b = ttk.Checkbutton(f, text=str(j))
b.pack()
buttons.append(b)
names = [str(b) for b in buttons]
self.assertEqual(len(set(names)), len(buttons), names)
variables = [str(b['variable']) for b in buttons]
self.assertEqual(len(set(variables)), len(buttons), variables)


@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
class EntryTest(AbstractWidgetTest, unittest.TestCase):
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_urlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,30 @@ def test_parse_qsl_separator(self):
result_bytes = urllib.parse.parse_qsl(orig, separator=b';')
self.assertEqual(result_bytes, expect, "Error parsing %r" % orig)

def test_parse_qsl_bytes(self):
self.assertEqual(urllib.parse.parse_qsl(b'a=b'), [(b'a', b'b')])
self.assertEqual(urllib.parse.parse_qsl(bytearray(b'a=b')), [(b'a', b'b')])
self.assertEqual(urllib.parse.parse_qsl(memoryview(b'a=b')), [(b'a', b'b')])

def test_parse_qsl_false_value(self):
kwargs = dict(keep_blank_values=True, strict_parsing=True)
for x in '', b'', None, 0, 0.0, [], {}, memoryview(b''):
self.assertEqual(urllib.parse.parse_qsl(x, **kwargs), [])
self.assertRaises(ValueError, urllib.parse.parse_qsl, x, separator=1)

def test_parse_qsl_errors(self):
self.assertRaises(TypeError, urllib.parse.parse_qsl, list(b'a=b'))
self.assertRaises(TypeError, urllib.parse.parse_qsl, iter(b'a=b'))
self.assertRaises(TypeError, urllib.parse.parse_qsl, 1)
self.assertRaises(TypeError, urllib.parse.parse_qsl, object())

for separator in '', b'', None, 0, 1, 0.0, 1.5:
with self.assertRaises(ValueError):
urllib.parse.parse_qsl('a=b', separator=separator)
with self.assertRaises(UnicodeEncodeError):
urllib.parse.parse_qsl(b'a=b', separator='\xa6')
with self.assertRaises(UnicodeDecodeError):
urllib.parse.parse_qsl('a=b', separator=b'\xa6')

def test_urlencode_sequences(self):
# Other tests incidentally urlencode things; test non-covered cases:
Expand Down
Loading

0 comments on commit 339b2e6

Please sign in to comment.