diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index fc3bd7dca..cd33878d6 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1835,20 +1835,6 @@ class AsyncGenAsyncioTest(unittest.TestCase): res = self.loop.run_until_complete(run()) self.assertEqual(res, [i * 2 for i in range(1, 10)]) - def test_async_gen_expression_incorrect(self): - async def ag(): - yield 42 - - async def run(arg): - (x async for x in arg) - - err_msg_async = "'async for' requires an object with " \ - "__aiter__ method, got .*" - - self.loop.run_until_complete(run(ag())) - with self.assertRaisesRegex(TypeError, err_msg_async): - self.loop.run_until_complete(run(None)) - def test_asyncgen_nonstarted_hooks_are_cancellable(self): # See https://bugs.python.org/issue38013 messages = [] @@ -2035,6 +2021,15 @@ class TestUnawaitedWarnings(unittest.TestCase): g.athrow(RuntimeError) gc_collect() + def test_athrow_throws_immediately(self): + async def gen(): + yield 1 + + g = gen() + msg = "athrow expected at least 1 argument, got 0" + with self.assertRaisesRegex(TypeError, msg): + g.athrow() + def test_aclose(self): async def gen(): yield 1 diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py b/Lib/test/test_asyncio/test_sock_lowlevel.py index a34e15b4d..eb9f2a531 100644 --- a/Lib/test/test_asyncio/test_sock_lowlevel.py +++ b/Lib/test/test_asyncio/test_sock_lowlevel.py @@ -1,5 +1,6 @@ import socket import asyncio +import select import sys import unittest @@ -257,6 +258,38 @@ class BaseSockTestsMixin: self.skipTest(skip_reason) + async def _basetest_sock_accept_racing(self, listener, sock): + # gh-153761: cancelling sock_accept() must not let a scheduled + # _sock_accept run on the cancelled future. + listener.setblocking(False) + listener.bind(('127.0.0.1', 0)) + listener.listen(1) + addr = listener.getsockname() + + errors = [] + self.loop.set_exception_handler(lambda loop, ctx: errors.append(ctx)) + task = asyncio.create_task(self.loop.sock_accept(listener)) + await asyncio.sleep(0) + + sock.connect(addr) + select.select([listener], [], [], support.SHORT_TIMEOUT) + + self.loop.call_soon(task.cancel) + await asyncio.sleep(0) + await asyncio.sleep(0) + + with self.assertRaises(asyncio.CancelledError): + await task + self.assertEqual(errors, []) + + # The pending connection must survive + conn, conn_addr = await self.loop.sock_accept(listener) + with conn: + self.assertEqual(conn_addr, sock.getsockname()) + sock.setblocking(False) + await self.loop.sock_sendall(conn, b'ping') + self.assertEqual(await self.loop.sock_recv(sock, 4), b'ping') + def test_sock_client_racing(self): with test_utils.run_test_server() as httpd: sock = socket.socket() @@ -280,6 +313,16 @@ class BaseSockTestsMixin: self.loop.run_until_complete(asyncio.wait_for( self._basetest_sock_connect_racing(listener, sock), 10)) + def test_sock_accept_racing(self): + if sys.platform == 'win32': + if isinstance(self.loop, asyncio.ProactorEventLoop): + raise unittest.SkipTest('Not relevant to ProactorEventLoop') + listener = socket.socket() + sock = socket.socket() + with listener, sock: + self.loop.run_until_complete(asyncio.wait_for( + self._basetest_sock_accept_racing(listener, sock), 10)) + async def _basetest_huge_content(self, address): sock = socket.socket() sock.setblocking(False) diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index 389a3fa0e..a986fd6b8 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -516,6 +516,7 @@ class CMathTests(ComplexesAreIdenticalMixin, unittest.TestCase): self.assertFalse(cmath.isinf(1j)) self.assertFalse(cmath.isinf(NAN)) self.assertTrue(cmath.isinf(INF)) + self.assertTrue(cmath.isinf(-INF)) self.assertTrue(cmath.isinf(complex(INF, 0))) self.assertTrue(cmath.isinf(complex(0, INF))) self.assertTrue(cmath.isinf(complex(INF, INF))) diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py index ef20495dc..f193a3335 100644 --- a/Lib/test/test_context.py +++ b/Lib/test/test_context.py @@ -1282,6 +1282,31 @@ class HamtTest(unittest.TestCase): self.assertIsNone(ref()) + def test_hamt_gc_3(self): + # gh-154535: the iterators must be tracked by the GC, otherwise a + # cycle running through one is never collected and the HAMT it + # holds -- and everything in it -- leaks. + A = HashKey(100, 'A') + + container = [] + h = hamt() + h = h.set(A, container) + + hi = h.items() + self.assertTrue(gc.is_tracked(hi)) + + # Close the cycle: hi -> h -> container -> hi. + container.append(hi) + ref = weakref.ref(h) + + del h, hi, container + + gc.collect() + gc.collect() + gc.collect() + + self.assertIsNone(ref()) + def test_hamt_in_1(self): A = HashKey(100, 'A') AA = HashKey(100, 'A') diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py index 85e0accb6..5951f8817 100644 --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -2,6 +2,7 @@ # Initial tests are copied as is from "test_poll.py" +import errno import os import random import select @@ -112,6 +113,15 @@ class DevPollTests(unittest.TestCase): self.assertRaises(ValueError, devpoll.register, fd, select.POLLIN) self.assertRaises(ValueError, devpoll.unregister, fd) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + devpoll = select.devpoll() + fd = devpoll.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + devpoll.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): devpoll = select.devpoll() self.addCleanup(devpoll.close) diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index c94946a6a..51405ca7c 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -25,6 +25,7 @@ import errno import os import select import socket +import sys import time import unittest from test import support @@ -52,12 +53,11 @@ class TestEPoll(unittest.TestCase): def _connected_pair(self): client = socket.socket() client.setblocking(False) + # The connection is either established immediately or in progress. try: client.connect(('127.0.0.1', self.serverSocket.getsockname()[1])) except OSError as e: self.assertEqual(e.args[0], errno.EINPROGRESS) - else: - raise AssertionError("Connect should have raised EINPROGRESS") server, addr = self.serverSocket.accept() self.connections.extend((client, server)) @@ -218,6 +218,9 @@ class TestEPoll(unittest.TestCase): self.assertRaises(ValueError, select.epoll().register, -1, select.EPOLLIN) + @unittest.skipIf(sys.platform.startswith('sunos'), + 'unregistering a closed file descriptor succeeds ' + 'on Solaris') def test_unregister_closed(self): server, client = self._connected_pair() fd = server.fileno() @@ -259,6 +262,15 @@ class TestEPoll(unittest.TestCase): self.assertRaises(ValueError, epoll.register, fd, select.EPOLLIN) self.assertRaises(ValueError, epoll.unregister, fd) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + epoll = select.epoll() + fd = epoll.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + epoll.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): epoll = select.epoll() self.addCleanup(epoll.close) diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index a6523bbc5..550faa8a1 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -11,6 +11,7 @@ import unittest from test.fork_wait import ForkWait from test import support +from test.support import warnings_helper # Skip test if fork does not exist. @@ -19,6 +20,7 @@ if not support.has_fork_support: class ForkTest(ForkWait): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_threaded_import_lock_fork(self): """Check fork() in main thread works while a subthread is doing an import""" import_started = threading.Event() @@ -61,7 +63,7 @@ class ForkTest(ForkWait): except OSError: pass - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_nested_import_lock_fork(self): """Check fork() in main thread works while the main thread is doing an import""" exitcode = 42 diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index e94edcbc1..2649f3a7a 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -9,6 +9,8 @@ from test import support import time import unittest +from test.support import warnings_helper + if not hasattr(select, "kqueue"): raise unittest.SkipTest("test works only on BSD") @@ -21,6 +23,20 @@ class TestKQueue(unittest.TestCase): self.assertTrue(kq.closed) self.assertRaises(ValueError, kq.fileno) + def test_control_overflowing_timeout(self): + # gh-154836: out-of-range timeouts must raise OverflowError, + # not a (misleading) TypeError, like select(), poll() and + # epoll() do. + kq = select.kqueue() + self.addCleanup(kq.close) + for timeout in (1e300, float('inf'), 2**200): + with self.subTest(timeout=timeout): + with self.assertRaises(OverflowError): + kq.control(None, 0, timeout) + # Non-numbers still raise TypeError. + with self.assertRaises(TypeError): + kq.control(None, 0, "0.1") + def test_create_event(self): from operator import lt, le, gt, ge @@ -252,11 +268,21 @@ class TestKQueue(unittest.TestCase): # operations must fail with ValueError("I/O operation on closed ...") self.assertRaises(ValueError, kqueue.control, None, 4) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + kqueue = select.kqueue() + fd = kqueue.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + kqueue.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): kqueue = select.kqueue() self.addCleanup(kqueue.close) self.assertEqual(os.get_inheritable(kqueue.fileno()), False) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def test_fork(self): # gh-110395: kqueue objects must be closed after fork diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py index 5675db8d1..a15612b91 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -173,17 +173,25 @@ class PollTests(unittest.TestCase): @cpython_only def test_poll_c_limits(self): try: + import _testcapi from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX + HAVE_PPOLL = getattr(_testcapi, 'HAVE_PPOLL', False) except ImportError: raise unittest.SkipTest("requires _testcapi") + pollster = select.poll() pollster.register(1) # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) - self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) + if HAVE_PPOLL: + MS_TO_NS = 1_000_000 + tmax = _testcapi.INT64_MAX // MS_TO_NS + self.assertRaises(OverflowError, pollster.poll, tmax + 1) + else: + self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) + self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) @threading_helper.reap_threads def test_threaded_poll(self): diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index b5407642f..7e4f4828c 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,6 +1,6 @@ import unittest from test.support import ( - is_android, is_apple_mobile, is_wasm32, reap_children, verbose + is_android, is_apple_mobile, is_wasm32, reap_children, verbose, warnings_helper ) from test.support.import_helper import import_module @@ -19,7 +19,6 @@ import select import signal import socket import io # readline -import warnings TEST_STRING_1 = b"I wish to buy a fish license.\n" TEST_STRING_2 = b"For my pet fish, Eric.\n" @@ -194,6 +193,7 @@ class PtyTest(unittest.TestCase): s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() @@ -229,6 +229,7 @@ class PtyTest(unittest.TestCase): os._exit(2) os._exit(4) else: + self.assertFalse(os.get_inheritable(master_fd)) debug("Waiting for child (%d) to finish." % pid) # In verbose mode, we have to consume the debug output from the # child or the child will block, causing this test to hang in the @@ -295,6 +296,7 @@ class PtyTest(unittest.TestCase): self.assertEqual(data, b"") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_doesnt_hang(self): # gh-140482: Do the test in a pty.fork() child to avoid messing # with the interactive test runner's terminal settings. @@ -314,7 +316,7 @@ class PtyTest(unittest.TestCase): (pid, status) = os.waitpid(pid, 0) self.assertEqual(status, 0) - self.assertEqual(bytes(buf), b"hi there\r\n") + self.assertEqual(buf.take_bytes(), b"hi there\r\n") finally: os.close(fd) diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 22a55b57c..5a95a05c6 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -695,8 +695,11 @@ class LongReprTest(unittest.TestCase): source_path_len += 2 * (len(self.longname) + 1) # a path separator + `module_name` + ".py" source_path_len += len(module_name) + 1 + len(".py") - cached_path_len = (source_path_len + - len(importlib.util.cache_from_source("x.py")) - len("x.py")) + try: + cached_path_len = (source_path_len + + len(importlib.util.cache_from_source("x.py")) - len("x.py")) + except NotImplementedError: + cached_path_len = source_path_len if os.name == 'nt' and cached_path_len >= 258: # Under Windows, the max path len is 260 including C's terminating # NUL character. diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index fb3ea34d7..b8aac8c20 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -1185,9 +1185,9 @@ class SMTPSimTests(unittest.TestCase): self.assertEqual(resp, (235, b'Authentication Succeeded')) smtp.close() - @mock.patch("hmac.HMAC") + @hashlib_helper.block_algorithm('md5') @mock.patch("smtplib._have_cram_md5_support", False) - def testAUTH_CRAM_MD5_blocked(self, hmac_constructor): + def testAUTH_CRAM_MD5_blocked(self): # CRAM-MD5 is the only "known" method by the server, # but it is not supported by the client. In particular, # no challenge will ever be sent. @@ -1198,8 +1198,8 @@ class SMTPSimTests(unittest.TestCase): msg = re.escape("No suitable authentication method found.") with self.assertRaisesRegex(smtplib.SMTPException, msg): smtp.login(sim_auth[0], sim_auth[1]) - hmac_constructor.assert_not_called() # call has been bypassed + @hashlib_helper.block_algorithm('md5') @mock.patch("smtplib._have_cram_md5_support", False) def testAUTH_CRAM_MD5_blocked_and_fallback(self): # Test that PLAIN is tried after CRAM-MD5 failed @@ -1215,7 +1215,7 @@ class SMTPSimTests(unittest.TestCase): ): resp = smtp.login(sim_auth[0], sim_auth[1]) smtp_auth_plain.assert_called_once() - smtp_auth_cram_md5.assert_not_called() # no call to HMAC constructor + smtp_auth_cram_md5.assert_not_called() self.assertEqual(resp, (235, b'Authentication Succeeded')) @hashlib_helper.requires_hashdigest('md5', openssl=True) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 640240935..a5e7bbbfa 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -360,6 +360,8 @@ class TraceTestCase(unittest.TestCase): # Disable gc collection when tracing, otherwise the # deallocators may be traced as well. def setUp(self): + if os.environ.get('PYTHON_UOPS_OPTIMIZE') == '0': + self.skipTest("Line tracing behavior differs when JIT optimizer is disabled") self.using_gc = gc.isenabled() gc.disable() self.addCleanup(sys.settrace, sys.gettrace()) @@ -664,21 +666,18 @@ class TraceTestCase(unittest.TestCase): (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), @@ -1799,9 +1798,6 @@ class SkipLineEventsTraceTestCase(TraceTestCase): def make_tracer(): return Tracer(trace_line_events=False) - def test_12_tighterloop(self): - TraceTestCase.test_12_tighterloop(self) - @support.cpython_only class TraceOpcodesTestCase(TraceTestCase): diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py index ce8392a6c..aa8927c1b 100644 --- a/Lib/test/test_termios.py +++ b/Lib/test/test_termios.py @@ -104,7 +104,7 @@ class TestFunctions(unittest.TestCase): try: termios.tcsendbreak(self.fd, 1) except termios.error as exc: - if exc.args[0] == errno.ENOTTY and sys.platform.startswith(('freebsd', "netbsd")): + if exc.args[0] == errno.ENOTTY and sys.platform.startswith(('freebsd', 'netbsd', 'openbsd', 'dragonfly')): self.skipTest('termios.tcsendbreak() is not supported ' 'with pseudo-terminals (?) on this platform') raise @@ -180,6 +180,9 @@ class TestFunctions(unittest.TestCase): termios.tcflow(self.fd, termios.TCOON) termios.tcflow(self.fd, termios.TCIOFF) termios.tcflow(self.fd, termios.TCION) + # Discard the transmitted STOP and START characters, + # otherwise closing the pseudo-terminal can block. + termios.tcflush(self.fd, termios.TCOFLUSH) @support.skip_android_selinux('tcflow') def test_tcflow_errors(self): diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py index 2a4496769..e50a85353 100644 --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -114,10 +114,10 @@ class UnicodeNamesTest(unittest.TestCase): @unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: got unexpected unicode def test_tangut_ideographs(self): self.checkletter("TANGUT IDEOGRAPH-17000", "\U00017000") - self.checkletter("TANGUT IDEOGRAPH-187F7", "\U000187f7") + self.checkletter("TANGUT IDEOGRAPH-187FF", "\U000187ff") self.checkletter("TANGUT IDEOGRAPH-18D00", "\U00018D00") - self.checkletter("TANGUT IDEOGRAPH-18D08", "\U00018d08") - self.checkletter("tangut ideograph-18d08", "\U00018d08") + self.checkletter("TANGUT IDEOGRAPH-18D1E", "\U00018d1e") + self.checkletter("tangut ideograph-18d1e", "\U00018d1e") def test_egyptian_hieroglyphs(self): self.checkletter("EGYPTIAN HIEROGLYPH-13460", "\U00013460") diff --git a/Lib/test/test_zstd.py b/Lib/test/test_zstd.py index cf618534a..9225c42e0 100644 --- a/Lib/test/test_zstd.py +++ b/Lib/test/test_zstd.py @@ -12,7 +12,6 @@ import threading from test.support.import_helper import import_module from test.support import threading_helper from test.support import _1M -from test.support import Py_GIL_DISABLED _zstd = import_module("_zstd") zstd = import_module("compression.zstd") @@ -2374,6 +2373,8 @@ class FileTestCase(unittest.TestCase): f.read(100) with self.assertRaises(io.UnsupportedOperation): f.seek(100) + with self.assertRaises(io.UnsupportedOperation): + next(iter(f)) self.assertEqual(f.closed, True) with self.assertRaises(ValueError): f.readable() diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 1f386f5f9..e4bf5f966 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -6122,7 +6122,12 @@ impl ExecutingFrame<'_> { Ok(None) } PyIterReturn::StopIteration(value) => { - if vm.use_tracing.get() && self.trace_is_set(vm) { + // CPython's INSTRUMENTED_END_SEND synthesizes this event + // only for generator/coroutine receivers, not other awaitables. + if vm.use_tracing.get() + && self.trace_is_set(vm) + && self.builtin_coro(receiver).is_some() + { let stop_exc = vm.new_stop_iteration(value.clone()); self.fire_exception_trace(&stop_exc, vm)?; } @@ -6190,7 +6195,12 @@ impl ExecutingFrame<'_> { Ok(None) } PyIterReturn::StopIteration(value) => { - if vm.use_tracing.get() && self.trace_is_set(vm) { + // CPython's INSTRUMENTED_END_SEND synthesizes this event + // only for generator/coroutine receivers, not other awaitables. + if vm.use_tracing.get() + && self.trace_is_set(vm) + && self.builtin_coro(receiver).is_some() + { let stop_exc = vm.new_stop_iteration(value.clone()); self.fire_exception_trace(&stop_exc, vm)?; }