diff options
Diffstat (limited to 'Lib/test/test_sys.py')
| -rw-r--r-- | Lib/test/test_sys.py | 204 |
1 files changed, 173 insertions, 31 deletions
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b7ddbe9..6046671 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): @@ -708,11 +814,6 @@ class SizeofTest(unittest.TestCase): self.longdigit = sys.int_info.sizeof_digit import _testcapi self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD - self.file = open(test.support.TESTFN, 'wb') - - def tearDown(self): - self.file.close() - test.support.unlink(test.support.TESTFN) check_sizeof = test.support.check_sizeof @@ -763,6 +864,7 @@ class SizeofTest(unittest.TestCase): def test_objecttypes(self): # check all types defined in Objects/ + calcsize = struct.calcsize size = test.support.calcobjsize vsize = test.support.calcvobjsize check = self.check_sizeof @@ -771,7 +873,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: @@ -814,17 +916,23 @@ class SizeofTest(unittest.TestCase): # method-wrapper (descriptor object) check({}.__iter__, size('2P')) # dict - check({}, size('n2P' + '2nPn' + 8*'n2P')) + check({}, size('n2P') + calcsize('2nPn') + 8*calcsize('n2P')) longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8} - check(longdict, size('n2P' + '2nPn') + 16*struct.calcsize('n2P')) - # dictionary-keyiterator + check(longdict, size('n2P') + calcsize('2nPn') + 16*calcsize('n2P')) + # dictionary-keyview check({}.keys(), size('P')) - # dictionary-valueiterator + # dictionary-valueview check({}.values(), size('P')) - # dictionary-itemiterator + # dictionary-itemview check({}.items(), size('P')) # dictionary iterator check(iter({}), size('P2nPn')) + # dictionary-keyiterator + check(iter({}.keys()), size('P2nPn')) + # dictionary-valueiterator + check(iter({}.values()), size('P2nPn')) + # dictionary-itemiterator + check(iter({}.items()), size('P2nPn')) # dictproxy class C(object): pass check(C.__dict__, size('P')) @@ -875,7 +983,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 +1037,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 @@ -943,8 +1051,8 @@ class SizeofTest(unittest.TestCase): check(set(sample), s) check(frozenset(sample), s) else: - check(set(sample), s + newsize*struct.calcsize('nP')) - check(frozenset(sample), s + newsize*struct.calcsize('nP')) + check(set(sample), s + newsize*calcsize('nP')) + check(frozenset(sample), s + newsize*calcsize('nP')) # setiterator check(iter(set()), size('P3n')) # slice @@ -958,11 +1066,15 @@ class SizeofTest(unittest.TestCase): # static type: PyTypeObject s = vsize('P2n15Pl4Pn9Pn11PIP') check(int, s) - # (PyTypeObject + PyNumberMethods + PyMappingMethods + - # PySequenceMethods + PyBufferProcs + 4P) - s = vsize('P2n15Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 10P 2P 4P') + s = vsize('P2n15Pl4Pn9Pn11PIP' # PyTypeObject + '3P' # PyAsyncMethods + '36P' # PyNumberMethods + '3P' # PyMappingMethods + '10P' # PySequenceMethods + '2P' # PyBufferProcs + '4P') # Separate block for PyDictKeysObject with 4 entries - s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P") + s += calcsize("2nPn") + 4*calcsize("n2P") # class class newstyleclass(object): pass check(newstyleclass, s) @@ -1006,6 +1118,36 @@ class SizeofTest(unittest.TestCase): # weakcallableproxy check(weakref.proxy(int), size('2Pn2P')) + def check_slots(self, obj, base, extra): + expected = sys.getsizeof(base) + struct.calcsize(extra) + if gc.is_tracked(obj) and not gc.is_tracked(base): + expected += self.gc_headsize + self.assertEqual(sys.getsizeof(obj), expected) + + def test_slots(self): + # check all subclassable types defined in Objects/ that allow + # non-empty __slots__ + check = self.check_slots + class BA(bytearray): + __slots__ = 'a', 'b', 'c' + check(BA(), bytearray(), '3P') + class D(dict): + __slots__ = 'a', 'b', 'c' + check(D(x=[]), {'x': []}, '3P') + class L(list): + __slots__ = 'a', 'b', 'c' + check(L(), [], '3P') + class S(set): + __slots__ = 'a', 'b', 'c' + check(S(), set(), '3P') + class FS(frozenset): + __slots__ = 'a', 'b', 'c' + check(FS(), frozenset(), '3P') + from collections import OrderedDict + class OD(OrderedDict): + __slots__ = 'a', 'b', 'c' + check(OD(x=[]), OrderedDict(x=[]), '3P') + def test_pythontypes(self): # check all types defined in Python/ size = test.support.calcobjsize |
