xref: /third_party/python/Lib/test/test_sys.py (revision 7db96d56)
1import builtins
2import codecs
3import gc
4import locale
5import operator
6import os
7import struct
8import subprocess
9import sys
10import sysconfig
11import test.support
12from test import support
13from test.support import os_helper
14from test.support.script_helper import assert_python_ok, assert_python_failure
15from test.support import threading_helper
16from test.support import import_helper
17import textwrap
18import unittest
19import warnings
20
21
22# count the number of test runs, used to create unique
23# strings to intern in test_intern()
24INTERN_NUMRUNS = 0
25
26DICT_KEY_STRUCT_FORMAT = 'n2BI2n'
27
28class DisplayHookTest(unittest.TestCase):
29
30    def test_original_displayhook(self):
31        dh = sys.__displayhook__
32
33        with support.captured_stdout() as out:
34            dh(42)
35
36        self.assertEqual(out.getvalue(), "42\n")
37        self.assertEqual(builtins._, 42)
38
39        del builtins._
40
41        with support.captured_stdout() as out:
42            dh(None)
43
44        self.assertEqual(out.getvalue(), "")
45        self.assertTrue(not hasattr(builtins, "_"))
46
47        # sys.displayhook() requires arguments
48        self.assertRaises(TypeError, dh)
49
50        stdout = sys.stdout
51        try:
52            del sys.stdout
53            self.assertRaises(RuntimeError, dh, 42)
54        finally:
55            sys.stdout = stdout
56
57    def test_lost_displayhook(self):
58        displayhook = sys.displayhook
59        try:
60            del sys.displayhook
61            code = compile("42", "<string>", "single")
62            self.assertRaises(RuntimeError, eval, code)
63        finally:
64            sys.displayhook = displayhook
65
66    def test_custom_displayhook(self):
67        def baddisplayhook(obj):
68            raise ValueError
69
70        with support.swap_attr(sys, 'displayhook', baddisplayhook):
71            code = compile("42", "<string>", "single")
72            self.assertRaises(ValueError, eval, code)
73
74class ActiveExceptionTests(unittest.TestCase):
75    def test_exc_info_no_exception(self):
76        self.assertEqual(sys.exc_info(), (None, None, None))
77
78    def test_sys_exception_no_exception(self):
79        self.assertEqual(sys.exception(), None)
80
81    def test_exc_info_with_exception_instance(self):
82        def f():
83            raise ValueError(42)
84
85        try:
86            f()
87        except Exception as e_:
88            e = e_
89            exc_info = sys.exc_info()
90
91        self.assertIsInstance(e, ValueError)
92        self.assertIs(exc_info[0], ValueError)
93        self.assertIs(exc_info[1], e)
94        self.assertIs(exc_info[2], e.__traceback__)
95
96    def test_exc_info_with_exception_type(self):
97        def f():
98            raise ValueError
99
100        try:
101            f()
102        except Exception as e_:
103            e = e_
104            exc_info = sys.exc_info()
105
106        self.assertIsInstance(e, ValueError)
107        self.assertIs(exc_info[0], ValueError)
108        self.assertIs(exc_info[1], e)
109        self.assertIs(exc_info[2], e.__traceback__)
110
111    def test_sys_exception_with_exception_instance(self):
112        def f():
113            raise ValueError(42)
114
115        try:
116            f()
117        except Exception as e_:
118            e = e_
119            exc = sys.exception()
120
121        self.assertIsInstance(e, ValueError)
122        self.assertIs(exc, e)
123
124    def test_sys_exception_with_exception_type(self):
125        def f():
126            raise ValueError
127
128        try:
129            f()
130        except Exception as e_:
131            e = e_
132            exc = sys.exception()
133
134        self.assertIsInstance(e, ValueError)
135        self.assertIs(exc, e)
136
137
138class ExceptHookTest(unittest.TestCase):
139
140    def test_original_excepthook(self):
141        try:
142            raise ValueError(42)
143        except ValueError as exc:
144            with support.captured_stderr() as err:
145                sys.__excepthook__(*sys.exc_info())
146
147        self.assertTrue(err.getvalue().endswith("ValueError: 42\n"))
148
149        self.assertRaises(TypeError, sys.__excepthook__)
150
151    def test_excepthook_bytes_filename(self):
152        # bpo-37467: sys.excepthook() must not crash if a filename
153        # is a bytes string
154        with warnings.catch_warnings():
155            warnings.simplefilter('ignore', BytesWarning)
156
157            try:
158                raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text"))
159            except SyntaxError as exc:
160                with support.captured_stderr() as err:
161                    sys.__excepthook__(*sys.exc_info())
162
163        err = err.getvalue()
164        self.assertIn("""  File "b'bytes_filename'", line 123\n""", err)
165        self.assertIn("""    text\n""", err)
166        self.assertTrue(err.endswith("SyntaxError: msg\n"))
167
168    def test_excepthook(self):
169        with test.support.captured_output("stderr") as stderr:
170            sys.excepthook(1, '1', 1)
171        self.assertTrue("TypeError: print_exception(): Exception expected for " \
172                         "value, str found" in stderr.getvalue())
173
174    # FIXME: testing the code for a lost or replaced excepthook in
175    # Python/pythonrun.c::PyErr_PrintEx() is tricky.
176
177
178class SysModuleTest(unittest.TestCase):
179
180    def tearDown(self):
181        test.support.reap_children()
182
183    def test_exit(self):
184        # call with two arguments
185        self.assertRaises(TypeError, sys.exit, 42, 42)
186
187        # call without argument
188        with self.assertRaises(SystemExit) as cm:
189            sys.exit()
190        self.assertIsNone(cm.exception.code)
191
192        rc, out, err = assert_python_ok('-c', 'import sys; sys.exit()')
193        self.assertEqual(rc, 0)
194        self.assertEqual(out, b'')
195        self.assertEqual(err, b'')
196
197        # call with integer argument
198        with self.assertRaises(SystemExit) as cm:
199            sys.exit(42)
200        self.assertEqual(cm.exception.code, 42)
201
202        # call with tuple argument with one entry
203        # entry will be unpacked
204        with self.assertRaises(SystemExit) as cm:
205            sys.exit((42,))
206        self.assertEqual(cm.exception.code, 42)
207
208        # call with string argument
209        with self.assertRaises(SystemExit) as cm:
210            sys.exit("exit")
211        self.assertEqual(cm.exception.code, "exit")
212
213        # call with tuple argument with two entries
214        with self.assertRaises(SystemExit) as cm:
215            sys.exit((17, 23))
216        self.assertEqual(cm.exception.code, (17, 23))
217
218        # test that the exit machinery handles SystemExits properly
219        rc, out, err = assert_python_failure('-c', 'raise SystemExit(47)')
220        self.assertEqual(rc, 47)
221        self.assertEqual(out, b'')
222        self.assertEqual(err, b'')
223
224        def check_exit_message(code, expected, **env_vars):
225            rc, out, err = assert_python_failure('-c', code, **env_vars)
226            self.assertEqual(rc, 1)
227            self.assertEqual(out, b'')
228            self.assertTrue(err.startswith(expected),
229                "%s doesn't start with %s" % (ascii(err), ascii(expected)))
230
231        # test that stderr buffer is flushed before the exit message is written
232        # into stderr
233        check_exit_message(
234            r'import sys; sys.stderr.write("unflushed,"); sys.exit("message")',
235            b"unflushed,message")
236
237        # test that the exit message is written with backslashreplace error
238        # handler to stderr
239        check_exit_message(
240            r'import sys; sys.exit("surrogates:\uDCFF")',
241            b"surrogates:\\udcff")
242
243        # test that the unicode message is encoded to the stderr encoding
244        # instead of the default encoding (utf8)
245        check_exit_message(
246            r'import sys; sys.exit("h\xe9")',
247            b"h\xe9", PYTHONIOENCODING='latin-1')
248
249    def test_getdefaultencoding(self):
250        self.assertRaises(TypeError, sys.getdefaultencoding, 42)
251        # can't check more than the type, as the user might have changed it
252        self.assertIsInstance(sys.getdefaultencoding(), str)
253
254    # testing sys.settrace() is done in test_sys_settrace.py
255    # testing sys.setprofile() is done in test_sys_setprofile.py
256
257    def test_switchinterval(self):
258        self.assertRaises(TypeError, sys.setswitchinterval)
259        self.assertRaises(TypeError, sys.setswitchinterval, "a")
260        self.assertRaises(ValueError, sys.setswitchinterval, -1.0)
261        self.assertRaises(ValueError, sys.setswitchinterval, 0.0)
262        orig = sys.getswitchinterval()
263        # sanity check
264        self.assertTrue(orig < 0.5, orig)
265        try:
266            for n in 0.00001, 0.05, 3.0, orig:
267                sys.setswitchinterval(n)
268                self.assertAlmostEqual(sys.getswitchinterval(), n)
269        finally:
270            sys.setswitchinterval(orig)
271
272    def test_recursionlimit(self):
273        self.assertRaises(TypeError, sys.getrecursionlimit, 42)
274        oldlimit = sys.getrecursionlimit()
275        self.assertRaises(TypeError, sys.setrecursionlimit)
276        self.assertRaises(ValueError, sys.setrecursionlimit, -42)
277        sys.setrecursionlimit(10000)
278        self.assertEqual(sys.getrecursionlimit(), 10000)
279        sys.setrecursionlimit(oldlimit)
280
281    def test_recursionlimit_recovery(self):
282        if hasattr(sys, 'gettrace') and sys.gettrace():
283            self.skipTest('fatal error if run with a trace function')
284
285        oldlimit = sys.getrecursionlimit()
286        def f():
287            f()
288        try:
289            for depth in (50, 75, 100, 250, 1000):
290                try:
291                    sys.setrecursionlimit(depth)
292                except RecursionError:
293                    # Issue #25274: The recursion limit is too low at the
294                    # current recursion depth
295                    continue
296
297                # Issue #5392: test stack overflow after hitting recursion
298                # limit twice
299                with self.assertRaises(RecursionError):
300                    f()
301                with self.assertRaises(RecursionError):
302                    f()
303        finally:
304            sys.setrecursionlimit(oldlimit)
305
306    @test.support.cpython_only
307    def test_setrecursionlimit_recursion_depth(self):
308        # Issue #25274: Setting a low recursion limit must be blocked if the
309        # current recursion depth is already higher than limit.
310
311        from _testinternalcapi import get_recursion_depth
312
313        def set_recursion_limit_at_depth(depth, limit):
314            recursion_depth = get_recursion_depth()
315            if recursion_depth >= depth:
316                with self.assertRaises(RecursionError) as cm:
317                    sys.setrecursionlimit(limit)
318                self.assertRegex(str(cm.exception),
319                                 "cannot set the recursion limit to [0-9]+ "
320                                 "at the recursion depth [0-9]+: "
321                                 "the limit is too low")
322            else:
323                set_recursion_limit_at_depth(depth, limit)
324
325        oldlimit = sys.getrecursionlimit()
326        try:
327            sys.setrecursionlimit(1000)
328
329            for limit in (10, 25, 50, 75, 100, 150, 200):
330                set_recursion_limit_at_depth(limit, limit)
331        finally:
332            sys.setrecursionlimit(oldlimit)
333
334    def test_getwindowsversion(self):
335        # Raise SkipTest if sys doesn't have getwindowsversion attribute
336        test.support.get_attribute(sys, "getwindowsversion")
337        v = sys.getwindowsversion()
338        self.assertEqual(len(v), 5)
339        self.assertIsInstance(v[0], int)
340        self.assertIsInstance(v[1], int)
341        self.assertIsInstance(v[2], int)
342        self.assertIsInstance(v[3], int)
343        self.assertIsInstance(v[4], str)
344        self.assertRaises(IndexError, operator.getitem, v, 5)
345        self.assertIsInstance(v.major, int)
346        self.assertIsInstance(v.minor, int)
347        self.assertIsInstance(v.build, int)
348        self.assertIsInstance(v.platform, int)
349        self.assertIsInstance(v.service_pack, str)
350        self.assertIsInstance(v.service_pack_minor, int)
351        self.assertIsInstance(v.service_pack_major, int)
352        self.assertIsInstance(v.suite_mask, int)
353        self.assertIsInstance(v.product_type, int)
354        self.assertEqual(v[0], v.major)
355        self.assertEqual(v[1], v.minor)
356        self.assertEqual(v[2], v.build)
357        self.assertEqual(v[3], v.platform)
358        self.assertEqual(v[4], v.service_pack)
359
360        # This is how platform.py calls it. Make sure tuple
361        #  still has 5 elements
362        maj, min, buildno, plat, csd = sys.getwindowsversion()
363
364    def test_call_tracing(self):
365        self.assertRaises(TypeError, sys.call_tracing, type, 2)
366
367    @unittest.skipUnless(hasattr(sys, "setdlopenflags"),
368                         'test needs sys.setdlopenflags()')
369    def test_dlopenflags(self):
370        self.assertTrue(hasattr(sys, "getdlopenflags"))
371        self.assertRaises(TypeError, sys.getdlopenflags, 42)
372        oldflags = sys.getdlopenflags()
373        self.assertRaises(TypeError, sys.setdlopenflags)
374        sys.setdlopenflags(oldflags+1)
375        self.assertEqual(sys.getdlopenflags(), oldflags+1)
376        sys.setdlopenflags(oldflags)
377
378    @test.support.refcount_test
379    def test_refcount(self):
380        # n here must be a global in order for this test to pass while
381        # tracing with a python function.  Tracing calls PyFrame_FastToLocals
382        # which will add a copy of any locals to the frame object, causing
383        # the reference count to increase by 2 instead of 1.
384        global n
385        self.assertRaises(TypeError, sys.getrefcount)
386        c = sys.getrefcount(None)
387        n = None
388        self.assertEqual(sys.getrefcount(None), c+1)
389        del n
390        self.assertEqual(sys.getrefcount(None), c)
391        if hasattr(sys, "gettotalrefcount"):
392            self.assertIsInstance(sys.gettotalrefcount(), int)
393
394    def test_getframe(self):
395        self.assertRaises(TypeError, sys._getframe, 42, 42)
396        self.assertRaises(ValueError, sys._getframe, 2000000000)
397        self.assertTrue(
398            SysModuleTest.test_getframe.__code__ \
399            is sys._getframe().f_code
400        )
401
402    # sys._current_frames() is a CPython-only gimmick.
403    @threading_helper.reap_threads
404    @threading_helper.requires_working_threading()
405    def test_current_frames(self):
406        import threading
407        import traceback
408
409        # Spawn a thread that blocks at a known place.  Then the main
410        # thread does sys._current_frames(), and verifies that the frames
411        # returned make sense.
412        entered_g = threading.Event()
413        leave_g = threading.Event()
414        thread_info = []  # the thread's id
415
416        def f123():
417            g456()
418
419        def g456():
420            thread_info.append(threading.get_ident())
421            entered_g.set()
422            leave_g.wait()
423
424        t = threading.Thread(target=f123)
425        t.start()
426        entered_g.wait()
427
428        # At this point, t has finished its entered_g.set(), although it's
429        # impossible to guess whether it's still on that line or has moved on
430        # to its leave_g.wait().
431        self.assertEqual(len(thread_info), 1)
432        thread_id = thread_info[0]
433
434        d = sys._current_frames()
435        for tid in d:
436            self.assertIsInstance(tid, int)
437            self.assertGreater(tid, 0)
438
439        main_id = threading.get_ident()
440        self.assertIn(main_id, d)
441        self.assertIn(thread_id, d)
442
443        # Verify that the captured main-thread frame is _this_ frame.
444        frame = d.pop(main_id)
445        self.assertTrue(frame is sys._getframe())
446
447        # Verify that the captured thread frame is blocked in g456, called
448        # from f123.  This is a little tricky, since various bits of
449        # threading.py are also in the thread's call stack.
450        frame = d.pop(thread_id)
451        stack = traceback.extract_stack(frame)
452        for i, (filename, lineno, funcname, sourceline) in enumerate(stack):
453            if funcname == "f123":
454                break
455        else:
456            self.fail("didn't find f123() on thread's call stack")
457
458        self.assertEqual(sourceline, "g456()")
459
460        # And the next record must be for g456().
461        filename, lineno, funcname, sourceline = stack[i+1]
462        self.assertEqual(funcname, "g456")
463        self.assertIn(sourceline, ["leave_g.wait()", "entered_g.set()"])
464
465        # Reap the spawned thread.
466        leave_g.set()
467        t.join()
468
469    @threading_helper.reap_threads
470    @threading_helper.requires_working_threading()
471    def test_current_exceptions(self):
472        import threading
473        import traceback
474
475        # Spawn a thread that blocks at a known place.  Then the main
476        # thread does sys._current_frames(), and verifies that the frames
477        # returned make sense.
478        entered_g = threading.Event()
479        leave_g = threading.Event()
480        thread_info = []  # the thread's id
481
482        def f123():
483            g456()
484
485        def g456():
486            thread_info.append(threading.get_ident())
487            entered_g.set()
488            while True:
489                try:
490                    raise ValueError("oops")
491                except ValueError:
492                    if leave_g.wait(timeout=support.LONG_TIMEOUT):
493                        break
494
495        t = threading.Thread(target=f123)
496        t.start()
497        entered_g.wait()
498
499        # At this point, t has finished its entered_g.set(), although it's
500        # impossible to guess whether it's still on that line or has moved on
501        # to its leave_g.wait().
502        self.assertEqual(len(thread_info), 1)
503        thread_id = thread_info[0]
504
505        d = sys._current_exceptions()
506        for tid in d:
507            self.assertIsInstance(tid, int)
508            self.assertGreater(tid, 0)
509
510        main_id = threading.get_ident()
511        self.assertIn(main_id, d)
512        self.assertIn(thread_id, d)
513        self.assertEqual((None, None, None), d.pop(main_id))
514
515        # Verify that the captured thread frame is blocked in g456, called
516        # from f123.  This is a little tricky, since various bits of
517        # threading.py are also in the thread's call stack.
518        exc_type, exc_value, exc_tb = d.pop(thread_id)
519        stack = traceback.extract_stack(exc_tb.tb_frame)
520        for i, (filename, lineno, funcname, sourceline) in enumerate(stack):
521            if funcname == "f123":
522                break
523        else:
524            self.fail("didn't find f123() on thread's call stack")
525
526        self.assertEqual(sourceline, "g456()")
527
528        # And the next record must be for g456().
529        filename, lineno, funcname, sourceline = stack[i+1]
530        self.assertEqual(funcname, "g456")
531        self.assertTrue(sourceline.startswith("if leave_g.wait("))
532
533        # Reap the spawned thread.
534        leave_g.set()
535        t.join()
536
537    def test_attributes(self):
538        self.assertIsInstance(sys.api_version, int)
539        self.assertIsInstance(sys.argv, list)
540        for arg in sys.argv:
541            self.assertIsInstance(arg, str)
542        self.assertIsInstance(sys.orig_argv, list)
543        for arg in sys.orig_argv:
544            self.assertIsInstance(arg, str)
545        self.assertIn(sys.byteorder, ("little", "big"))
546        self.assertIsInstance(sys.builtin_module_names, tuple)
547        self.assertIsInstance(sys.copyright, str)
548        self.assertIsInstance(sys.exec_prefix, str)
549        self.assertIsInstance(sys.base_exec_prefix, str)
550        self.assertIsInstance(sys.executable, str)
551        self.assertEqual(len(sys.float_info), 11)
552        self.assertEqual(sys.float_info.radix, 2)
553        self.assertEqual(len(sys.int_info), 4)
554        self.assertTrue(sys.int_info.bits_per_digit % 5 == 0)
555        self.assertTrue(sys.int_info.sizeof_digit >= 1)
556        self.assertGreaterEqual(sys.int_info.default_max_str_digits, 500)
557        self.assertGreaterEqual(sys.int_info.str_digits_check_threshold, 100)
558        self.assertGreater(sys.int_info.default_max_str_digits,
559                           sys.int_info.str_digits_check_threshold)
560        self.assertEqual(type(sys.int_info.bits_per_digit), int)
561        self.assertEqual(type(sys.int_info.sizeof_digit), int)
562        self.assertIsInstance(sys.int_info.default_max_str_digits, int)
563        self.assertIsInstance(sys.int_info.str_digits_check_threshold, int)
564        self.assertIsInstance(sys.hexversion, int)
565
566        self.assertEqual(len(sys.hash_info), 9)
567        self.assertLess(sys.hash_info.modulus, 2**sys.hash_info.width)
568        # sys.hash_info.modulus should be a prime; we do a quick
569        # probable primality test (doesn't exclude the possibility of
570        # a Carmichael number)
571        for x in range(1, 100):
572            self.assertEqual(
573                pow(x, sys.hash_info.modulus-1, sys.hash_info.modulus),
574                1,
575                "sys.hash_info.modulus {} is a non-prime".format(
576                    sys.hash_info.modulus)
577                )
578        self.assertIsInstance(sys.hash_info.inf, int)
579        self.assertIsInstance(sys.hash_info.nan, int)
580        self.assertIsInstance(sys.hash_info.imag, int)
581        algo = sysconfig.get_config_var("Py_HASH_ALGORITHM")
582        if sys.hash_info.algorithm in {"fnv", "siphash13", "siphash24"}:
583            self.assertIn(sys.hash_info.hash_bits, {32, 64})
584            self.assertIn(sys.hash_info.seed_bits, {32, 64, 128})
585
586            if algo == 1:
587                self.assertEqual(sys.hash_info.algorithm, "siphash24")
588            elif algo == 2:
589                self.assertEqual(sys.hash_info.algorithm, "fnv")
590            elif algo == 3:
591                self.assertEqual(sys.hash_info.algorithm, "siphash13")
592            else:
593                self.assertIn(sys.hash_info.algorithm, {"fnv", "siphash13", "siphash24"})
594        else:
595            # PY_HASH_EXTERNAL
596            self.assertEqual(algo, 0)
597        self.assertGreaterEqual(sys.hash_info.cutoff, 0)
598        self.assertLess(sys.hash_info.cutoff, 8)
599
600        self.assertIsInstance(sys.maxsize, int)
601        self.assertIsInstance(sys.maxunicode, int)
602        self.assertEqual(sys.maxunicode, 0x10FFFF)
603        self.assertIsInstance(sys.platform, str)
604        self.assertIsInstance(sys.prefix, str)
605        self.assertIsInstance(sys.base_prefix, str)
606        self.assertIsInstance(sys.platlibdir, str)
607        self.assertIsInstance(sys.version, str)
608        vi = sys.version_info
609        self.assertIsInstance(vi[:], tuple)
610        self.assertEqual(len(vi), 5)
611        self.assertIsInstance(vi[0], int)
612        self.assertIsInstance(vi[1], int)
613        self.assertIsInstance(vi[2], int)
614        self.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
615        self.assertIsInstance(vi[4], int)
616        self.assertIsInstance(vi.major, int)
617        self.assertIsInstance(vi.minor, int)
618        self.assertIsInstance(vi.micro, int)
619        self.assertIn(vi.releaselevel, ("alpha", "beta", "candidate", "final"))
620        self.assertIsInstance(vi.serial, int)
621        self.assertEqual(vi[0], vi.major)
622        self.assertEqual(vi[1], vi.minor)
623        self.assertEqual(vi[2], vi.micro)
624        self.assertEqual(vi[3], vi.releaselevel)
625        self.assertEqual(vi[4], vi.serial)
626        self.assertTrue(vi > (1,0,0))
627        self.assertIsInstance(sys.float_repr_style, str)
628        self.assertIn(sys.float_repr_style, ('short', 'legacy'))
629        if not sys.platform.startswith('win'):
630            self.assertIsInstance(sys.abiflags, str)
631
632    def test_thread_info(self):
633        info = sys.thread_info
634        self.assertEqual(len(info), 3)
635        self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None))
636        self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))
637        if sys.platform.startswith(("linux", "freebsd")):
638            self.assertEqual(info.name, "pthread")
639        elif sys.platform == "win32":
640            self.assertEqual(info.name, "nt")
641        elif sys.platform == "emscripten":
642            self.assertIn(info.name, {"pthread", "pthread-stubs"})
643        elif sys.platform == "wasi":
644            self.assertEqual(info.name, "pthread-stubs")
645
646    @unittest.skipUnless(support.is_emscripten, "only available on Emscripten")
647    def test_emscripten_info(self):
648        self.assertEqual(len(sys._emscripten_info), 4)
649        self.assertIsInstance(sys._emscripten_info.emscripten_version, tuple)
650        self.assertIsInstance(sys._emscripten_info.runtime, (str, type(None)))
651        self.assertIsInstance(sys._emscripten_info.pthreads, bool)
652        self.assertIsInstance(sys._emscripten_info.shared_memory, bool)
653
654    def test_43581(self):
655        # Can't use sys.stdout, as this is a StringIO object when
656        # the test runs under regrtest.
657        self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding)
658
659    def test_intern(self):
660        global INTERN_NUMRUNS
661        INTERN_NUMRUNS += 1
662        self.assertRaises(TypeError, sys.intern)
663        s = "never interned before" + str(INTERN_NUMRUNS)
664        self.assertTrue(sys.intern(s) is s)
665        s2 = s.swapcase().swapcase()
666        self.assertTrue(sys.intern(s2) is s)
667
668        # Subclasses of string can't be interned, because they
669        # provide too much opportunity for insane things to happen.
670        # We don't want them in the interned dict and if they aren't
671        # actually interned, we don't want to create the appearance
672        # that they are by allowing intern() to succeed.
673        class S(str):
674            def __hash__(self):
675                return 123
676
677        self.assertRaises(TypeError, sys.intern, S("abc"))
678
679    def test_sys_flags(self):
680        self.assertTrue(sys.flags)
681        attrs = ("debug",
682                 "inspect", "interactive", "optimize",
683                 "dont_write_bytecode", "no_user_site", "no_site",
684                 "ignore_environment", "verbose", "bytes_warning", "quiet",
685                 "hash_randomization", "isolated", "dev_mode", "utf8_mode",
686                 "warn_default_encoding", "safe_path", "int_max_str_digits")
687        for attr in attrs:
688            self.assertTrue(hasattr(sys.flags, attr), attr)
689            attr_type = bool if attr in ("dev_mode", "safe_path") else int
690            self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
691        self.assertTrue(repr(sys.flags))
692        self.assertEqual(len(sys.flags), len(attrs))
693
694        self.assertIn(sys.flags.utf8_mode, {0, 1, 2})
695
696    def assert_raise_on_new_sys_type(self, sys_attr):
697        # Users are intentionally prevented from creating new instances of
698        # sys.flags, sys.version_info, and sys.getwindowsversion.
699        arg = sys_attr
700        attr_type = type(sys_attr)
701        with self.assertRaises(TypeError):
702            attr_type(arg)
703        with self.assertRaises(TypeError):
704            attr_type.__new__(attr_type, arg)
705
706    def test_sys_flags_no_instantiation(self):
707        self.assert_raise_on_new_sys_type(sys.flags)
708
709    def test_sys_version_info_no_instantiation(self):
710        self.assert_raise_on_new_sys_type(sys.version_info)
711
712    def test_sys_getwindowsversion_no_instantiation(self):
713        # Skip if not being run on Windows.
714        test.support.get_attribute(sys, "getwindowsversion")
715        self.assert_raise_on_new_sys_type(sys.getwindowsversion())
716
717    @test.support.cpython_only
718    def test_clear_type_cache(self):
719        sys._clear_type_cache()
720
721    @support.requires_subprocess()
722    def test_ioencoding(self):
723        env = dict(os.environ)
724
725        # Test character: cent sign, encoded as 0x4A (ASCII J) in CP424,
726        # not representable in ASCII.
727
728        env["PYTHONIOENCODING"] = "cp424"
729        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
730                             stdout = subprocess.PIPE, env=env)
731        out = p.communicate()[0].strip()
732        expected = ("\xa2" + os.linesep).encode("cp424")
733        self.assertEqual(out, expected)
734
735        env["PYTHONIOENCODING"] = "ascii:replace"
736        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
737                             stdout = subprocess.PIPE, env=env)
738        out = p.communicate()[0].strip()
739        self.assertEqual(out, b'?')
740
741        env["PYTHONIOENCODING"] = "ascii"
742        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
743                             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
744                             env=env)
745        out, err = p.communicate()
746        self.assertEqual(out, b'')
747        self.assertIn(b'UnicodeEncodeError:', err)
748        self.assertIn(rb"'\xa2'", err)
749
750        env["PYTHONIOENCODING"] = "ascii:"
751        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
752                             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
753                             env=env)
754        out, err = p.communicate()
755        self.assertEqual(out, b'')
756        self.assertIn(b'UnicodeEncodeError:', err)
757        self.assertIn(rb"'\xa2'", err)
758
759        env["PYTHONIOENCODING"] = ":surrogateescape"
760        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xdcbd))'],
761                             stdout=subprocess.PIPE, env=env)
762        out = p.communicate()[0].strip()
763        self.assertEqual(out, b'\xbd')
764
765    @unittest.skipUnless(os_helper.FS_NONASCII,
766                         'requires OS support of non-ASCII encodings')
767    @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False),
768                         'requires FS encoding to match locale')
769    @support.requires_subprocess()
770    def test_ioencoding_nonascii(self):
771        env = dict(os.environ)
772
773        env["PYTHONIOENCODING"] = ""
774        p = subprocess.Popen([sys.executable, "-c",
775                                'print(%a)' % os_helper.FS_NONASCII],
776                                stdout=subprocess.PIPE, env=env)
777        out = p.communicate()[0].strip()
778        self.assertEqual(out, os.fsencode(os_helper.FS_NONASCII))
779
780    @unittest.skipIf(sys.base_prefix != sys.prefix,
781                     'Test is not venv-compatible')
782    @support.requires_subprocess()
783    def test_executable(self):
784        # sys.executable should be absolute
785        self.assertEqual(os.path.abspath(sys.executable), sys.executable)
786
787        # Issue #7774: Ensure that sys.executable is an empty string if argv[0]
788        # has been set to a non existent program name and Python is unable to
789        # retrieve the real program name
790
791        # For a normal installation, it should work without 'cwd'
792        # argument. For test runs in the build directory, see #7774.
793        python_dir = os.path.dirname(os.path.realpath(sys.executable))
794        p = subprocess.Popen(
795            ["nonexistent", "-c",
796             'import sys; print(sys.executable.encode("ascii", "backslashreplace"))'],
797            executable=sys.executable, stdout=subprocess.PIPE, cwd=python_dir)
798        stdout = p.communicate()[0]
799        executable = stdout.strip().decode("ASCII")
800        p.wait()
801        self.assertIn(executable, ["b''", repr(sys.executable.encode("ascii", "backslashreplace"))])
802
803    def check_fsencoding(self, fs_encoding, expected=None):
804        self.assertIsNotNone(fs_encoding)
805        codecs.lookup(fs_encoding)
806        if expected:
807            self.assertEqual(fs_encoding, expected)
808
809    def test_getfilesystemencoding(self):
810        fs_encoding = sys.getfilesystemencoding()
811        if sys.platform == 'darwin':
812            expected = 'utf-8'
813        else:
814            expected = None
815        self.check_fsencoding(fs_encoding, expected)
816
817    def c_locale_get_error_handler(self, locale, isolated=False, encoding=None):
818        # Force the POSIX locale
819        env = os.environ.copy()
820        env["LC_ALL"] = locale
821        env["PYTHONCOERCECLOCALE"] = "0"
822        code = '\n'.join((
823            'import sys',
824            'def dump(name):',
825            '    std = getattr(sys, name)',
826            '    print("%s: %s" % (name, std.errors))',
827            'dump("stdin")',
828            'dump("stdout")',
829            'dump("stderr")',
830        ))
831        args = [sys.executable, "-X", "utf8=0", "-c", code]
832        if isolated:
833            args.append("-I")
834        if encoding is not None:
835            env['PYTHONIOENCODING'] = encoding
836        else:
837            env.pop('PYTHONIOENCODING', None)
838        p = subprocess.Popen(args,
839                              stdout=subprocess.PIPE,
840                              stderr=subprocess.STDOUT,
841                              env=env,
842                              universal_newlines=True)
843        stdout, stderr = p.communicate()
844        return stdout
845
846    def check_locale_surrogateescape(self, locale):
847        out = self.c_locale_get_error_handler(locale, isolated=True)
848        self.assertEqual(out,
849                         'stdin: surrogateescape\n'
850                         'stdout: surrogateescape\n'
851                         'stderr: backslashreplace\n')
852
853        # replace the default error handler
854        out = self.c_locale_get_error_handler(locale, encoding=':ignore')
855        self.assertEqual(out,
856                         'stdin: ignore\n'
857                         'stdout: ignore\n'
858                         'stderr: backslashreplace\n')
859
860        # force the encoding
861        out = self.c_locale_get_error_handler(locale, encoding='iso8859-1')
862        self.assertEqual(out,
863                         'stdin: strict\n'
864                         'stdout: strict\n'
865                         'stderr: backslashreplace\n')
866        out = self.c_locale_get_error_handler(locale, encoding='iso8859-1:')
867        self.assertEqual(out,
868                         'stdin: strict\n'
869                         'stdout: strict\n'
870                         'stderr: backslashreplace\n')
871
872        # have no any effect
873        out = self.c_locale_get_error_handler(locale, encoding=':')
874        self.assertEqual(out,
875                         'stdin: surrogateescape\n'
876                         'stdout: surrogateescape\n'
877                         'stderr: backslashreplace\n')
878        out = self.c_locale_get_error_handler(locale, encoding='')
879        self.assertEqual(out,
880                         'stdin: surrogateescape\n'
881                         'stdout: surrogateescape\n'
882                         'stderr: backslashreplace\n')
883
884    @support.requires_subprocess()
885    def test_c_locale_surrogateescape(self):
886        self.check_locale_surrogateescape('C')
887
888    @support.requires_subprocess()
889    def test_posix_locale_surrogateescape(self):
890        self.check_locale_surrogateescape('POSIX')
891
892    def test_implementation(self):
893        # This test applies to all implementations equally.
894
895        levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'final': 0xF}
896
897        self.assertTrue(hasattr(sys.implementation, 'name'))
898        self.assertTrue(hasattr(sys.implementation, 'version'))
899        self.assertTrue(hasattr(sys.implementation, 'hexversion'))
900        self.assertTrue(hasattr(sys.implementation, 'cache_tag'))
901
902        version = sys.implementation.version
903        self.assertEqual(version[:2], (version.major, version.minor))
904
905        hexversion = (version.major << 24 | version.minor << 16 |
906                      version.micro << 8 | levels[version.releaselevel] << 4 |
907                      version.serial << 0)
908        self.assertEqual(sys.implementation.hexversion, hexversion)
909
910        # PEP 421 requires that .name be lower case.
911        self.assertEqual(sys.implementation.name,
912                         sys.implementation.name.lower())
913
914    @test.support.cpython_only
915    def test_debugmallocstats(self):
916        # Test sys._debugmallocstats()
917        from test.support.script_helper import assert_python_ok
918        args = ['-c', 'import sys; sys._debugmallocstats()']
919        ret, out, err = assert_python_ok(*args)
920
921        # Output of sys._debugmallocstats() depends on configure flags.
922        # The sysconfig vars are not available on Windows.
923        if sys.platform != "win32":
924            with_freelists = sysconfig.get_config_var("WITH_FREELISTS")
925            with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
926            if with_freelists:
927                self.assertIn(b"free PyDictObjects", err)
928            if with_pymalloc:
929                self.assertIn(b'Small block threshold', err)
930            if not with_freelists and not with_pymalloc:
931                self.assertFalse(err)
932
933        # The function has no parameter
934        self.assertRaises(TypeError, sys._debugmallocstats, True)
935
936    @unittest.skipUnless(hasattr(sys, "getallocatedblocks"),
937                         "sys.getallocatedblocks unavailable on this build")
938    def test_getallocatedblocks(self):
939        try:
940            import _testcapi
941        except ImportError:
942            with_pymalloc = support.with_pymalloc()
943        else:
944            try:
945                alloc_name = _testcapi.pymem_getallocatorsname()
946            except RuntimeError as exc:
947                # "cannot get allocators name" (ex: tracemalloc is used)
948                with_pymalloc = True
949            else:
950                with_pymalloc = (alloc_name in ('pymalloc', 'pymalloc_debug'))
951
952        # Some sanity checks
953        a = sys.getallocatedblocks()
954        self.assertIs(type(a), int)
955        if with_pymalloc:
956            self.assertGreater(a, 0)
957        else:
958            # When WITH_PYMALLOC isn't available, we don't know anything
959            # about the underlying implementation: the function might
960            # return 0 or something greater.
961            self.assertGreaterEqual(a, 0)
962        try:
963            # While we could imagine a Python session where the number of
964            # multiple buffer objects would exceed the sharing of references,
965            # it is unlikely to happen in a normal test run.
966            self.assertLess(a, sys.gettotalrefcount())
967        except AttributeError:
968            # gettotalrefcount() not available
969            pass
970        gc.collect()
971        b = sys.getallocatedblocks()
972        self.assertLessEqual(b, a)
973        gc.collect()
974        c = sys.getallocatedblocks()
975        self.assertIn(c, range(b - 50, b + 50))
976
977    def test_is_finalizing(self):
978        self.assertIs(sys.is_finalizing(), False)
979        # Don't use the atexit module because _Py_Finalizing is only set
980        # after calling atexit callbacks
981        code = """if 1:
982            import sys
983
984            class AtExit:
985                is_finalizing = sys.is_finalizing
986                print = print
987
988                def __del__(self):
989                    self.print(self.is_finalizing(), flush=True)
990
991            # Keep a reference in the __main__ module namespace, so the
992            # AtExit destructor will be called at Python exit
993            ref = AtExit()
994        """
995        rc, stdout, stderr = assert_python_ok('-c', code)
996        self.assertEqual(stdout.rstrip(), b'True')
997
998    def test_issue20602(self):
999        # sys.flags and sys.float_info were wiped during shutdown.
1000        code = """if 1:
1001            import sys
1002            class A:
1003                def __del__(self, sys=sys):
1004                    print(sys.flags)
1005                    print(sys.float_info)
1006            a = A()
1007            """
1008        rc, out, err = assert_python_ok('-c', code)
1009        out = out.splitlines()
1010        self.assertIn(b'sys.flags', out[0])
1011        self.assertIn(b'sys.float_info', out[1])
1012
1013    def test_sys_ignores_cleaning_up_user_data(self):
1014        code = """if 1:
1015            import struct, sys
1016
1017            class C:
1018                def __init__(self):
1019                    self.pack = struct.pack
1020                def __del__(self):
1021                    self.pack('I', -42)
1022
1023            sys.x = C()
1024            """
1025        rc, stdout, stderr = assert_python_ok('-c', code)
1026        self.assertEqual(rc, 0)
1027        self.assertEqual(stdout.rstrip(), b"")
1028        self.assertEqual(stderr.rstrip(), b"")
1029
1030    @unittest.skipUnless(hasattr(sys, 'getandroidapilevel'),
1031                         'need sys.getandroidapilevel()')
1032    def test_getandroidapilevel(self):
1033        level = sys.getandroidapilevel()
1034        self.assertIsInstance(level, int)
1035        self.assertGreater(level, 0)
1036
1037    @support.requires_subprocess()
1038    def test_sys_tracebacklimit(self):
1039        code = """if 1:
1040            import sys
1041            def f1():
1042                1 / 0
1043            def f2():
1044                f1()
1045            sys.tracebacklimit = %r
1046            f2()
1047        """
1048        def check(tracebacklimit, expected):
1049            p = subprocess.Popen([sys.executable, '-c', code % tracebacklimit],
1050                                 stderr=subprocess.PIPE)
1051            out = p.communicate()[1]
1052            self.assertEqual(out.splitlines(), expected)
1053
1054        traceback = [
1055            b'Traceback (most recent call last):',
1056            b'  File "<string>", line 8, in <module>',
1057            b'  File "<string>", line 6, in f2',
1058            b'  File "<string>", line 4, in f1',
1059            b'ZeroDivisionError: division by zero'
1060        ]
1061        check(10, traceback)
1062        check(3, traceback)
1063        check(2, traceback[:1] + traceback[2:])
1064        check(1, traceback[:1] + traceback[3:])
1065        check(0, [traceback[-1]])
1066        check(-1, [traceback[-1]])
1067        check(1<<1000, traceback)
1068        check(-1<<1000, [traceback[-1]])
1069        check(None, traceback)
1070
1071    def test_no_duplicates_in_meta_path(self):
1072        self.assertEqual(len(sys.meta_path), len(set(sys.meta_path)))
1073
1074    @unittest.skipUnless(hasattr(sys, "_enablelegacywindowsfsencoding"),
1075                         'needs sys._enablelegacywindowsfsencoding()')
1076    def test__enablelegacywindowsfsencoding(self):
1077        code = ('import sys',
1078                'sys._enablelegacywindowsfsencoding()',
1079                'print(sys.getfilesystemencoding(), sys.getfilesystemencodeerrors())')
1080        rc, out, err = assert_python_ok('-c', '; '.join(code))
1081        out = out.decode('ascii', 'replace').rstrip()
1082        self.assertEqual(out, 'mbcs replace')
1083
1084    @support.requires_subprocess()
1085    def test_orig_argv(self):
1086        code = textwrap.dedent('''
1087            import sys
1088            print(sys.argv)
1089            print(sys.orig_argv)
1090        ''')
1091        args = [sys.executable, '-I', '-X', 'utf8', '-c', code, 'arg']
1092        proc = subprocess.run(args, check=True, capture_output=True, text=True)
1093        expected = [
1094            repr(['-c', 'arg']),  # sys.argv
1095            repr(args),  # sys.orig_argv
1096        ]
1097        self.assertEqual(proc.stdout.rstrip().splitlines(), expected,
1098                         proc)
1099
1100    def test_module_names(self):
1101        self.assertIsInstance(sys.stdlib_module_names, frozenset)
1102        for name in sys.stdlib_module_names:
1103            self.assertIsInstance(name, str)
1104
1105    def test_stdlib_dir(self):
1106        os = import_helper.import_fresh_module('os')
1107        marker = getattr(os, '__file__', None)
1108        if marker and not os.path.exists(marker):
1109            marker = None
1110        expected = os.path.dirname(marker) if marker else None
1111        self.assertEqual(os.path.normpath(sys._stdlib_dir),
1112                         os.path.normpath(expected))
1113
1114
1115@test.support.cpython_only
1116class UnraisableHookTest(unittest.TestCase):
1117    def write_unraisable_exc(self, exc, err_msg, obj):
1118        import _testcapi
1119        import types
1120        err_msg2 = f"Exception ignored {err_msg}"
1121        try:
1122            _testcapi.write_unraisable_exc(exc, err_msg, obj)
1123            return types.SimpleNamespace(exc_type=type(exc),
1124                                         exc_value=exc,
1125                                         exc_traceback=exc.__traceback__,
1126                                         err_msg=err_msg2,
1127                                         object=obj)
1128        finally:
1129            # Explicitly break any reference cycle
1130            exc = None
1131
1132    def test_original_unraisablehook(self):
1133        for err_msg in (None, "original hook"):
1134            with self.subTest(err_msg=err_msg):
1135                obj = "an object"
1136
1137                with test.support.captured_output("stderr") as stderr:
1138                    with test.support.swap_attr(sys, 'unraisablehook',
1139                                                sys.__unraisablehook__):
1140                        self.write_unraisable_exc(ValueError(42), err_msg, obj)
1141
1142                err = stderr.getvalue()
1143                if err_msg is not None:
1144                    self.assertIn(f'Exception ignored {err_msg}: {obj!r}\n', err)
1145                else:
1146                    self.assertIn(f'Exception ignored in: {obj!r}\n', err)
1147                self.assertIn('Traceback (most recent call last):\n', err)
1148                self.assertIn('ValueError: 42\n', err)
1149
1150    def test_original_unraisablehook_err(self):
1151        # bpo-22836: PyErr_WriteUnraisable() should give sensible reports
1152        class BrokenDel:
1153            def __del__(self):
1154                exc = ValueError("del is broken")
1155                # The following line is included in the traceback report:
1156                raise exc
1157
1158        class BrokenStrException(Exception):
1159            def __str__(self):
1160                raise Exception("str() is broken")
1161
1162        class BrokenExceptionDel:
1163            def __del__(self):
1164                exc = BrokenStrException()
1165                # The following line is included in the traceback report:
1166                raise exc
1167
1168        for test_class in (BrokenDel, BrokenExceptionDel):
1169            with self.subTest(test_class):
1170                obj = test_class()
1171                with test.support.captured_stderr() as stderr, \
1172                     test.support.swap_attr(sys, 'unraisablehook',
1173                                            sys.__unraisablehook__):
1174                    # Trigger obj.__del__()
1175                    del obj
1176
1177                report = stderr.getvalue()
1178                self.assertIn("Exception ignored", report)
1179                self.assertIn(test_class.__del__.__qualname__, report)
1180                self.assertIn("test_sys.py", report)
1181                self.assertIn("raise exc", report)
1182                if test_class is BrokenExceptionDel:
1183                    self.assertIn("BrokenStrException", report)
1184                    self.assertIn("<exception str() failed>", report)
1185                else:
1186                    self.assertIn("ValueError", report)
1187                    self.assertIn("del is broken", report)
1188                self.assertTrue(report.endswith("\n"))
1189
1190    def test_original_unraisablehook_exception_qualname(self):
1191        # See bpo-41031, bpo-45083.
1192        # Check that the exception is printed with its qualified name
1193        # rather than just classname, and the module names appears
1194        # unless it is one of the hard-coded exclusions.
1195        class A:
1196            class B:
1197                class X(Exception):
1198                    pass
1199
1200        for moduleName in 'builtins', '__main__', 'some_module':
1201            with self.subTest(moduleName=moduleName):
1202                A.B.X.__module__ = moduleName
1203                with test.support.captured_stderr() as stderr, test.support.swap_attr(
1204                    sys, 'unraisablehook', sys.__unraisablehook__
1205                ):
1206                    expected = self.write_unraisable_exc(
1207                        A.B.X(), "msg", "obj"
1208                    )
1209                report = stderr.getvalue()
1210                self.assertIn(A.B.X.__qualname__, report)
1211                if moduleName in ['builtins', '__main__']:
1212                    self.assertNotIn(moduleName + '.', report)
1213                else:
1214                    self.assertIn(moduleName + '.', report)
1215
1216    def test_original_unraisablehook_wrong_type(self):
1217        exc = ValueError(42)
1218        with test.support.swap_attr(sys, 'unraisablehook',
1219                                    sys.__unraisablehook__):
1220            with self.assertRaises(TypeError):
1221                sys.unraisablehook(exc)
1222
1223    def test_custom_unraisablehook(self):
1224        hook_args = None
1225
1226        def hook_func(args):
1227            nonlocal hook_args
1228            hook_args = args
1229
1230        obj = object()
1231        try:
1232            with test.support.swap_attr(sys, 'unraisablehook', hook_func):
1233                expected = self.write_unraisable_exc(ValueError(42),
1234                                                     "custom hook", obj)
1235                for attr in "exc_type exc_value exc_traceback err_msg object".split():
1236                    self.assertEqual(getattr(hook_args, attr),
1237                                     getattr(expected, attr),
1238                                     (hook_args, expected))
1239        finally:
1240            # expected and hook_args contain an exception: break reference cycle
1241            expected = None
1242            hook_args = None
1243
1244    def test_custom_unraisablehook_fail(self):
1245        def hook_func(*args):
1246            raise Exception("hook_func failed")
1247
1248        with test.support.captured_output("stderr") as stderr:
1249            with test.support.swap_attr(sys, 'unraisablehook', hook_func):
1250                self.write_unraisable_exc(ValueError(42),
1251                                          "custom hook fail", None)
1252
1253        err = stderr.getvalue()
1254        self.assertIn(f'Exception ignored in sys.unraisablehook: '
1255                      f'{hook_func!r}\n',
1256                      err)
1257        self.assertIn('Traceback (most recent call last):\n', err)
1258        self.assertIn('Exception: hook_func failed\n', err)
1259
1260
1261@test.support.cpython_only
1262class SizeofTest(unittest.TestCase):
1263
1264    def setUp(self):
1265        self.P = struct.calcsize('P')
1266        self.longdigit = sys.int_info.sizeof_digit
1267        import _testinternalcapi
1268        self.gc_headsize = _testinternalcapi.SIZEOF_PYGC_HEAD
1269
1270    check_sizeof = test.support.check_sizeof
1271
1272    def test_gc_head_size(self):
1273        # Check that the gc header size is added to objects tracked by the gc.
1274        vsize = test.support.calcvobjsize
1275        gc_header_size = self.gc_headsize
1276        # bool objects are not gc tracked
1277        self.assertEqual(sys.getsizeof(True), vsize('') + self.longdigit)
1278        # but lists are
1279        self.assertEqual(sys.getsizeof([]), vsize('Pn') + gc_header_size)
1280
1281    def test_errors(self):
1282        class BadSizeof:
1283            def __sizeof__(self):
1284                raise ValueError
1285        self.assertRaises(ValueError, sys.getsizeof, BadSizeof())
1286
1287        class InvalidSizeof:
1288            def __sizeof__(self):
1289                return None
1290        self.assertRaises(TypeError, sys.getsizeof, InvalidSizeof())
1291        sentinel = ["sentinel"]
1292        self.assertIs(sys.getsizeof(InvalidSizeof(), sentinel), sentinel)
1293
1294        class FloatSizeof:
1295            def __sizeof__(self):
1296                return 4.5
1297        self.assertRaises(TypeError, sys.getsizeof, FloatSizeof())
1298        self.assertIs(sys.getsizeof(FloatSizeof(), sentinel), sentinel)
1299
1300        class OverflowSizeof(int):
1301            def __sizeof__(self):
1302                return int(self)
1303        self.assertEqual(sys.getsizeof(OverflowSizeof(sys.maxsize)),
1304                         sys.maxsize + self.gc_headsize)
1305        with self.assertRaises(OverflowError):
1306            sys.getsizeof(OverflowSizeof(sys.maxsize + 1))
1307        with self.assertRaises(ValueError):
1308            sys.getsizeof(OverflowSizeof(-1))
1309        with self.assertRaises((ValueError, OverflowError)):
1310            sys.getsizeof(OverflowSizeof(-sys.maxsize - 1))
1311
1312    def test_default(self):
1313        size = test.support.calcvobjsize
1314        self.assertEqual(sys.getsizeof(True), size('') + self.longdigit)
1315        self.assertEqual(sys.getsizeof(True, -1), size('') + self.longdigit)
1316
1317    def test_objecttypes(self):
1318        # check all types defined in Objects/
1319        calcsize = struct.calcsize
1320        size = test.support.calcobjsize
1321        vsize = test.support.calcvobjsize
1322        check = self.check_sizeof
1323        # bool
1324        check(True, vsize('') + self.longdigit)
1325        check(False, vsize('') + self.longdigit)
1326        # buffer
1327        # XXX
1328        # builtin_function_or_method
1329        check(len, size('5P'))
1330        # bytearray
1331        samples = [b'', b'u'*100000]
1332        for sample in samples:
1333            x = bytearray(sample)
1334            check(x, vsize('n2Pi') + x.__alloc__())
1335        # bytearray_iterator
1336        check(iter(bytearray()), size('nP'))
1337        # bytes
1338        check(b'', vsize('n') + 1)
1339        check(b'x' * 10, vsize('n') + 11)
1340        # cell
1341        def get_cell():
1342            x = 42
1343            def inner():
1344                return x
1345            return inner
1346        check(get_cell().__closure__[0], size('P'))
1347        # code
1348        def check_code_size(a, expected_size):
1349            self.assertGreaterEqual(sys.getsizeof(a), expected_size)
1350        check_code_size(get_cell().__code__, size('6i13P'))
1351        check_code_size(get_cell.__code__, size('6i13P'))
1352        def get_cell2(x):
1353            def inner():
1354                return x
1355            return inner
1356        check_code_size(get_cell2.__code__, size('6i13P') + calcsize('n'))
1357        # complex
1358        check(complex(0,1), size('2d'))
1359        # method_descriptor (descriptor object)
1360        check(str.lower, size('3PPP'))
1361        # classmethod_descriptor (descriptor object)
1362        # XXX
1363        # member_descriptor (descriptor object)
1364        import datetime
1365        check(datetime.timedelta.days, size('3PP'))
1366        # getset_descriptor (descriptor object)
1367        import collections
1368        check(collections.defaultdict.default_factory, size('3PP'))
1369        # wrapper_descriptor (descriptor object)
1370        check(int.__add__, size('3P2P'))
1371        # method-wrapper (descriptor object)
1372        check({}.__iter__, size('2P'))
1373        # empty dict
1374        check({}, size('nQ2P'))
1375        # dict (string key)
1376        check({"a": 1}, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + (8*2//3)*calcsize('2P'))
1377        longdict = {str(i): i for i in range(8)}
1378        check(longdict, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + (16*2//3)*calcsize('2P'))
1379        # dict (non-string key)
1380        check({1: 1}, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + (8*2//3)*calcsize('n2P'))
1381        longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
1382        check(longdict, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + (16*2//3)*calcsize('n2P'))
1383        # dictionary-keyview
1384        check({}.keys(), size('P'))
1385        # dictionary-valueview
1386        check({}.values(), size('P'))
1387        # dictionary-itemview
1388        check({}.items(), size('P'))
1389        # dictionary iterator
1390        check(iter({}), size('P2nPn'))
1391        # dictionary-keyiterator
1392        check(iter({}.keys()), size('P2nPn'))
1393        # dictionary-valueiterator
1394        check(iter({}.values()), size('P2nPn'))
1395        # dictionary-itemiterator
1396        check(iter({}.items()), size('P2nPn'))
1397        # dictproxy
1398        class C(object): pass
1399        check(C.__dict__, size('P'))
1400        # BaseException
1401        check(BaseException(), size('6Pb'))
1402        # UnicodeEncodeError
1403        check(UnicodeEncodeError("", "", 0, 0, ""), size('6Pb 2P2nP'))
1404        # UnicodeDecodeError
1405        check(UnicodeDecodeError("", b"", 0, 0, ""), size('6Pb 2P2nP'))
1406        # UnicodeTranslateError
1407        check(UnicodeTranslateError("", 0, 1, ""), size('6Pb 2P2nP'))
1408        # ellipses
1409        check(Ellipsis, size(''))
1410        # EncodingMap
1411        import codecs, encodings.iso8859_3
1412        x = codecs.charmap_build(encodings.iso8859_3.decoding_table)
1413        check(x, size('32B2iB'))
1414        # enumerate
1415        check(enumerate([]), size('n4P'))
1416        # reverse
1417        check(reversed(''), size('nP'))
1418        # float
1419        check(float(0), size('d'))
1420        # sys.floatinfo
1421        check(sys.float_info, vsize('') + self.P * len(sys.float_info))
1422        # frame
1423        def func():
1424            return sys._getframe()
1425        x = func()
1426        check(x, size('3Pi3c7P2ic??2P'))
1427        # function
1428        def func(): pass
1429        check(func, size('14Pi'))
1430        class c():
1431            @staticmethod
1432            def foo():
1433                pass
1434            @classmethod
1435            def bar(cls):
1436                pass
1437            # staticmethod
1438            check(foo, size('PP'))
1439            # classmethod
1440            check(bar, size('PP'))
1441        # generator
1442        def get_gen(): yield 1
1443        check(get_gen(), size('P2P4P4c7P2ic??P'))
1444        # iterator
1445        check(iter('abc'), size('lP'))
1446        # callable-iterator
1447        import re
1448        check(re.finditer('',''), size('2P'))
1449        # list
1450        check(list([]), vsize('Pn'))
1451        check(list([1]), vsize('Pn') + 2*self.P)
1452        check(list([1, 2]), vsize('Pn') + 2*self.P)
1453        check(list([1, 2, 3]), vsize('Pn') + 4*self.P)
1454        # sortwrapper (list)
1455        # XXX
1456        # cmpwrapper (list)
1457        # XXX
1458        # listiterator (list)
1459        check(iter([]), size('lP'))
1460        # listreverseiterator (list)
1461        check(reversed([]), size('nP'))
1462        # int
1463        check(0, vsize('') + self.longdigit)
1464        check(1, vsize('') + self.longdigit)
1465        check(-1, vsize('') + self.longdigit)
1466        PyLong_BASE = 2**sys.int_info.bits_per_digit
1467        check(int(PyLong_BASE), vsize('') + 2*self.longdigit)
1468        check(int(PyLong_BASE**2-1), vsize('') + 2*self.longdigit)
1469        check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit)
1470        # module
1471        check(unittest, size('PnPPP'))
1472        # None
1473        check(None, size(''))
1474        # NotImplementedType
1475        check(NotImplemented, size(''))
1476        # object
1477        check(object(), size(''))
1478        # property (descriptor object)
1479        class C(object):
1480            def getx(self): return self.__x
1481            def setx(self, value): self.__x = value
1482            def delx(self): del self.__x
1483            x = property(getx, setx, delx, "")
1484            check(x, size('5Pi'))
1485        # PyCapsule
1486        # XXX
1487        # rangeiterator
1488        check(iter(range(1)), size('4l'))
1489        # reverse
1490        check(reversed(''), size('nP'))
1491        # range
1492        check(range(1), size('4P'))
1493        check(range(66000), size('4P'))
1494        # set
1495        # frozenset
1496        PySet_MINSIZE = 8
1497        samples = [[], range(10), range(50)]
1498        s = size('3nP' + PySet_MINSIZE*'nP' + '2nP')
1499        for sample in samples:
1500            minused = len(sample)
1501            if minused == 0: tmp = 1
1502            # the computation of minused is actually a bit more complicated
1503            # but this suffices for the sizeof test
1504            minused = minused*2
1505            newsize = PySet_MINSIZE
1506            while newsize <= minused:
1507                newsize = newsize << 1
1508            if newsize <= 8:
1509                check(set(sample), s)
1510                check(frozenset(sample), s)
1511            else:
1512                check(set(sample), s + newsize*calcsize('nP'))
1513                check(frozenset(sample), s + newsize*calcsize('nP'))
1514        # setiterator
1515        check(iter(set()), size('P3n'))
1516        # slice
1517        check(slice(0), size('3P'))
1518        # super
1519        check(super(int), size('3P'))
1520        # tuple
1521        check((), vsize(''))
1522        check((1,2,3), vsize('') + 3*self.P)
1523        # type
1524        # static type: PyTypeObject
1525        fmt = 'P2nPI13Pl4Pn9Pn12PIP'
1526        s = vsize('2P' + fmt)
1527        check(int, s)
1528        # class
1529        s = vsize(fmt +                 # PyTypeObject
1530                  '4P'                  # PyAsyncMethods
1531                  '36P'                 # PyNumberMethods
1532                  '3P'                  # PyMappingMethods
1533                  '10P'                 # PySequenceMethods
1534                  '2P'                  # PyBufferProcs
1535                  '6P'
1536                  '1P'                  # Specializer cache
1537                  )
1538        class newstyleclass(object): pass
1539        # Separate block for PyDictKeysObject with 8 keys and 5 entries
1540        check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 64 + 42*calcsize("2P"))
1541        # dict with shared keys
1542        [newstyleclass() for _ in range(100)]
1543        check(newstyleclass().__dict__, size('nQ2P') + self.P)
1544        o = newstyleclass()
1545        o.a = o.b = o.c = o.d = o.e = o.f = o.g = o.h = 1
1546        # Separate block for PyDictKeysObject with 16 keys and 10 entries
1547        check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 64 + 42*calcsize("2P"))
1548        # dict with shared keys
1549        check(newstyleclass().__dict__, size('nQ2P') + self.P)
1550        # unicode
1551        # each tuple contains a string and its expected character size
1552        # don't put any static strings here, as they may contain
1553        # wchar_t or UTF-8 representations
1554        samples = ['1'*100, '\xff'*50,
1555                   '\u0100'*40, '\uffff'*100,
1556                   '\U00010000'*30, '\U0010ffff'*100]
1557        # also update field definitions in test_unicode.test_raiseMemError
1558        asciifields = "nnbP"
1559        compactfields = asciifields + "nPn"
1560        unicodefields = compactfields + "P"
1561        for s in samples:
1562            maxchar = ord(max(s))
1563            if maxchar < 128:
1564                L = size(asciifields) + len(s) + 1
1565            elif maxchar < 256:
1566                L = size(compactfields) + len(s) + 1
1567            elif maxchar < 65536:
1568                L = size(compactfields) + 2*(len(s) + 1)
1569            else:
1570                L = size(compactfields) + 4*(len(s) + 1)
1571            check(s, L)
1572        # verify that the UTF-8 size is accounted for
1573        s = chr(0x4000)   # 4 bytes canonical representation
1574        check(s, size(compactfields) + 4)
1575        # compile() will trigger the generation of the UTF-8
1576        # representation as a side effect
1577        compile(s, "<stdin>", "eval")
1578        check(s, size(compactfields) + 4 + 4)
1579        # TODO: add check that forces the presence of wchar_t representation
1580        # TODO: add check that forces layout of unicodefields
1581        # weakref
1582        import weakref
1583        check(weakref.ref(int), size('2Pn3P'))
1584        # weakproxy
1585        # XXX
1586        # weakcallableproxy
1587        check(weakref.proxy(int), size('2Pn3P'))
1588
1589    def check_slots(self, obj, base, extra):
1590        expected = sys.getsizeof(base) + struct.calcsize(extra)
1591        if gc.is_tracked(obj) and not gc.is_tracked(base):
1592            expected += self.gc_headsize
1593        self.assertEqual(sys.getsizeof(obj), expected)
1594
1595    def test_slots(self):
1596        # check all subclassable types defined in Objects/ that allow
1597        # non-empty __slots__
1598        check = self.check_slots
1599        class BA(bytearray):
1600            __slots__ = 'a', 'b', 'c'
1601        check(BA(), bytearray(), '3P')
1602        class D(dict):
1603            __slots__ = 'a', 'b', 'c'
1604        check(D(x=[]), {'x': []}, '3P')
1605        class L(list):
1606            __slots__ = 'a', 'b', 'c'
1607        check(L(), [], '3P')
1608        class S(set):
1609            __slots__ = 'a', 'b', 'c'
1610        check(S(), set(), '3P')
1611        class FS(frozenset):
1612            __slots__ = 'a', 'b', 'c'
1613        check(FS(), frozenset(), '3P')
1614        from collections import OrderedDict
1615        class OD(OrderedDict):
1616            __slots__ = 'a', 'b', 'c'
1617        check(OD(x=[]), OrderedDict(x=[]), '3P')
1618
1619    def test_pythontypes(self):
1620        # check all types defined in Python/
1621        size = test.support.calcobjsize
1622        vsize = test.support.calcvobjsize
1623        check = self.check_sizeof
1624        # _ast.AST
1625        import _ast
1626        check(_ast.AST(), size('P'))
1627        try:
1628            raise TypeError
1629        except TypeError:
1630            tb = sys.exc_info()[2]
1631            # traceback
1632            if tb is not None:
1633                check(tb, size('2P2i'))
1634        # symtable entry
1635        # XXX
1636        # sys.flags
1637        check(sys.flags, vsize('') + self.P * len(sys.flags))
1638
1639    def test_asyncgen_hooks(self):
1640        old = sys.get_asyncgen_hooks()
1641        self.assertIsNone(old.firstiter)
1642        self.assertIsNone(old.finalizer)
1643
1644        firstiter = lambda *a: None
1645        sys.set_asyncgen_hooks(firstiter=firstiter)
1646        hooks = sys.get_asyncgen_hooks()
1647        self.assertIs(hooks.firstiter, firstiter)
1648        self.assertIs(hooks[0], firstiter)
1649        self.assertIs(hooks.finalizer, None)
1650        self.assertIs(hooks[1], None)
1651
1652        finalizer = lambda *a: None
1653        sys.set_asyncgen_hooks(finalizer=finalizer)
1654        hooks = sys.get_asyncgen_hooks()
1655        self.assertIs(hooks.firstiter, firstiter)
1656        self.assertIs(hooks[0], firstiter)
1657        self.assertIs(hooks.finalizer, finalizer)
1658        self.assertIs(hooks[1], finalizer)
1659
1660        sys.set_asyncgen_hooks(*old)
1661        cur = sys.get_asyncgen_hooks()
1662        self.assertIsNone(cur.firstiter)
1663        self.assertIsNone(cur.finalizer)
1664
1665    def test_changing_sys_stderr_and_removing_reference(self):
1666        # If the default displayhook doesn't take a strong reference
1667        # to sys.stderr the following code can crash. See bpo-43660
1668        # for more details.
1669        code = textwrap.dedent('''
1670            import sys
1671            class MyStderr:
1672                def write(self, s):
1673                    sys.stderr = None
1674            sys.stderr = MyStderr()
1675            1/0
1676        ''')
1677        rc, out, err = assert_python_failure('-c', code)
1678        self.assertEqual(out, b"")
1679        self.assertEqual(err, b"")
1680
1681if __name__ == "__main__":
1682    unittest.main()
1683