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