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