summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_sys.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_sys.py')
-rw-r--r--Lib/test/test_sys.py140
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