| 1 | n/a | # Run the _testcapi module tests (tests for the Python/C API): by defn, |
|---|
| 2 | n/a | # these are all functions _testcapi exports whose name begins with 'test_'. |
|---|
| 3 | n/a | |
|---|
| 4 | n/a | import os |
|---|
| 5 | n/a | import pickle |
|---|
| 6 | n/a | import random |
|---|
| 7 | n/a | import re |
|---|
| 8 | n/a | import subprocess |
|---|
| 9 | n/a | import sys |
|---|
| 10 | n/a | import sysconfig |
|---|
| 11 | n/a | import textwrap |
|---|
| 12 | n/a | import time |
|---|
| 13 | n/a | import unittest |
|---|
| 14 | n/a | from test import support |
|---|
| 15 | n/a | from test.support import MISSING_C_DOCSTRINGS |
|---|
| 16 | n/a | from test.support.script_helper import assert_python_failure |
|---|
| 17 | n/a | try: |
|---|
| 18 | n/a | import _posixsubprocess |
|---|
| 19 | n/a | except ImportError: |
|---|
| 20 | n/a | _posixsubprocess = None |
|---|
| 21 | n/a | try: |
|---|
| 22 | n/a | import threading |
|---|
| 23 | n/a | except ImportError: |
|---|
| 24 | n/a | threading = None |
|---|
| 25 | n/a | # Skip this test if the _testcapi module isn't available. |
|---|
| 26 | n/a | _testcapi = support.import_module('_testcapi') |
|---|
| 27 | n/a | |
|---|
| 28 | n/a | # Were we compiled --with-pydebug or with #define Py_DEBUG? |
|---|
| 29 | n/a | Py_DEBUG = hasattr(sys, 'gettotalrefcount') |
|---|
| 30 | n/a | |
|---|
| 31 | n/a | |
|---|
| 32 | n/a | def testfunction(self): |
|---|
| 33 | n/a | """some doc""" |
|---|
| 34 | n/a | return self |
|---|
| 35 | n/a | |
|---|
| 36 | n/a | class InstanceMethod: |
|---|
| 37 | n/a | id = _testcapi.instancemethod(id) |
|---|
| 38 | n/a | testfunction = _testcapi.instancemethod(testfunction) |
|---|
| 39 | n/a | |
|---|
| 40 | n/a | class CAPITest(unittest.TestCase): |
|---|
| 41 | n/a | |
|---|
| 42 | n/a | def test_instancemethod(self): |
|---|
| 43 | n/a | inst = InstanceMethod() |
|---|
| 44 | n/a | self.assertEqual(id(inst), inst.id()) |
|---|
| 45 | n/a | self.assertTrue(inst.testfunction() is inst) |
|---|
| 46 | n/a | self.assertEqual(inst.testfunction.__doc__, testfunction.__doc__) |
|---|
| 47 | n/a | self.assertEqual(InstanceMethod.testfunction.__doc__, testfunction.__doc__) |
|---|
| 48 | n/a | |
|---|
| 49 | n/a | InstanceMethod.testfunction.attribute = "test" |
|---|
| 50 | n/a | self.assertEqual(testfunction.attribute, "test") |
|---|
| 51 | n/a | self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") |
|---|
| 52 | n/a | |
|---|
| 53 | n/a | @unittest.skipUnless(threading, 'Threading required for this test.') |
|---|
| 54 | n/a | def test_no_FatalError_infinite_loop(self): |
|---|
| 55 | n/a | with support.SuppressCrashReport(): |
|---|
| 56 | n/a | p = subprocess.Popen([sys.executable, "-c", |
|---|
| 57 | n/a | 'import _testcapi;' |
|---|
| 58 | n/a | '_testcapi.crash_no_current_thread()'], |
|---|
| 59 | n/a | stdout=subprocess.PIPE, |
|---|
| 60 | n/a | stderr=subprocess.PIPE) |
|---|
| 61 | n/a | (out, err) = p.communicate() |
|---|
| 62 | n/a | self.assertEqual(out, b'') |
|---|
| 63 | n/a | # This used to cause an infinite loop. |
|---|
| 64 | n/a | self.assertTrue(err.rstrip().startswith( |
|---|
| 65 | n/a | b'Fatal Python error:' |
|---|
| 66 | n/a | b' PyThreadState_Get: no current thread')) |
|---|
| 67 | n/a | |
|---|
| 68 | n/a | def test_memoryview_from_NULL_pointer(self): |
|---|
| 69 | n/a | self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) |
|---|
| 70 | n/a | |
|---|
| 71 | n/a | def test_exc_info(self): |
|---|
| 72 | n/a | raised_exception = ValueError("5") |
|---|
| 73 | n/a | new_exc = TypeError("TEST") |
|---|
| 74 | n/a | try: |
|---|
| 75 | n/a | raise raised_exception |
|---|
| 76 | n/a | except ValueError as e: |
|---|
| 77 | n/a | tb = e.__traceback__ |
|---|
| 78 | n/a | orig_sys_exc_info = sys.exc_info() |
|---|
| 79 | n/a | orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) |
|---|
| 80 | n/a | new_sys_exc_info = sys.exc_info() |
|---|
| 81 | n/a | new_exc_info = _testcapi.set_exc_info(*orig_exc_info) |
|---|
| 82 | n/a | reset_sys_exc_info = sys.exc_info() |
|---|
| 83 | n/a | |
|---|
| 84 | n/a | self.assertEqual(orig_exc_info[1], e) |
|---|
| 85 | n/a | |
|---|
| 86 | n/a | self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) |
|---|
| 87 | n/a | self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) |
|---|
| 88 | n/a | self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) |
|---|
| 89 | n/a | self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) |
|---|
| 90 | n/a | self.assertSequenceEqual(new_sys_exc_info, new_exc_info) |
|---|
| 91 | n/a | else: |
|---|
| 92 | n/a | self.assertTrue(False) |
|---|
| 93 | n/a | |
|---|
| 94 | n/a | @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') |
|---|
| 95 | n/a | def test_seq_bytes_to_charp_array(self): |
|---|
| 96 | n/a | # Issue #15732: crash in _PySequence_BytesToCharpArray() |
|---|
| 97 | n/a | class Z(object): |
|---|
| 98 | n/a | def __len__(self): |
|---|
| 99 | n/a | return 1 |
|---|
| 100 | n/a | self.assertRaises(TypeError, _posixsubprocess.fork_exec, |
|---|
| 101 | n/a | 1,Z(),3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17) |
|---|
| 102 | n/a | # Issue #15736: overflow in _PySequence_BytesToCharpArray() |
|---|
| 103 | n/a | class Z(object): |
|---|
| 104 | n/a | def __len__(self): |
|---|
| 105 | n/a | return sys.maxsize |
|---|
| 106 | n/a | def __getitem__(self, i): |
|---|
| 107 | n/a | return b'x' |
|---|
| 108 | n/a | self.assertRaises(MemoryError, _posixsubprocess.fork_exec, |
|---|
| 109 | n/a | 1,Z(),3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17) |
|---|
| 110 | n/a | |
|---|
| 111 | n/a | @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') |
|---|
| 112 | n/a | def test_subprocess_fork_exec(self): |
|---|
| 113 | n/a | class Z(object): |
|---|
| 114 | n/a | def __len__(self): |
|---|
| 115 | n/a | return 1 |
|---|
| 116 | n/a | |
|---|
| 117 | n/a | # Issue #15738: crash in subprocess_fork_exec() |
|---|
| 118 | n/a | self.assertRaises(TypeError, _posixsubprocess.fork_exec, |
|---|
| 119 | n/a | Z(),[b'1'],3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17) |
|---|
| 120 | n/a | |
|---|
| 121 | n/a | @unittest.skipIf(MISSING_C_DOCSTRINGS, |
|---|
| 122 | n/a | "Signature information for builtins requires docstrings") |
|---|
| 123 | n/a | def test_docstring_signature_parsing(self): |
|---|
| 124 | n/a | |
|---|
| 125 | n/a | self.assertEqual(_testcapi.no_docstring.__doc__, None) |
|---|
| 126 | n/a | self.assertEqual(_testcapi.no_docstring.__text_signature__, None) |
|---|
| 127 | n/a | |
|---|
| 128 | n/a | self.assertEqual(_testcapi.docstring_empty.__doc__, None) |
|---|
| 129 | n/a | self.assertEqual(_testcapi.docstring_empty.__text_signature__, None) |
|---|
| 130 | n/a | |
|---|
| 131 | n/a | self.assertEqual(_testcapi.docstring_no_signature.__doc__, |
|---|
| 132 | n/a | "This docstring has no signature.") |
|---|
| 133 | n/a | self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None) |
|---|
| 134 | n/a | |
|---|
| 135 | n/a | self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__, |
|---|
| 136 | n/a | "docstring_with_invalid_signature($module, /, boo)\n" |
|---|
| 137 | n/a | "\n" |
|---|
| 138 | n/a | "This docstring has an invalid signature." |
|---|
| 139 | n/a | ) |
|---|
| 140 | n/a | self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None) |
|---|
| 141 | n/a | |
|---|
| 142 | n/a | self.assertEqual(_testcapi.docstring_with_invalid_signature2.__doc__, |
|---|
| 143 | n/a | "docstring_with_invalid_signature2($module, /, boo)\n" |
|---|
| 144 | n/a | "\n" |
|---|
| 145 | n/a | "--\n" |
|---|
| 146 | n/a | "\n" |
|---|
| 147 | n/a | "This docstring also has an invalid signature." |
|---|
| 148 | n/a | ) |
|---|
| 149 | n/a | self.assertEqual(_testcapi.docstring_with_invalid_signature2.__text_signature__, None) |
|---|
| 150 | n/a | |
|---|
| 151 | n/a | self.assertEqual(_testcapi.docstring_with_signature.__doc__, |
|---|
| 152 | n/a | "This docstring has a valid signature.") |
|---|
| 153 | n/a | self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)") |
|---|
| 154 | n/a | |
|---|
| 155 | n/a | self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__doc__, None) |
|---|
| 156 | n/a | self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__text_signature__, |
|---|
| 157 | n/a | "($module, /, sig)") |
|---|
| 158 | n/a | |
|---|
| 159 | n/a | self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__, |
|---|
| 160 | n/a | "\nThis docstring has a valid signature and some extra newlines.") |
|---|
| 161 | n/a | self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, |
|---|
| 162 | n/a | "($module, /, parameter)") |
|---|
| 163 | n/a | |
|---|
| 164 | n/a | def test_c_type_with_matrix_multiplication(self): |
|---|
| 165 | n/a | M = _testcapi.matmulType |
|---|
| 166 | n/a | m1 = M() |
|---|
| 167 | n/a | m2 = M() |
|---|
| 168 | n/a | self.assertEqual(m1 @ m2, ("matmul", m1, m2)) |
|---|
| 169 | n/a | self.assertEqual(m1 @ 42, ("matmul", m1, 42)) |
|---|
| 170 | n/a | self.assertEqual(42 @ m1, ("matmul", 42, m1)) |
|---|
| 171 | n/a | o = m1 |
|---|
| 172 | n/a | o @= m2 |
|---|
| 173 | n/a | self.assertEqual(o, ("imatmul", m1, m2)) |
|---|
| 174 | n/a | o = m1 |
|---|
| 175 | n/a | o @= 42 |
|---|
| 176 | n/a | self.assertEqual(o, ("imatmul", m1, 42)) |
|---|
| 177 | n/a | o = 42 |
|---|
| 178 | n/a | o @= m1 |
|---|
| 179 | n/a | self.assertEqual(o, ("matmul", 42, m1)) |
|---|
| 180 | n/a | |
|---|
| 181 | n/a | def test_return_null_without_error(self): |
|---|
| 182 | n/a | # Issue #23571: A function must not return NULL without setting an |
|---|
| 183 | n/a | # error |
|---|
| 184 | n/a | if Py_DEBUG: |
|---|
| 185 | n/a | code = textwrap.dedent(""" |
|---|
| 186 | n/a | import _testcapi |
|---|
| 187 | n/a | from test import support |
|---|
| 188 | n/a | |
|---|
| 189 | n/a | with support.SuppressCrashReport(): |
|---|
| 190 | n/a | _testcapi.return_null_without_error() |
|---|
| 191 | n/a | """) |
|---|
| 192 | n/a | rc, out, err = assert_python_failure('-c', code) |
|---|
| 193 | n/a | self.assertRegex(err.replace(b'\r', b''), |
|---|
| 194 | n/a | br'Fatal Python error: a function returned NULL ' |
|---|
| 195 | n/a | br'without setting an error\n' |
|---|
| 196 | n/a | br'SystemError: <built-in function ' |
|---|
| 197 | n/a | br'return_null_without_error> returned NULL ' |
|---|
| 198 | n/a | br'without setting an error\n' |
|---|
| 199 | n/a | br'\n' |
|---|
| 200 | n/a | br'Current thread.*:\n' |
|---|
| 201 | n/a | br' File .*", line 6 in <module>') |
|---|
| 202 | n/a | else: |
|---|
| 203 | n/a | with self.assertRaises(SystemError) as cm: |
|---|
| 204 | n/a | _testcapi.return_null_without_error() |
|---|
| 205 | n/a | self.assertRegex(str(cm.exception), |
|---|
| 206 | n/a | 'return_null_without_error.* ' |
|---|
| 207 | n/a | 'returned NULL without setting an error') |
|---|
| 208 | n/a | |
|---|
| 209 | n/a | def test_return_result_with_error(self): |
|---|
| 210 | n/a | # Issue #23571: A function must not return a result with an error set |
|---|
| 211 | n/a | if Py_DEBUG: |
|---|
| 212 | n/a | code = textwrap.dedent(""" |
|---|
| 213 | n/a | import _testcapi |
|---|
| 214 | n/a | from test import support |
|---|
| 215 | n/a | |
|---|
| 216 | n/a | with support.SuppressCrashReport(): |
|---|
| 217 | n/a | _testcapi.return_result_with_error() |
|---|
| 218 | n/a | """) |
|---|
| 219 | n/a | rc, out, err = assert_python_failure('-c', code) |
|---|
| 220 | n/a | self.assertRegex(err.replace(b'\r', b''), |
|---|
| 221 | n/a | br'Fatal Python error: a function returned a ' |
|---|
| 222 | n/a | br'result with an error set\n' |
|---|
| 223 | n/a | br'ValueError\n' |
|---|
| 224 | n/a | br'\n' |
|---|
| 225 | n/a | br'The above exception was the direct cause ' |
|---|
| 226 | n/a | br'of the following exception:\n' |
|---|
| 227 | n/a | br'\n' |
|---|
| 228 | n/a | br'SystemError: <built-in ' |
|---|
| 229 | n/a | br'function return_result_with_error> ' |
|---|
| 230 | n/a | br'returned a result with an error set\n' |
|---|
| 231 | n/a | br'\n' |
|---|
| 232 | n/a | br'Current thread.*:\n' |
|---|
| 233 | n/a | br' File .*, line 6 in <module>') |
|---|
| 234 | n/a | else: |
|---|
| 235 | n/a | with self.assertRaises(SystemError) as cm: |
|---|
| 236 | n/a | _testcapi.return_result_with_error() |
|---|
| 237 | n/a | self.assertRegex(str(cm.exception), |
|---|
| 238 | n/a | 'return_result_with_error.* ' |
|---|
| 239 | n/a | 'returned a result with an error set') |
|---|
| 240 | n/a | |
|---|
| 241 | n/a | def test_buildvalue_N(self): |
|---|
| 242 | n/a | _testcapi.test_buildvalue_N() |
|---|
| 243 | n/a | |
|---|
| 244 | n/a | |
|---|
| 245 | n/a | @unittest.skipUnless(threading, 'Threading required for this test.') |
|---|
| 246 | n/a | class TestPendingCalls(unittest.TestCase): |
|---|
| 247 | n/a | |
|---|
| 248 | n/a | def pendingcalls_submit(self, l, n): |
|---|
| 249 | n/a | def callback(): |
|---|
| 250 | n/a | #this function can be interrupted by thread switching so let's |
|---|
| 251 | n/a | #use an atomic operation |
|---|
| 252 | n/a | l.append(None) |
|---|
| 253 | n/a | |
|---|
| 254 | n/a | for i in range(n): |
|---|
| 255 | n/a | time.sleep(random.random()*0.02) #0.01 secs on average |
|---|
| 256 | n/a | #try submitting callback until successful. |
|---|
| 257 | n/a | #rely on regular interrupt to flush queue if we are |
|---|
| 258 | n/a | #unsuccessful. |
|---|
| 259 | n/a | while True: |
|---|
| 260 | n/a | if _testcapi._pending_threadfunc(callback): |
|---|
| 261 | n/a | break; |
|---|
| 262 | n/a | |
|---|
| 263 | n/a | def pendingcalls_wait(self, l, n, context = None): |
|---|
| 264 | n/a | #now, stick around until l[0] has grown to 10 |
|---|
| 265 | n/a | count = 0; |
|---|
| 266 | n/a | while len(l) != n: |
|---|
| 267 | n/a | #this busy loop is where we expect to be interrupted to |
|---|
| 268 | n/a | #run our callbacks. Note that callbacks are only run on the |
|---|
| 269 | n/a | #main thread |
|---|
| 270 | n/a | if False and support.verbose: |
|---|
| 271 | n/a | print("(%i)"%(len(l),),) |
|---|
| 272 | n/a | for i in range(1000): |
|---|
| 273 | n/a | a = i*i |
|---|
| 274 | n/a | if context and not context.event.is_set(): |
|---|
| 275 | n/a | continue |
|---|
| 276 | n/a | count += 1 |
|---|
| 277 | n/a | self.assertTrue(count < 10000, |
|---|
| 278 | n/a | "timeout waiting for %i callbacks, got %i"%(n, len(l))) |
|---|
| 279 | n/a | if False and support.verbose: |
|---|
| 280 | n/a | print("(%i)"%(len(l),)) |
|---|
| 281 | n/a | |
|---|
| 282 | n/a | def test_pendingcalls_threaded(self): |
|---|
| 283 | n/a | |
|---|
| 284 | n/a | #do every callback on a separate thread |
|---|
| 285 | n/a | n = 32 #total callbacks |
|---|
| 286 | n/a | threads = [] |
|---|
| 287 | n/a | class foo(object):pass |
|---|
| 288 | n/a | context = foo() |
|---|
| 289 | n/a | context.l = [] |
|---|
| 290 | n/a | context.n = 2 #submits per thread |
|---|
| 291 | n/a | context.nThreads = n // context.n |
|---|
| 292 | n/a | context.nFinished = 0 |
|---|
| 293 | n/a | context.lock = threading.Lock() |
|---|
| 294 | n/a | context.event = threading.Event() |
|---|
| 295 | n/a | |
|---|
| 296 | n/a | threads = [threading.Thread(target=self.pendingcalls_thread, |
|---|
| 297 | n/a | args=(context,)) |
|---|
| 298 | n/a | for i in range(context.nThreads)] |
|---|
| 299 | n/a | with support.start_threads(threads): |
|---|
| 300 | n/a | self.pendingcalls_wait(context.l, n, context) |
|---|
| 301 | n/a | |
|---|
| 302 | n/a | def pendingcalls_thread(self, context): |
|---|
| 303 | n/a | try: |
|---|
| 304 | n/a | self.pendingcalls_submit(context.l, context.n) |
|---|
| 305 | n/a | finally: |
|---|
| 306 | n/a | with context.lock: |
|---|
| 307 | n/a | context.nFinished += 1 |
|---|
| 308 | n/a | nFinished = context.nFinished |
|---|
| 309 | n/a | if False and support.verbose: |
|---|
| 310 | n/a | print("finished threads: ", nFinished) |
|---|
| 311 | n/a | if nFinished == context.nThreads: |
|---|
| 312 | n/a | context.event.set() |
|---|
| 313 | n/a | |
|---|
| 314 | n/a | def test_pendingcalls_non_threaded(self): |
|---|
| 315 | n/a | #again, just using the main thread, likely they will all be dispatched at |
|---|
| 316 | n/a | #once. It is ok to ask for too many, because we loop until we find a slot. |
|---|
| 317 | n/a | #the loop can be interrupted to dispatch. |
|---|
| 318 | n/a | #there are only 32 dispatch slots, so we go for twice that! |
|---|
| 319 | n/a | l = [] |
|---|
| 320 | n/a | n = 64 |
|---|
| 321 | n/a | self.pendingcalls_submit(l, n) |
|---|
| 322 | n/a | self.pendingcalls_wait(l, n) |
|---|
| 323 | n/a | |
|---|
| 324 | n/a | |
|---|
| 325 | n/a | class SubinterpreterTest(unittest.TestCase): |
|---|
| 326 | n/a | |
|---|
| 327 | n/a | def test_subinterps(self): |
|---|
| 328 | n/a | import builtins |
|---|
| 329 | n/a | r, w = os.pipe() |
|---|
| 330 | n/a | code = """if 1: |
|---|
| 331 | n/a | import sys, builtins, pickle |
|---|
| 332 | n/a | with open({:d}, "wb") as f: |
|---|
| 333 | n/a | pickle.dump(id(sys.modules), f) |
|---|
| 334 | n/a | pickle.dump(id(builtins), f) |
|---|
| 335 | n/a | """.format(w) |
|---|
| 336 | n/a | with open(r, "rb") as f: |
|---|
| 337 | n/a | ret = support.run_in_subinterp(code) |
|---|
| 338 | n/a | self.assertEqual(ret, 0) |
|---|
| 339 | n/a | self.assertNotEqual(pickle.load(f), id(sys.modules)) |
|---|
| 340 | n/a | self.assertNotEqual(pickle.load(f), id(builtins)) |
|---|
| 341 | n/a | |
|---|
| 342 | n/a | |
|---|
| 343 | n/a | # Bug #6012 |
|---|
| 344 | n/a | class Test6012(unittest.TestCase): |
|---|
| 345 | n/a | def test(self): |
|---|
| 346 | n/a | self.assertEqual(_testcapi.argparsing("Hello", "World"), 1) |
|---|
| 347 | n/a | |
|---|
| 348 | n/a | |
|---|
| 349 | n/a | class EmbeddingTests(unittest.TestCase): |
|---|
| 350 | n/a | def setUp(self): |
|---|
| 351 | n/a | here = os.path.abspath(__file__) |
|---|
| 352 | n/a | basepath = os.path.dirname(os.path.dirname(os.path.dirname(here))) |
|---|
| 353 | n/a | exename = "_testembed" |
|---|
| 354 | n/a | if sys.platform.startswith("win"): |
|---|
| 355 | n/a | ext = ("_d" if "_d" in sys.executable else "") + ".exe" |
|---|
| 356 | n/a | exename += ext |
|---|
| 357 | n/a | exepath = os.path.dirname(sys.executable) |
|---|
| 358 | n/a | else: |
|---|
| 359 | n/a | exepath = os.path.join(basepath, "Programs") |
|---|
| 360 | n/a | self.test_exe = exe = os.path.join(exepath, exename) |
|---|
| 361 | n/a | if not os.path.exists(exe): |
|---|
| 362 | n/a | self.skipTest("%r doesn't exist" % exe) |
|---|
| 363 | n/a | # This is needed otherwise we get a fatal error: |
|---|
| 364 | n/a | # "Py_Initialize: Unable to get the locale encoding |
|---|
| 365 | n/a | # LookupError: no codec search functions registered: can't find encoding" |
|---|
| 366 | n/a | self.oldcwd = os.getcwd() |
|---|
| 367 | n/a | os.chdir(basepath) |
|---|
| 368 | n/a | |
|---|
| 369 | n/a | def tearDown(self): |
|---|
| 370 | n/a | os.chdir(self.oldcwd) |
|---|
| 371 | n/a | |
|---|
| 372 | n/a | def run_embedded_interpreter(self, *args): |
|---|
| 373 | n/a | """Runs a test in the embedded interpreter""" |
|---|
| 374 | n/a | cmd = [self.test_exe] |
|---|
| 375 | n/a | cmd.extend(args) |
|---|
| 376 | n/a | p = subprocess.Popen(cmd, |
|---|
| 377 | n/a | stdout=subprocess.PIPE, |
|---|
| 378 | n/a | stderr=subprocess.PIPE, |
|---|
| 379 | n/a | universal_newlines=True) |
|---|
| 380 | n/a | (out, err) = p.communicate() |
|---|
| 381 | n/a | self.assertEqual(p.returncode, 0, |
|---|
| 382 | n/a | "bad returncode %d, stderr is %r" % |
|---|
| 383 | n/a | (p.returncode, err)) |
|---|
| 384 | n/a | return out, err |
|---|
| 385 | n/a | |
|---|
| 386 | n/a | def test_subinterps(self): |
|---|
| 387 | n/a | # This is just a "don't crash" test |
|---|
| 388 | n/a | out, err = self.run_embedded_interpreter("repeated_init_and_subinterpreters") |
|---|
| 389 | n/a | if support.verbose: |
|---|
| 390 | n/a | print() |
|---|
| 391 | n/a | print(out) |
|---|
| 392 | n/a | print(err) |
|---|
| 393 | n/a | |
|---|
| 394 | n/a | @staticmethod |
|---|
| 395 | n/a | def _get_default_pipe_encoding(): |
|---|
| 396 | n/a | rp, wp = os.pipe() |
|---|
| 397 | n/a | try: |
|---|
| 398 | n/a | with os.fdopen(wp, 'w') as w: |
|---|
| 399 | n/a | default_pipe_encoding = w.encoding |
|---|
| 400 | n/a | finally: |
|---|
| 401 | n/a | os.close(rp) |
|---|
| 402 | n/a | return default_pipe_encoding |
|---|
| 403 | n/a | |
|---|
| 404 | n/a | def test_forced_io_encoding(self): |
|---|
| 405 | n/a | # Checks forced configuration of embedded interpreter IO streams |
|---|
| 406 | n/a | out, err = self.run_embedded_interpreter("forced_io_encoding") |
|---|
| 407 | n/a | if support.verbose: |
|---|
| 408 | n/a | print() |
|---|
| 409 | n/a | print(out) |
|---|
| 410 | n/a | print(err) |
|---|
| 411 | n/a | expected_errors = sys.__stdout__.errors |
|---|
| 412 | n/a | expected_stdin_encoding = sys.__stdin__.encoding |
|---|
| 413 | n/a | expected_pipe_encoding = self._get_default_pipe_encoding() |
|---|
| 414 | n/a | expected_output = '\n'.join([ |
|---|
| 415 | n/a | "--- Use defaults ---", |
|---|
| 416 | n/a | "Expected encoding: default", |
|---|
| 417 | n/a | "Expected errors: default", |
|---|
| 418 | n/a | "stdin: {in_encoding}:{errors}", |
|---|
| 419 | n/a | "stdout: {out_encoding}:{errors}", |
|---|
| 420 | n/a | "stderr: {out_encoding}:backslashreplace", |
|---|
| 421 | n/a | "--- Set errors only ---", |
|---|
| 422 | n/a | "Expected encoding: default", |
|---|
| 423 | n/a | "Expected errors: ignore", |
|---|
| 424 | n/a | "stdin: {in_encoding}:ignore", |
|---|
| 425 | n/a | "stdout: {out_encoding}:ignore", |
|---|
| 426 | n/a | "stderr: {out_encoding}:backslashreplace", |
|---|
| 427 | n/a | "--- Set encoding only ---", |
|---|
| 428 | n/a | "Expected encoding: latin-1", |
|---|
| 429 | n/a | "Expected errors: default", |
|---|
| 430 | n/a | "stdin: latin-1:{errors}", |
|---|
| 431 | n/a | "stdout: latin-1:{errors}", |
|---|
| 432 | n/a | "stderr: latin-1:backslashreplace", |
|---|
| 433 | n/a | "--- Set encoding and errors ---", |
|---|
| 434 | n/a | "Expected encoding: latin-1", |
|---|
| 435 | n/a | "Expected errors: replace", |
|---|
| 436 | n/a | "stdin: latin-1:replace", |
|---|
| 437 | n/a | "stdout: latin-1:replace", |
|---|
| 438 | n/a | "stderr: latin-1:backslashreplace"]) |
|---|
| 439 | n/a | expected_output = expected_output.format( |
|---|
| 440 | n/a | in_encoding=expected_stdin_encoding, |
|---|
| 441 | n/a | out_encoding=expected_pipe_encoding, |
|---|
| 442 | n/a | errors=expected_errors) |
|---|
| 443 | n/a | # This is useful if we ever trip over odd platform behaviour |
|---|
| 444 | n/a | self.maxDiff = None |
|---|
| 445 | n/a | self.assertEqual(out.strip(), expected_output) |
|---|
| 446 | n/a | |
|---|
| 447 | n/a | |
|---|
| 448 | n/a | class SkipitemTest(unittest.TestCase): |
|---|
| 449 | n/a | |
|---|
| 450 | n/a | def test_skipitem(self): |
|---|
| 451 | n/a | """ |
|---|
| 452 | n/a | If this test failed, you probably added a new "format unit" |
|---|
| 453 | n/a | in Python/getargs.c, but neglected to update our poor friend |
|---|
| 454 | n/a | skipitem() in the same file. (If so, shame on you!) |
|---|
| 455 | n/a | |
|---|
| 456 | n/a | With a few exceptions**, this function brute-force tests all |
|---|
| 457 | n/a | printable ASCII*** characters (32 to 126 inclusive) as format units, |
|---|
| 458 | n/a | checking to see that PyArg_ParseTupleAndKeywords() return consistent |
|---|
| 459 | n/a | errors both when the unit is attempted to be used and when it is |
|---|
| 460 | n/a | skipped. If the format unit doesn't exist, we'll get one of two |
|---|
| 461 | n/a | specific error messages (one for used, one for skipped); if it does |
|---|
| 462 | n/a | exist we *won't* get that error--we'll get either no error or some |
|---|
| 463 | n/a | other error. If we get the specific "does not exist" error for one |
|---|
| 464 | n/a | test and not for the other, there's a mismatch, and the test fails. |
|---|
| 465 | n/a | |
|---|
| 466 | n/a | ** Some format units have special funny semantics and it would |
|---|
| 467 | n/a | be difficult to accommodate them here. Since these are all |
|---|
| 468 | n/a | well-established and properly skipped in skipitem() we can |
|---|
| 469 | n/a | get away with not testing them--this test is really intended |
|---|
| 470 | n/a | to catch *new* format units. |
|---|
| 471 | n/a | |
|---|
| 472 | n/a | *** Python C source files must be ASCII. Therefore it's impossible |
|---|
| 473 | n/a | to have non-ASCII format units. |
|---|
| 474 | n/a | |
|---|
| 475 | n/a | """ |
|---|
| 476 | n/a | empty_tuple = () |
|---|
| 477 | n/a | tuple_1 = (0,) |
|---|
| 478 | n/a | dict_b = {'b':1} |
|---|
| 479 | n/a | keywords = ["a", "b"] |
|---|
| 480 | n/a | |
|---|
| 481 | n/a | for i in range(32, 127): |
|---|
| 482 | n/a | c = chr(i) |
|---|
| 483 | n/a | |
|---|
| 484 | n/a | # skip parentheses, the error reporting is inconsistent about them |
|---|
| 485 | n/a | # skip 'e', it's always a two-character code |
|---|
| 486 | n/a | # skip '|' and '$', they don't represent arguments anyway |
|---|
| 487 | n/a | if c in '()e|$': |
|---|
| 488 | n/a | continue |
|---|
| 489 | n/a | |
|---|
| 490 | n/a | # test the format unit when not skipped |
|---|
| 491 | n/a | format = c + "i" |
|---|
| 492 | n/a | try: |
|---|
| 493 | n/a | # (note: the format string must be bytes!) |
|---|
| 494 | n/a | _testcapi.parse_tuple_and_keywords(tuple_1, dict_b, |
|---|
| 495 | n/a | format.encode("ascii"), keywords) |
|---|
| 496 | n/a | when_not_skipped = False |
|---|
| 497 | n/a | except SystemError as e: |
|---|
| 498 | n/a | s = "argument 1 (impossible<bad format char>)" |
|---|
| 499 | n/a | when_not_skipped = (str(e) == s) |
|---|
| 500 | n/a | except TypeError: |
|---|
| 501 | n/a | when_not_skipped = False |
|---|
| 502 | n/a | |
|---|
| 503 | n/a | # test the format unit when skipped |
|---|
| 504 | n/a | optional_format = "|" + format |
|---|
| 505 | n/a | try: |
|---|
| 506 | n/a | _testcapi.parse_tuple_and_keywords(empty_tuple, dict_b, |
|---|
| 507 | n/a | optional_format.encode("ascii"), keywords) |
|---|
| 508 | n/a | when_skipped = False |
|---|
| 509 | n/a | except SystemError as e: |
|---|
| 510 | n/a | s = "impossible<bad format char>: '{}'".format(format) |
|---|
| 511 | n/a | when_skipped = (str(e) == s) |
|---|
| 512 | n/a | |
|---|
| 513 | n/a | message = ("test_skipitem_parity: " |
|---|
| 514 | n/a | "detected mismatch between convertsimple and skipitem " |
|---|
| 515 | n/a | "for format unit '{}' ({}), not skipped {}, skipped {}".format( |
|---|
| 516 | n/a | c, i, when_skipped, when_not_skipped)) |
|---|
| 517 | n/a | self.assertIs(when_skipped, when_not_skipped, message) |
|---|
| 518 | n/a | |
|---|
| 519 | n/a | def test_parse_tuple_and_keywords(self): |
|---|
| 520 | n/a | # parse_tuple_and_keywords error handling tests |
|---|
| 521 | n/a | self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords, |
|---|
| 522 | n/a | (), {}, 42, []) |
|---|
| 523 | n/a | self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, |
|---|
| 524 | n/a | (), {}, b'', 42) |
|---|
| 525 | n/a | self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, |
|---|
| 526 | n/a | (), {}, b'', [''] * 42) |
|---|
| 527 | n/a | self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, |
|---|
| 528 | n/a | (), {}, b'', [42]) |
|---|
| 529 | n/a | |
|---|
| 530 | n/a | def test_positional_only(self): |
|---|
| 531 | n/a | parse = _testcapi.parse_tuple_and_keywords |
|---|
| 532 | n/a | |
|---|
| 533 | n/a | parse((1, 2, 3), {}, b'OOO', ['', '', 'a']) |
|---|
| 534 | n/a | parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a']) |
|---|
| 535 | n/a | with self.assertRaisesRegex(TypeError, |
|---|
| 536 | n/a | r'Function takes at least 2 positional arguments \(1 given\)'): |
|---|
| 537 | n/a | parse((1,), {'a': 3}, b'OOO', ['', '', 'a']) |
|---|
| 538 | n/a | parse((1,), {}, b'O|OO', ['', '', 'a']) |
|---|
| 539 | n/a | with self.assertRaisesRegex(TypeError, |
|---|
| 540 | n/a | r'Function takes at least 1 positional arguments \(0 given\)'): |
|---|
| 541 | n/a | parse((), {}, b'O|OO', ['', '', 'a']) |
|---|
| 542 | n/a | parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a']) |
|---|
| 543 | n/a | with self.assertRaisesRegex(TypeError, |
|---|
| 544 | n/a | r'Function takes exactly 2 positional arguments \(1 given\)'): |
|---|
| 545 | n/a | parse((1,), {'a': 3}, b'OO$O', ['', '', 'a']) |
|---|
| 546 | n/a | parse((1,), {}, b'O|O$O', ['', '', 'a']) |
|---|
| 547 | n/a | with self.assertRaisesRegex(TypeError, |
|---|
| 548 | n/a | r'Function takes at least 1 positional arguments \(0 given\)'): |
|---|
| 549 | n/a | parse((), {}, b'O|O$O', ['', '', 'a']) |
|---|
| 550 | n/a | with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): |
|---|
| 551 | n/a | parse((1,), {}, b'O|$OO', ['', '', 'a']) |
|---|
| 552 | n/a | with self.assertRaisesRegex(SystemError, 'Empty keyword'): |
|---|
| 553 | n/a | parse((1,), {}, b'O|OO', ['', 'a', '']) |
|---|
| 554 | n/a | |
|---|
| 555 | n/a | |
|---|
| 556 | n/a | @unittest.skipUnless(threading, 'Threading required for this test.') |
|---|
| 557 | n/a | class TestThreadState(unittest.TestCase): |
|---|
| 558 | n/a | |
|---|
| 559 | n/a | @support.reap_threads |
|---|
| 560 | n/a | def test_thread_state(self): |
|---|
| 561 | n/a | # some extra thread-state tests driven via _testcapi |
|---|
| 562 | n/a | def target(): |
|---|
| 563 | n/a | idents = [] |
|---|
| 564 | n/a | |
|---|
| 565 | n/a | def callback(): |
|---|
| 566 | n/a | idents.append(threading.get_ident()) |
|---|
| 567 | n/a | |
|---|
| 568 | n/a | _testcapi._test_thread_state(callback) |
|---|
| 569 | n/a | a = b = callback |
|---|
| 570 | n/a | time.sleep(1) |
|---|
| 571 | n/a | # Check our main thread is in the list exactly 3 times. |
|---|
| 572 | n/a | self.assertEqual(idents.count(threading.get_ident()), 3, |
|---|
| 573 | n/a | "Couldn't find main thread correctly in the list") |
|---|
| 574 | n/a | |
|---|
| 575 | n/a | target() |
|---|
| 576 | n/a | t = threading.Thread(target=target) |
|---|
| 577 | n/a | t.start() |
|---|
| 578 | n/a | t.join() |
|---|
| 579 | n/a | |
|---|
| 580 | n/a | |
|---|
| 581 | n/a | class Test_testcapi(unittest.TestCase): |
|---|
| 582 | n/a | def test__testcapi(self): |
|---|
| 583 | n/a | for name in dir(_testcapi): |
|---|
| 584 | n/a | if name.startswith('test_'): |
|---|
| 585 | n/a | with self.subTest("internal", name=name): |
|---|
| 586 | n/a | test = getattr(_testcapi, name) |
|---|
| 587 | n/a | test() |
|---|
| 588 | n/a | |
|---|
| 589 | n/a | |
|---|
| 590 | n/a | class PyMemDebugTests(unittest.TestCase): |
|---|
| 591 | n/a | PYTHONMALLOC = 'debug' |
|---|
| 592 | n/a | # '0x04c06e0' or '04C06E0' |
|---|
| 593 | n/a | PTR_REGEX = r'(?:0x)?[0-9a-fA-F]+' |
|---|
| 594 | n/a | |
|---|
| 595 | n/a | def check(self, code): |
|---|
| 596 | n/a | with support.SuppressCrashReport(): |
|---|
| 597 | n/a | out = assert_python_failure('-c', code, |
|---|
| 598 | n/a | PYTHONMALLOC=self.PYTHONMALLOC) |
|---|
| 599 | n/a | stderr = out.err |
|---|
| 600 | n/a | return stderr.decode('ascii', 'replace') |
|---|
| 601 | n/a | |
|---|
| 602 | n/a | def test_buffer_overflow(self): |
|---|
| 603 | n/a | out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()') |
|---|
| 604 | n/a | regex = (r"Debug memory block at address p={ptr}: API 'm'\n" |
|---|
| 605 | n/a | r" 16 bytes originally requested\n" |
|---|
| 606 | n/a | r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" |
|---|
| 607 | n/a | r" The [0-9] pad bytes at tail={ptr} are not all FORBIDDENBYTE \(0x[0-9a-f]{{2}}\):\n" |
|---|
| 608 | n/a | r" at tail\+0: 0x78 \*\*\* OUCH\n" |
|---|
| 609 | n/a | r" at tail\+1: 0xfb\n" |
|---|
| 610 | n/a | r" at tail\+2: 0xfb\n" |
|---|
| 611 | n/a | r" .*\n" |
|---|
| 612 | n/a | r" The block was made by call #[0-9]+ to debug malloc/realloc.\n" |
|---|
| 613 | n/a | r" Data at p: cb cb cb .*\n" |
|---|
| 614 | n/a | r"\n" |
|---|
| 615 | n/a | r"Fatal Python error: bad trailing pad byte") |
|---|
| 616 | n/a | regex = regex.format(ptr=self.PTR_REGEX) |
|---|
| 617 | n/a | regex = re.compile(regex, flags=re.DOTALL) |
|---|
| 618 | n/a | self.assertRegex(out, regex) |
|---|
| 619 | n/a | |
|---|
| 620 | n/a | def test_api_misuse(self): |
|---|
| 621 | n/a | out = self.check('import _testcapi; _testcapi.pymem_api_misuse()') |
|---|
| 622 | n/a | regex = (r"Debug memory block at address p={ptr}: API 'm'\n" |
|---|
| 623 | n/a | r" 16 bytes originally requested\n" |
|---|
| 624 | n/a | r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" |
|---|
| 625 | n/a | r" The [0-9] pad bytes at tail={ptr} are FORBIDDENBYTE, as expected.\n" |
|---|
| 626 | n/a | r" The block was made by call #[0-9]+ to debug malloc/realloc.\n" |
|---|
| 627 | n/a | r" Data at p: cb cb cb .*\n" |
|---|
| 628 | n/a | r"\n" |
|---|
| 629 | n/a | r"Fatal Python error: bad ID: Allocated using API 'm', verified using API 'r'\n") |
|---|
| 630 | n/a | regex = regex.format(ptr=self.PTR_REGEX) |
|---|
| 631 | n/a | self.assertRegex(out, regex) |
|---|
| 632 | n/a | |
|---|
| 633 | n/a | @unittest.skipUnless(threading, 'Test requires a GIL (multithreading)') |
|---|
| 634 | n/a | def check_malloc_without_gil(self, code): |
|---|
| 635 | n/a | out = self.check(code) |
|---|
| 636 | n/a | expected = ('Fatal Python error: Python memory allocator called ' |
|---|
| 637 | n/a | 'without holding the GIL') |
|---|
| 638 | n/a | self.assertIn(expected, out) |
|---|
| 639 | n/a | |
|---|
| 640 | n/a | def test_pymem_malloc_without_gil(self): |
|---|
| 641 | n/a | # Debug hooks must raise an error if PyMem_Malloc() is called |
|---|
| 642 | n/a | # without holding the GIL |
|---|
| 643 | n/a | code = 'import _testcapi; _testcapi.pymem_malloc_without_gil()' |
|---|
| 644 | n/a | self.check_malloc_without_gil(code) |
|---|
| 645 | n/a | |
|---|
| 646 | n/a | def test_pyobject_malloc_without_gil(self): |
|---|
| 647 | n/a | # Debug hooks must raise an error if PyObject_Malloc() is called |
|---|
| 648 | n/a | # without holding the GIL |
|---|
| 649 | n/a | code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()' |
|---|
| 650 | n/a | self.check_malloc_without_gil(code) |
|---|
| 651 | n/a | |
|---|
| 652 | n/a | |
|---|
| 653 | n/a | class PyMemMallocDebugTests(PyMemDebugTests): |
|---|
| 654 | n/a | PYTHONMALLOC = 'malloc_debug' |
|---|
| 655 | n/a | |
|---|
| 656 | n/a | |
|---|
| 657 | n/a | @unittest.skipUnless(sysconfig.get_config_var('WITH_PYMALLOC') == 1, |
|---|
| 658 | n/a | 'need pymalloc') |
|---|
| 659 | n/a | class PyMemPymallocDebugTests(PyMemDebugTests): |
|---|
| 660 | n/a | PYTHONMALLOC = 'pymalloc_debug' |
|---|
| 661 | n/a | |
|---|
| 662 | n/a | |
|---|
| 663 | n/a | @unittest.skipUnless(Py_DEBUG, 'need Py_DEBUG') |
|---|
| 664 | n/a | class PyMemDefaultTests(PyMemDebugTests): |
|---|
| 665 | n/a | # test default allocator of Python compiled in debug mode |
|---|
| 666 | n/a | PYTHONMALLOC = '' |
|---|
| 667 | n/a | |
|---|
| 668 | n/a | |
|---|
| 669 | n/a | if __name__ == "__main__": |
|---|
| 670 | n/a | unittest.main() |
|---|