test_sys.py 33.2 KB
Newer Older
1
import unittest, test.support
2
import sys, io, os
3
import struct
4 5
import subprocess
import textwrap
6
import warnings
7
import operator
8
import codecs
9

10 11 12 13
# count the number of test runs, used to create unique
# strings to intern in test_intern()
numruns = 0

14 15 16 17
try:
    import threading
except ImportError:
    threading = None
18

19 20
class SysModuleTest(unittest.TestCase):

21 22 23 24 25 26 27 28 29
    def setUp(self):
        self.orig_stdout = sys.stdout
        self.orig_stderr = sys.stderr
        self.orig_displayhook = sys.displayhook

    def tearDown(self):
        sys.stdout = self.orig_stdout
        sys.stderr = self.orig_stderr
        sys.displayhook = self.orig_displayhook
30
        test.support.reap_children()
31

32
    def test_original_displayhook(self):
33
        import builtins
34
        out = io.StringIO()
35 36 37 38 39
        sys.stdout = out

        dh = sys.__displayhook__

        self.assertRaises(TypeError, dh)
40 41
        if hasattr(builtins, "_"):
            del builtins._
42 43 44

        dh(None)
        self.assertEqual(out.getvalue(), "")
45
        self.assertTrue(not hasattr(builtins, "_"))
46 47
        dh(42)
        self.assertEqual(out.getvalue(), "42\n")
48
        self.assertEqual(builtins._, 42)
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

        del sys.stdout
        self.assertRaises(RuntimeError, dh, 42)

    def test_lost_displayhook(self):
        del sys.displayhook
        code = compile("42", "<string>", "single")
        self.assertRaises(RuntimeError, eval, code)

    def test_custom_displayhook(self):
        def baddisplayhook(obj):
            raise ValueError
        sys.displayhook = baddisplayhook
        code = compile("42", "<string>", "single")
        self.assertRaises(ValueError, eval, code)

    def test_original_excepthook(self):
66
        err = io.StringIO()
67 68 69 70 71 72 73
        sys.stderr = err

        eh = sys.__excepthook__

        self.assertRaises(TypeError, eh)
        try:
            raise ValueError(42)
74
        except ValueError as exc:
75 76
            eh(*sys.exc_info())

77
        self.assertTrue(err.getvalue().endswith("ValueError: 42\n"))
78

79 80 81
    def test_excepthook(self):
        with test.support.captured_output("stderr") as stderr:
            sys.excepthook(1, '1', 1)
82
        self.assertTrue("TypeError: print_exception(): Exception expected for " \
83 84
                         "value, str found" in stderr.getvalue())

Walter Dörwald's avatar
Walter Dörwald committed
85
    # FIXME: testing the code for a lost or replaced excepthook in
86 87 88
    # Python/pythonrun.c::PyErr_PrintEx() is tricky.

    def test_exit(self):
89

90 91 92 93 94
        self.assertRaises(TypeError, sys.exit, 42, 42)

        # call without argument
        try:
            sys.exit(0)
95
        except SystemExit as exc:
96
            self.assertEqual(exc.code, 0)
97 98 99 100 101 102 103 104 105
        except:
            self.fail("wrong exception")
        else:
            self.fail("no exception")

        # call with tuple argument with one entry
        # entry will be unpacked
        try:
            sys.exit(42)
106
        except SystemExit as exc:
107
            self.assertEqual(exc.code, 42)
108 109 110 111 112 113 114 115
        except:
            self.fail("wrong exception")
        else:
            self.fail("no exception")

        # call with integer argument
        try:
            sys.exit((42,))
116
        except SystemExit as exc:
117
            self.assertEqual(exc.code, 42)
118 119 120 121 122 123 124 125
        except:
            self.fail("wrong exception")
        else:
            self.fail("no exception")

        # call with string argument
        try:
            sys.exit("exit")
126
        except SystemExit as exc:
127
            self.assertEqual(exc.code, "exit")
128 129 130 131 132 133 134 135
        except:
            self.fail("wrong exception")
        else:
            self.fail("no exception")

        # call with tuple argument with two entries
        try:
            sys.exit((17, 23))
136
        except SystemExit as exc:
137
            self.assertEqual(exc.code, (17, 23))
138 139 140 141 142
        except:
            self.fail("wrong exception")
        else:
            self.fail("no exception")

143 144 145 146
        # test that the exit machinery handles SystemExits properly
        rc = subprocess.call([sys.executable, "-c",
                              "raise SystemExit(47)"])
        self.assertEqual(rc, 47)
Tim Peters's avatar
Tim Peters committed
147

148
        def check_exit_message(code, expected, env=None):
149
            process = subprocess.Popen([sys.executable, "-c", code],
150
                                       stderr=subprocess.PIPE, env=env)
151 152
            stdout, stderr = process.communicate()
            self.assertEqual(process.returncode, 1)
153
            self.assertTrue(stderr.startswith(expected),
154
                "%s doesn't start with %s" % (ascii(stderr), ascii(expected)))
155 156 157 158 159 160 161

        # test that stderr buffer if flushed before the exit message is written
        # into stderr
        check_exit_message(
            r'import sys; sys.stderr.write("unflushed,"); sys.exit("message")',
            b"unflushed,message")

162 163
        # test that the exit message is written with backslashreplace error
        # handler to stderr
164 165 166
        check_exit_message(
            r'import sys; sys.exit("surrogates:\uDCFF")',
            b"surrogates:\\udcff")
167

168 169 170 171 172 173 174 175
        # test that the unicode message is encoded to the stderr encoding
        # instead of the default encoding (utf8)
        env = os.environ.copy()
        env['PYTHONIOENCODING'] = 'latin-1'
        check_exit_message(
            r'import sys; sys.exit("h\xe9")',
            b"h\xe9", env=env)

176
    def test_getdefaultencoding(self):
177 178
        self.assertRaises(TypeError, sys.getdefaultencoding, 42)
        # can't check more than the type, as the user might have changed it
179
        self.assertIsInstance(sys.getdefaultencoding(), str)
180

181 182
    # testing sys.settrace() is done in test_sys_settrace.py
    # testing sys.setprofile() is done in test_sys_setprofile.py
183 184

    def test_setcheckinterval(self):
185 186 187 188 189 190
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            self.assertRaises(TypeError, sys.setcheckinterval)
            orig = sys.getcheckinterval()
            for n in 0, 100, 120, orig: # orig last to restore starting state
                sys.setcheckinterval(n)
191
                self.assertEqual(sys.getcheckinterval(), n)
192

193
    @unittest.skipUnless(threading, 'Threading required for this test.')
Antoine Pitrou's avatar
Antoine Pitrou committed
194 195 196 197 198 199 200 201 202 203 204
    def test_switchinterval(self):
        self.assertRaises(TypeError, sys.setswitchinterval)
        self.assertRaises(TypeError, sys.setswitchinterval, "a")
        self.assertRaises(ValueError, sys.setswitchinterval, -1.0)
        self.assertRaises(ValueError, sys.setswitchinterval, 0.0)
        orig = sys.getswitchinterval()
        # sanity check
        self.assertTrue(orig < 0.5, orig)
        try:
            for n in 0.00001, 0.05, 3.0, orig:
                sys.setswitchinterval(n)
205
                self.assertAlmostEqual(sys.getswitchinterval(), n)
Antoine Pitrou's avatar
Antoine Pitrou committed
206 207 208
        finally:
            sys.setswitchinterval(orig)

209
    def test_recursionlimit(self):
Tim Peters's avatar
Tim Peters committed
210 211 212 213 214 215 216
        self.assertRaises(TypeError, sys.getrecursionlimit, 42)
        oldlimit = sys.getrecursionlimit()
        self.assertRaises(TypeError, sys.setrecursionlimit)
        self.assertRaises(ValueError, sys.setrecursionlimit, -42)
        sys.setrecursionlimit(10000)
        self.assertEqual(sys.getrecursionlimit(), 10000)
        sys.setrecursionlimit(oldlimit)
217

218 219
    @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
                     'fatal error if run with a trace function')
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    def test_recursionlimit_recovery(self):
        # NOTE: this test is slightly fragile in that it depends on the current
        # recursion count when executing the test being low enough so as to
        # trigger the recursion recovery detection in the _Py_MakeEndRecCheck
        # macro (see ceval.h).
        oldlimit = sys.getrecursionlimit()
        def f():
            f()
        try:
            for i in (50, 1000):
                # Issue #5392: stack overflow after hitting recursion limit twice
                sys.setrecursionlimit(i)
                self.assertRaises(RuntimeError, f)
                self.assertRaises(RuntimeError, f)
        finally:
            sys.setrecursionlimit(oldlimit)

    def test_recursionlimit_fatalerror(self):
        # A fatal error occurs if a second recursion limit is hit when recovering
        # from a first one.
        code = textwrap.dedent("""
            import sys

            def f():
                try:
                    f()
                except RuntimeError:
                    f()

            sys.setrecursionlimit(%d)
            f()""")
251 252 253 254 255 256 257 258 259
        with test.support.suppress_crash_popup():
            for i in (50, 1000):
                sub = subprocess.Popen([sys.executable, '-c', code % i],
                    stderr=subprocess.PIPE)
                err = sub.communicate()[1]
                self.assertTrue(sub.returncode, sub.returncode)
                self.assertIn(
                    b"Fatal Python error: Cannot recover from stack overflow",
                    err)
260

261
    def test_getwindowsversion(self):
262
        # Raise SkipTest if sys doesn't have getwindowsversion attribute
263
        test.support.get_attribute(sys, "getwindowsversion")
264
        v = sys.getwindowsversion()
Brian Curtin's avatar
Brian Curtin committed
265
        self.assertEqual(len(v), 5)
266 267 268 269 270 271 272 273 274 275 276
        self.assertIsInstance(v[0], int)
        self.assertIsInstance(v[1], int)
        self.assertIsInstance(v[2], int)
        self.assertIsInstance(v[3], int)
        self.assertIsInstance(v[4], str)
        self.assertRaises(IndexError, operator.getitem, v, 5)
        self.assertIsInstance(v.major, int)
        self.assertIsInstance(v.minor, int)
        self.assertIsInstance(v.build, int)
        self.assertIsInstance(v.platform, int)
        self.assertIsInstance(v.service_pack, str)
277 278 279 280
        self.assertIsInstance(v.service_pack_minor, int)
        self.assertIsInstance(v.service_pack_major, int)
        self.assertIsInstance(v.suite_mask, int)
        self.assertIsInstance(v.product_type, int)
281 282 283 284 285 286 287 288 289
        self.assertEqual(v[0], v.major)
        self.assertEqual(v[1], v.minor)
        self.assertEqual(v[2], v.build)
        self.assertEqual(v[3], v.platform)
        self.assertEqual(v[4], v.service_pack)

        # This is how platform.py calls it. Make sure tuple
        #  still has 5 elements
        maj, min, buildno, plat, csd = sys.getwindowsversion()
290

291 292 293
    def test_call_tracing(self):
        self.assertRaises(TypeError, sys.call_tracing, type, 2)

294 295
    def test_dlopenflags(self):
        if hasattr(sys, "setdlopenflags"):
296
            self.assertTrue(hasattr(sys, "getdlopenflags"))
297 298 299 300 301 302 303
            self.assertRaises(TypeError, sys.getdlopenflags, 42)
            oldflags = sys.getdlopenflags()
            self.assertRaises(TypeError, sys.setdlopenflags)
            sys.setdlopenflags(oldflags+1)
            self.assertEqual(sys.getdlopenflags(), oldflags+1)
            sys.setdlopenflags(oldflags)

304
    @test.support.refcount_test
305
    def test_refcount(self):
Benjamin Peterson's avatar
Benjamin Peterson committed
306 307 308 309 310
        # n here must be a global in order for this test to pass while
        # tracing with a python function.  Tracing calls PyFrame_FastToLocals
        # which will add a copy of any locals to the frame object, causing
        # the reference count to increase by 2 instead of 1.
        global n
311 312 313 314 315 316 317
        self.assertRaises(TypeError, sys.getrefcount)
        c = sys.getrefcount(None)
        n = None
        self.assertEqual(sys.getrefcount(None), c+1)
        del n
        self.assertEqual(sys.getrefcount(None), c)
        if hasattr(sys, "gettotalrefcount"):
318
            self.assertIsInstance(sys.gettotalrefcount(), int)
319 320 321

    def test_getframe(self):
        self.assertRaises(TypeError, sys._getframe, 42, 42)
322
        self.assertRaises(ValueError, sys._getframe, 2000000000)
323
        self.assertTrue(
324
            SysModuleTest.test_getframe.__code__ \
325 326 327
            is sys._getframe().f_code
        )

328 329 330 331
    # sys._current_frames() is a CPython-only gimmick.
    def test_current_frames(self):
        have_threads = True
        try:
332
            import _thread
333 334 335 336 337 338 339 340 341
        except ImportError:
            have_threads = False

        if have_threads:
            self.current_frames_with_threads()
        else:
            self.current_frames_without_threads()

    # Test sys._current_frames() in a WITH_THREADS build.
342
    @test.support.reap_threads
343
    def current_frames_with_threads(self):
344
        import threading
345 346 347 348 349 350 351 352 353 354 355 356 357
        import traceback

        # Spawn a thread that blocks at a known place.  Then the main
        # thread does sys._current_frames(), and verifies that the frames
        # returned make sense.
        entered_g = threading.Event()
        leave_g = threading.Event()
        thread_info = []  # the thread's id

        def f123():
            g456()

        def g456():
358
            thread_info.append(threading.get_ident())
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
            entered_g.set()
            leave_g.wait()

        t = threading.Thread(target=f123)
        t.start()
        entered_g.wait()

        # At this point, t has finished its entered_g.set(), although it's
        # impossible to guess whether it's still on that line or has moved on
        # to its leave_g.wait().
        self.assertEqual(len(thread_info), 1)
        thread_id = thread_info[0]

        d = sys._current_frames()

374
        main_id = threading.get_ident()
375 376
        self.assertIn(main_id, d)
        self.assertIn(thread_id, d)
377 378 379

        # Verify that the captured main-thread frame is _this_ frame.
        frame = d.pop(main_id)
380
        self.assertTrue(frame is sys._getframe())
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397

        # Verify that the captured thread frame is blocked in g456, called
        # from f123.  This is a litte tricky, since various bits of
        # threading.py are also in the thread's call stack.
        frame = d.pop(thread_id)
        stack = traceback.extract_stack(frame)
        for i, (filename, lineno, funcname, sourceline) in enumerate(stack):
            if funcname == "f123":
                break
        else:
            self.fail("didn't find f123() on thread's call stack")

        self.assertEqual(sourceline, "g456()")

        # And the next record must be for g456().
        filename, lineno, funcname, sourceline = stack[i+1]
        self.assertEqual(funcname, "g456")
398
        self.assertIn(sourceline, ["leave_g.wait()", "entered_g.set()"])
399 400 401 402 403 404 405 406 407 408 409

        # Reap the spawned thread.
        leave_g.set()
        t.join()

    # Test sys._current_frames() when thread support doesn't exist.
    def current_frames_without_threads(self):
        # Not much happens here:  there is only one thread, with artificial
        # "thread id" 0.
        d = sys._current_frames()
        self.assertEqual(len(d), 1)
410
        self.assertIn(0, d)
411
        self.assertTrue(d[0] is sys._getframe())
412

413
    def test_attributes(self):
414 415
        self.assertIsInstance(sys.api_version, int)
        self.assertIsInstance(sys.argv, list)
416
        self.assertIn(sys.byteorder, ("little", "big"))
417 418 419
        self.assertIsInstance(sys.builtin_module_names, tuple)
        self.assertIsInstance(sys.copyright, str)
        self.assertIsInstance(sys.exec_prefix, str)
420
        self.assertIsInstance(sys.base_exec_prefix, str)
421
        self.assertIsInstance(sys.executable, str)
422
        self.assertEqual(len(sys.float_info), 11)
423
        self.assertEqual(sys.float_info.radix, 2)
424
        self.assertEqual(len(sys.int_info), 2)
425 426
        self.assertTrue(sys.int_info.bits_per_digit % 5 == 0)
        self.assertTrue(sys.int_info.sizeof_digit >= 1)
Benjamin Peterson's avatar
Benjamin Peterson committed
427 428
        self.assertEqual(type(sys.int_info.bits_per_digit), int)
        self.assertEqual(type(sys.int_info.sizeof_digit), int)
429
        self.assertIsInstance(sys.hexversion, int)
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

        self.assertEqual(len(sys.hash_info), 5)
        self.assertLess(sys.hash_info.modulus, 2**sys.hash_info.width)
        # sys.hash_info.modulus should be a prime; we do a quick
        # probable primality test (doesn't exclude the possibility of
        # a Carmichael number)
        for x in range(1, 100):
            self.assertEqual(
                pow(x, sys.hash_info.modulus-1, sys.hash_info.modulus),
                1,
                "sys.hash_info.modulus {} is a non-prime".format(
                    sys.hash_info.modulus)
                )
        self.assertIsInstance(sys.hash_info.inf, int)
        self.assertIsInstance(sys.hash_info.nan, int)
        self.assertIsInstance(sys.hash_info.imag, int)

447 448
        self.assertIsInstance(sys.maxsize, int)
        self.assertIsInstance(sys.maxunicode, int)
449
        self.assertEqual(sys.maxunicode, 0x10FFFF)
450 451
        self.assertIsInstance(sys.platform, str)
        self.assertIsInstance(sys.prefix, str)
452
        self.assertIsInstance(sys.base_prefix, str)
453
        self.assertIsInstance(sys.version, str)
454
        vi = sys.version_info
455
        self.assertIsInstance(vi[:], tuple)
456
        self.assertEqual(len(vi), 5)
457 458 459
        self.assertIsInstance(vi[0], int)
        self.assertIsInstance(vi[1], int)
        self.assertIsInstance(vi[2], int)
460
        self.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
461 462 463 464
        self.assertIsInstance(vi[4], int)
        self.assertIsInstance(vi.major, int)
        self.assertIsInstance(vi.minor, int)
        self.assertIsInstance(vi.micro, int)
465
        self.assertIn(vi.releaselevel, ("alpha", "beta", "candidate", "final"))
466
        self.assertIsInstance(vi.serial, int)
467 468 469 470 471
        self.assertEqual(vi[0], vi.major)
        self.assertEqual(vi[1], vi.minor)
        self.assertEqual(vi[2], vi.micro)
        self.assertEqual(vi[3], vi.releaselevel)
        self.assertEqual(vi[4], vi.serial)
472
        self.assertTrue(vi > (1,0,0))
473
        self.assertIsInstance(sys.float_repr_style, str)
474
        self.assertIn(sys.float_repr_style, ('short', 'legacy'))
475 476
        if not sys.platform.startswith('win'):
            self.assertIsInstance(sys.abiflags, str)
477

478 479 480 481
    @unittest.skipUnless(hasattr(sys, 'thread_info'),
                         'Threading required for this test.')
    def test_thread_info(self):
        info = sys.thread_info
Ezio Melotti's avatar
Ezio Melotti committed
482
        self.assertEqual(len(info), 3)
483 484 485
        self.assertIn(info.name, ('nt', 'os2', 'pthread', 'solaris', None))
        self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))

486
    def test_43581(self):
487
        # Can't use sys.stdout, as this is a StringIO object when
488
        # the test runs under regrtest.
489
        self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding)
490

491
    def test_intern(self):
492 493
        global numruns
        numruns += 1
494
        self.assertRaises(TypeError, sys.intern)
495
        s = "never interned before" + str(numruns)
496
        self.assertTrue(sys.intern(s) is s)
497
        s2 = s.swapcase().swapcase()
498
        self.assertTrue(sys.intern(s2) is s)
499 500 501 502 503

        # Subclasses of string can't be interned, because they
        # provide too much opportunity for insane things to happen.
        # We don't want them in the interned dict and if they aren't
        # actually interned, we don't want to create the appearance
504
        # that they are by allowing intern() to succeed.
505
        class S(str):
506 507 508
            def __hash__(self):
                return 123

509
        self.assertRaises(TypeError, sys.intern, S("abc"))
510

511
    def test_sys_flags(self):
512
        self.assertTrue(sys.flags)
513
        attrs = ("debug",
514
                 "inspect", "interactive", "optimize", "dont_write_bytecode",
515
                 "no_user_site", "no_site", "ignore_environment", "verbose",
516
                 "bytes_warning", "quiet", "hash_randomization")
517
        for attr in attrs:
518
            self.assertTrue(hasattr(sys.flags, attr), attr)
519
            self.assertEqual(type(getattr(sys.flags, attr)), int, attr)
520
        self.assertTrue(repr(sys.flags))
521
        self.assertEqual(len(sys.flags), len(attrs))
522

Christian Heimes's avatar
Christian Heimes committed
523 524 525
    def test_clear_type_cache(self):
        sys._clear_type_cache()

526 527 528 529 530 531 532 533 534
    def test_ioencoding(self):
        env = dict(os.environ)

        # Test character: cent sign, encoded as 0x4A (ASCII J) in CP424,
        # not representable in ASCII.

        env["PYTHONIOENCODING"] = "cp424"
        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
                             stdout = subprocess.PIPE, env=env)
535
        out = p.communicate()[0].strip()
536 537
        expected = ("\xa2" + os.linesep).encode("cp424")
        self.assertEqual(out, expected)
538 539 540 541

        env["PYTHONIOENCODING"] = "ascii:replace"
        p = subprocess.Popen([sys.executable, "-c", 'print(chr(0xa2))'],
                             stdout = subprocess.PIPE, env=env)
542
        out = p.communicate()[0].strip()
543 544
        self.assertEqual(out, b'?')

545 546
    @unittest.skipIf(sys.base_prefix != sys.prefix,
                     'Test is not venv-compatible')
547
    def test_executable(self):
548 549 550
        # sys.executable should be absolute
        self.assertEqual(os.path.abspath(sys.executable), sys.executable)

551 552 553
        # Issue #7774: Ensure that sys.executable is an empty string if argv[0]
        # has been set to an non existent program name and Python is unable to
        # retrieve the real program name
Florent Xicluna's avatar
Florent Xicluna committed
554

555 556 557 558 559 560 561 562 563 564 565 566
        # For a normal installation, it should work without 'cwd'
        # argument. For test runs in the build directory, see #7774.
        python_dir = os.path.dirname(os.path.realpath(sys.executable))
        p = subprocess.Popen(
            ["nonexistent", "-c",
             'import sys; print(sys.executable.encode("ascii", "backslashreplace"))'],
            executable=sys.executable, stdout=subprocess.PIPE, cwd=python_dir)
        stdout = p.communicate()[0]
        executable = stdout.strip().decode("ASCII")
        p.wait()
        self.assertIn(executable, ["b''", repr(sys.executable.encode("ascii", "backslashreplace"))])

567 568 569 570 571
    def check_fsencoding(self, fs_encoding, expected=None):
        self.assertIsNotNone(fs_encoding)
        codecs.lookup(fs_encoding)
        if expected:
            self.assertEqual(fs_encoding, expected)
572

573
    def test_getfilesystemencoding(self):
574
        fs_encoding = sys.getfilesystemencoding()
575 576 577 578 579 580
        if sys.platform == 'darwin':
            expected = 'utf-8'
        elif sys.platform == 'win32':
            expected = 'mbcs'
        else:
            expected = None
581
        self.check_fsencoding(fs_encoding, expected)
582

583 584 585
    def test_implementation(self):
        # This test applies to all implementations equally.

586
        levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'final': 0xF}
587 588 589 590 591 592 593 594 595 596 597 598 599 600

        self.assertTrue(hasattr(sys.implementation, 'name'))
        self.assertTrue(hasattr(sys.implementation, 'version'))
        self.assertTrue(hasattr(sys.implementation, 'hexversion'))
        self.assertTrue(hasattr(sys.implementation, 'cache_tag'))

        version = sys.implementation.version
        self.assertEqual(version[:2], (version.major, version.minor))

        hexversion = (version.major << 24 | version.minor << 16 |
                      version.micro << 8 | levels[version.releaselevel] << 4 |
                      version.serial << 0)
        self.assertEqual(sys.implementation.hexversion, hexversion)

601
        # PEP 421 requires that .name be lower case.
Barry Warsaw's avatar
Barry Warsaw committed
602
        self.assertEqual(sys.implementation.name,
603 604
                         sys.implementation.name.lower())

605 606 607 608 609 610
    def test_debugmallocstats(self):
        # Test sys._debugmallocstats()
        from test.script_helper import assert_python_ok
        args = ['-c', 'import sys; sys._debugmallocstats()']
        ret, out, err = assert_python_ok(*args)
        self.assertIn(b"free PyDictObjects", err)
611

612 613 614
class SizeofTest(unittest.TestCase):

    def setUp(self):
615
        self.P = struct.calcsize('P')
616
        self.longdigit = sys.int_info.sizeof_digit
617 618
        import _testcapi
        self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD
619 620 621 622 623 624
        self.file = open(test.support.TESTFN, 'wb')

    def tearDown(self):
        self.file.close()
        test.support.unlink(test.support.TESTFN)

625
    check_sizeof = test.support.check_sizeof
626

627 628
    def test_gc_head_size(self):
        # Check that the gc header size is added to objects tracked by the gc.
629
        vsize = test.support.calcvobjsize
630 631
        gc_header_size = self.gc_headsize
        # bool objects are not gc tracked
632
        self.assertEqual(sys.getsizeof(True), vsize('') + self.longdigit)
633
        # but lists are
634
        self.assertEqual(sys.getsizeof([]), vsize('Pn') + gc_header_size)
635 636

    def test_default(self):
637 638 639
        size = test.support.calcvobjsize
        self.assertEqual(sys.getsizeof(True), size('') + self.longdigit)
        self.assertEqual(sys.getsizeof(True, -1), size('') + self.longdigit)
640 641 642

    def test_objecttypes(self):
        # check all types defined in Objects/
643 644
        size = test.support.calcobjsize
        vsize = test.support.calcvobjsize
645 646
        check = self.check_sizeof
        # bool
647
        check(True, vsize('') + self.longdigit)
648 649 650
        # buffer
        # XXX
        # builtin_function_or_method
651
        check(len, size('3P')) # XXX check layout
652 653 654 655
        # bytearray
        samples = [b'', b'u'*100000]
        for sample in samples:
            x = bytearray(sample)
656
            check(x, vsize('inP') + x.__alloc__())
657
        # bytearray_iterator
658
        check(iter(bytearray()), size('nP'))
659 660 661 662 663 664
        # cell
        def get_cell():
            x = 42
            def inner():
                return x
            return inner
665
        check(get_cell().__closure__[0], size('P'))
666
        # code
667 668
        check(get_cell().__code__, size('5i9Pi3P'))
        check(get_cell.__code__, size('5i9Pi3P'))
669 670 671 672
        def get_cell2(x):
            def inner():
                return x
            return inner
673
        check(get_cell2.__code__, size('5i9Pi3P') + 1)
674
        # complex
675
        check(complex(0,1), size('2d'))
676
        # method_descriptor (descriptor object)
677
        check(str.lower, size('3PP'))
678 679 680 681
        # classmethod_descriptor (descriptor object)
        # XXX
        # member_descriptor (descriptor object)
        import datetime
682
        check(datetime.timedelta.days, size('3PP'))
683 684
        # getset_descriptor (descriptor object)
        import collections
685
        check(collections.defaultdict.default_factory, size('3PP'))
686
        # wrapper_descriptor (descriptor object)
687
        check(int.__add__, size('3P2P'))
688
        # method-wrapper (descriptor object)
689
        check({}.__iter__, size('2P'))
690
        # dict
691
        check({}, size('n2P' + '2nPn' + 8*'n2P'))
692
        longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
693
        check(longdict, size('n2P' + '2nPn') + 16*struct.calcsize('n2P'))
694
        # dictionary-keyiterator
695
        check({}.keys(), size('P'))
696
        # dictionary-valueiterator
697
        check({}.values(), size('P'))
698
        # dictionary-itemiterator
699 700
        check({}.items(), size('P'))
        # dictionary iterator
701
        check(iter({}), size('P2nPn'))
702 703
        # dictproxy
        class C(object): pass
704
        check(C.__dict__, size('P'))
705
        # BaseException
706
        check(BaseException(), size('5Pi'))
707
        # UnicodeEncodeError
708
        check(UnicodeEncodeError("", "", 0, 0, ""), size('5Pi 2P2nP'))
709
        # UnicodeDecodeError
710
        check(UnicodeDecodeError("", b"", 0, 0, ""), size('5Pi 2P2nP'))
711
        # UnicodeTranslateError
712
        check(UnicodeTranslateError("", 0, 1, ""), size('5Pi 2P2nP'))
713
        # ellipses
714
        check(Ellipsis, size(''))
715 716 717
        # EncodingMap
        import codecs, encodings.iso8859_3
        x = codecs.charmap_build(encodings.iso8859_3.decoding_table)
718
        check(x, size('32B2iB'))
719
        # enumerate
720
        check(enumerate([]), size('n3P'))
721
        # reverse
722
        check(reversed(''), size('nP'))
723
        # float
724
        check(float(0), size('d'))
725
        # sys.floatinfo
726
        check(sys.float_info, vsize('') + self.P * len(sys.float_info))
727 728 729 730 731 732 733 734
        # frame
        import inspect
        CO_MAXBLOCKS = 20
        x = inspect.currentframe()
        ncells = len(x.f_code.co_cellvars)
        nfrees = len(x.f_code.co_freevars)
        extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
                  ncells + nfrees - 1
735
        check(x, vsize('12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
736 737
        # function
        def func(): pass
738
        check(func, size('12P'))
739 740 741 742 743 744 745 746
        class c():
            @staticmethod
            def foo():
                pass
            @classmethod
            def bar(cls):
                pass
            # staticmethod
747
            check(foo, size('PP'))
748
            # classmethod
749
            check(bar, size('PP'))
750 751
        # generator
        def get_gen(): yield 1
752
        check(get_gen(), size('Pb2P'))
753
        # iterator
754
        check(iter('abc'), size('lP'))
755 756
        # callable-iterator
        import re
757
        check(re.finditer('',''), size('2P'))
758 759 760
        # list
        samples = [[], [1,2,3], ['1', '2', '3']]
        for sample in samples:
761
            check(sample, vsize('Pn') + len(sample)*self.P)
762 763 764 765 766
        # sortwrapper (list)
        # XXX
        # cmpwrapper (list)
        # XXX
        # listiterator (list)
767
        check(iter([]), size('lP'))
768
        # listreverseiterator (list)
769
        check(reversed([]), size('nP'))
770
        # long
771 772 773
        check(0, vsize(''))
        check(1, vsize('') + self.longdigit)
        check(-1, vsize('') + self.longdigit)
774
        PyLong_BASE = 2**sys.int_info.bits_per_digit
775 776 777
        check(int(PyLong_BASE), vsize('') + 2*self.longdigit)
        check(int(PyLong_BASE**2-1), vsize('') + 2*self.longdigit)
        check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit)
778
        # memoryview
779
        check(memoryview(b''), size('Pnin 2P2n2i5P 3cPn'))
780
        # module
781
        check(unittest, size('PnP'))
782
        # None
783
        check(None, size(''))
784
        # NotImplementedType
785
        check(NotImplemented, size(''))
786
        # object
787
        check(object(), size(''))
788 789 790 791 792 793
        # property (descriptor object)
        class C(object):
            def getx(self): return self.__x
            def setx(self, value): self.__x = value
            def delx(self): del self.__x
            x = property(getx, setx, delx, "")
794
            check(x, size('4Pi'))
795
        # PyCapsule
796 797
        # XXX
        # rangeiterator
798
        check(iter(range(1)), size('4l'))
799
        # reverse
800
        check(reversed(''), size('nP'))
801
        # range
802 803
        check(range(1), size('4P'))
        check(range(66000), size('4P'))
804 805 806 807
        # set
        # frozenset
        PySet_MINSIZE = 8
        samples = [[], range(10), range(50)]
808
        s = size('3n2P' + PySet_MINSIZE*'nP' + 'nP')
809 810 811 812 813 814 815 816 817 818 819 820 821
        for sample in samples:
            minused = len(sample)
            if minused == 0: tmp = 1
            # the computation of minused is actually a bit more complicated
            # but this suffices for the sizeof test
            minused = minused*2
            newsize = PySet_MINSIZE
            while newsize <= minused:
                newsize = newsize << 1
            if newsize <= 8:
                check(set(sample), s)
                check(frozenset(sample), s)
            else:
822 823
                check(set(sample), s + newsize*struct.calcsize('nP'))
                check(frozenset(sample), s + newsize*struct.calcsize('nP'))
824
        # setiterator
825
        check(iter(set()), size('P3n'))
826
        # slice
827
        check(slice(0), size('3P'))
828
        # super
829
        check(super(int), size('3P'))
830
        # tuple
831 832
        check((), vsize(''))
        check((1,2,3), vsize('') + 3*self.P)
833
        # type
834
        # static type: PyTypeObject
835
        s = vsize('P2n15Pl4Pn9Pn11PI')
836
        check(int, s)
837 838
        # (PyTypeObject + PyNumberMethods + PyMappingMethods +
        #  PySequenceMethods + PyBufferProcs + 4P)
839
        s = vsize('P2n15Pl4Pn9Pn11PI') + struct.calcsize('34P 3P 10P 2P 4P')
840
        # Separate block for PyDictKeysObject with 4 entries
841
        s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P")
842 843 844
        # class
        class newstyleclass(object): pass
        check(newstyleclass, s)
845
        # dict with shared keys
846
        check(newstyleclass().__dict__, size('n2P' + '2nPn'))
Georg Brandl's avatar
Georg Brandl committed
847
        # unicode
Martin v. Löwis's avatar
Martin v. Löwis committed
848 849 850 851 852 853
        # each tuple contains a string and its expected character size
        # don't put any static strings here, as they may contain
        # wchar_t or UTF-8 representations
        samples = ['1'*100, '\xff'*50,
                   '\u0100'*40, '\uffff'*100,
                   '\U00010000'*30, '\U0010ffff'*100]
854 855
        asciifields = "nniP"
        compactfields = asciifields + "nPn"
Martin v. Löwis's avatar
Martin v. Löwis committed
856
        unicodefields = compactfields + "P"
Georg Brandl's avatar
Georg Brandl committed
857
        for s in samples:
Martin v. Löwis's avatar
Martin v. Löwis committed
858 859 860 861 862 863 864 865 866 867 868 869 870
            maxchar = ord(max(s))
            if maxchar < 128:
                L = size(asciifields) + len(s) + 1
            elif maxchar < 256:
                L = size(compactfields) + len(s) + 1
            elif maxchar < 65536:
                L = size(compactfields) + 2*(len(s) + 1)
            else:
                L = size(compactfields) + 4*(len(s) + 1)
            check(s, L)
        # verify that the UTF-8 size is accounted for
        s = chr(0x4000)   # 4 bytes canonical representation
        check(s, size(compactfields) + 4)
871 872 873 874
        # compile() will trigger the generation of the UTF-8
        # representation as a side effect
        compile(s, "<stdin>", "eval")
        check(s, size(compactfields) + 4 + 4)
Martin v. Löwis's avatar
Martin v. Löwis committed
875 876
        # TODO: add check that forces the presence of wchar_t representation
        # TODO: add check that forces layout of unicodefields
877 878
        # weakref
        import weakref
879
        check(weakref.ref(int), size('2Pn2P'))
880 881 882
        # weakproxy
        # XXX
        # weakcallableproxy
883
        check(weakref.proxy(int), size('2Pn2P'))
884 885 886

    def test_pythontypes(self):
        # check all types defined in Python/
887 888
        size = test.support.calcobjsize
        vsize = test.support.calcvobjsize
889 890 891
        check = self.check_sizeof
        # _ast.AST
        import _ast
892
        check(_ast.AST(), size('P'))
893 894 895 896 897
        try:
            raise TypeError
        except TypeError:
            tb = sys.exc_info()[2]
            # traceback
898
            if tb is not None:
899
                check(tb, size('2P2i'))
900 901 902
        # symtable entry
        # XXX
        # sys.flags
903
        check(sys.flags, vsize('') + self.P * len(sys.flags))
904 905


906
def test_main():
907
    test.support.run_unittest(SysModuleTest, SizeofTest)
908 909 910

if __name__ == "__main__":
    test_main()