diff options
Diffstat (limited to 'Lib/test/test_sys.py')
-rw-r--r-- | Lib/test/test_sys.py | 140 |
1 files changed, 123 insertions, 17 deletions
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b7ddbe9..2d95653 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,5 +1,5 @@ import unittest, test.support -from test.script_helper import assert_python_ok, assert_python_failure +from test.support.script_helper import assert_python_ok, assert_python_failure import sys, io, os import struct import subprocess @@ -201,19 +201,57 @@ class SysModuleTest(unittest.TestCase): if hasattr(sys, 'gettrace') and sys.gettrace(): self.skipTest('fatal error if run with a trace function') - # NOTE: this test is slightly fragile in that it depends on the current - # recursion count when executing the test being low enough so as to - # trigger the recursion recovery detection in the _Py_MakeEndRecCheck - # macro (see ceval.h). oldlimit = sys.getrecursionlimit() def f(): f() try: - for i in (50, 1000): - # Issue #5392: stack overflow after hitting recursion limit twice - sys.setrecursionlimit(i) - self.assertRaises(RuntimeError, f) - self.assertRaises(RuntimeError, f) + for depth in (10, 25, 50, 75, 100, 250, 1000): + try: + sys.setrecursionlimit(depth) + except RecursionError: + # Issue #25274: The recursion limit is too low at the + # current recursion depth + continue + + # Issue #5392: test stack overflow after hitting recursion + # limit twice + self.assertRaises(RecursionError, f) + self.assertRaises(RecursionError, f) + finally: + sys.setrecursionlimit(oldlimit) + + @test.support.cpython_only + def test_setrecursionlimit_recursion_depth(self): + # Issue #25274: Setting a low recursion limit must be blocked if the + # current recursion depth is already higher than the "lower-water + # mark". Otherwise, it may not be possible anymore to + # reset the overflowed flag to 0. + + from _testcapi import get_recursion_depth + + def set_recursion_limit_at_depth(depth, limit): + recursion_depth = get_recursion_depth() + if recursion_depth >= depth: + with self.assertRaises(RecursionError) as cm: + sys.setrecursionlimit(limit) + self.assertRegex(str(cm.exception), + "cannot set the recursion limit to [0-9]+ " + "at the recursion depth [0-9]+: " + "the limit is too low") + else: + set_recursion_limit_at_depth(depth, limit) + + oldlimit = sys.getrecursionlimit() + try: + sys.setrecursionlimit(1000) + + for limit in (10, 25, 50, 75, 100, 150, 200): + # formula extracted from _Py_RecursionLimitLowerWaterMark() + if limit > 200: + depth = limit - 50 + else: + depth = limit * 3 // 4 + set_recursion_limit_at_depth(depth, limit) finally: sys.setrecursionlimit(oldlimit) @@ -226,7 +264,7 @@ class SysModuleTest(unittest.TestCase): def f(): try: f() - except RuntimeError: + except RecursionError: f() sys.setrecursionlimit(%d) @@ -637,6 +675,53 @@ class SysModuleTest(unittest.TestCase): expected = None self.check_fsencoding(fs_encoding, expected) + def c_locale_get_error_handler(self, isolated=False, encoding=None): + # Force the POSIX locale + env = os.environ.copy() + env["LC_ALL"] = "C" + code = '\n'.join(( + 'import sys', + 'def dump(name):', + ' std = getattr(sys, name)', + ' print("%s: %s" % (name, std.errors))', + 'dump("stdin")', + 'dump("stdout")', + 'dump("stderr")', + )) + args = [sys.executable, "-c", code] + if isolated: + args.append("-I") + elif encoding: + env['PYTHONIOENCODING'] = encoding + p = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=env, + universal_newlines=True) + stdout, stderr = p.communicate() + return stdout + + def test_c_locale_surrogateescape(self): + out = self.c_locale_get_error_handler(isolated=True) + self.assertEqual(out, + 'stdin: surrogateescape\n' + 'stdout: surrogateescape\n' + 'stderr: backslashreplace\n') + + # replace the default error handler + out = self.c_locale_get_error_handler(encoding=':strict') + self.assertEqual(out, + 'stdin: strict\n' + 'stdout: strict\n' + 'stderr: backslashreplace\n') + + # force the encoding + out = self.c_locale_get_error_handler(encoding='iso8859-1') + self.assertEqual(out, + 'stdin: surrogateescape\n' + 'stdout: surrogateescape\n' + 'stderr: backslashreplace\n') + def test_implementation(self): # This test applies to all implementations equally. @@ -662,7 +747,7 @@ class SysModuleTest(unittest.TestCase): @test.support.cpython_only def test_debugmallocstats(self): # Test sys._debugmallocstats() - from test.script_helper import assert_python_ok + from test.support.script_helper import assert_python_ok args = ['-c', 'import sys; sys._debugmallocstats()'] ret, out, err = assert_python_ok(*args) self.assertIn(b"free PyDictObjects", err) @@ -699,6 +784,27 @@ class SysModuleTest(unittest.TestCase): c = sys.getallocatedblocks() self.assertIn(c, range(b - 50, b + 50)) + def test_is_finalizing(self): + self.assertIs(sys.is_finalizing(), False) + # Don't use the atexit module because _Py_Finalizing is only set + # after calling atexit callbacks + code = """if 1: + import sys + + class AtExit: + is_finalizing = sys.is_finalizing + print = print + + def __del__(self): + self.print(self.is_finalizing(), flush=True) + + # Keep a reference in the __main__ module namespace, so the + # AtExit destructor will be called at Python exit + ref = AtExit() + """ + rc, stdout, stderr = assert_python_ok('-c', code) + self.assertEqual(stdout.rstrip(), b'True') + @test.support.cpython_only class SizeofTest(unittest.TestCase): @@ -771,7 +877,7 @@ class SizeofTest(unittest.TestCase): # buffer # XXX # builtin_function_or_method - check(len, size('3P')) # XXX check layout + check(len, size('4P')) # XXX check layout # bytearray samples = [b'', b'u'*100000] for sample in samples: @@ -875,7 +981,7 @@ class SizeofTest(unittest.TestCase): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2P')) + check(get_gen(), size('Pb2PPP')) # iterator check(iter('abc'), size('lP')) # callable-iterator @@ -929,7 +1035,7 @@ class SizeofTest(unittest.TestCase): # frozenset PySet_MINSIZE = 8 samples = [[], range(10), range(50)] - s = size('3n2P' + PySet_MINSIZE*'nP' + 'nP') + s = size('3nP' + PySet_MINSIZE*'nP' + '2nP') for sample in samples: minused = len(sample) if minused == 0: tmp = 1 @@ -958,9 +1064,9 @@ class SizeofTest(unittest.TestCase): # static type: PyTypeObject s = vsize('P2n15Pl4Pn9Pn11PIP') check(int, s) - # (PyTypeObject + PyNumberMethods + PyMappingMethods + + # (PyTypeObject + PyAsyncMethods + PyNumberMethods + PyMappingMethods + # PySequenceMethods + PyBufferProcs + 4P) - s = vsize('P2n15Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 10P 2P 4P') + s = vsize('P2n17Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 3P 10P 2P 4P') # Separate block for PyDictKeysObject with 4 entries s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P") # class |