Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3043,22 +3043,37 @@ def test_pack_version(self):

def test_pack_full_version_ctypes(self):
ctypes = import_helper.import_module('ctypes')
ctypes_func = ctypes.pythonapi.Py_PACK_FULL_VERSION
ctypes_func.restype = ctypes.c_uint32
ctypes_func.argtypes = [ctypes.c_int] * 5
import ctypes.util # noqa: F811

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def Py_PACK_FULL_VERSION(
x: ctypes.c_int,
y: ctypes.c_int,
z: ctypes.c_int,
level: ctypes.c_int,
serial: ctypes.c_int,
) -> ctypes.c_uint32:
pass

for *args, expected in self.full_cases:
with self.subTest(hexversion=hex(expected)):
result = ctypes_func(*args)
result = Py_PACK_FULL_VERSION(*args)
self.assertEqual(result, expected)

def test_pack_version_ctypes(self):
ctypes = import_helper.import_module('ctypes')
ctypes_func = ctypes.pythonapi.Py_PACK_VERSION
ctypes_func.restype = ctypes.c_uint32
ctypes_func.argtypes = [ctypes.c_int] * 2
import ctypes.util # noqa: F811

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def Py_PACK_VERSION(
x: ctypes.c_int,
y: ctypes.c_int,
) -> ctypes.c_uint32:
pass

for *args, expected in self.xy_cases:
with self.subTest(hexversion=hex(expected)):
result = ctypes_func(*args)
result = Py_PACK_VERSION(*args)
self.assertEqual(result, expected)


Expand Down
37 changes: 21 additions & 16 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -1540,29 +1540,34 @@ async def afunc():
[(1,1,3)])

if check_impl_detail(cpython=True) and ctypes is not None:
py = ctypes.pythonapi
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)

RequestCodeExtraIndex = py.PyUnstable_Eval_RequestCodeExtraIndex
RequestCodeExtraIndex.argtypes = (freefunc,)
RequestCodeExtraIndex.restype = ctypes.c_ssize_t

SetExtra = py.PyUnstable_Code_SetExtra
SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
SetExtra.restype = ctypes.c_int

GetExtra = py.PyUnstable_Code_GetExtra
GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
ctypes.POINTER(ctypes.c_voidp))
GetExtra.restype = ctypes.c_int
import ctypes.util
freefunc = ctypes.CFUNCTYPE(None, ctypes.c_voidp)

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Eval_RequestCodeExtraIndex(free: freefunc) -> ctypes.c_ssize_t:
pass

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Code_SetExtra(code: ctypes.py_object,
index: ctypes.c_ssize_t,
extra: ctypes.c_voidp) -> ctypes.c_int:
pass
SetExtra = PyUnstable_Code_SetExtra

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Code_GetExtra(code: ctypes.py_object,
index: ctypes.c_ssize_t,
extra: ctypes.POINTER(ctypes.c_voidp)) -> ctypes.c_int:
pass
GetExtra = PyUnstable_Code_GetExtra

LAST_FREED = None
def myfree(ptr):
global LAST_FREED
LAST_FREED = ptr

FREE_FUNC = freefunc(myfree)
FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC)
FREE_INDEX = PyUnstable_Eval_RequestCodeExtraIndex(FREE_FUNC)
# Make sure myfree sticks around at least as long as the interpreter,
# since we (currently) can't unregister the function and leaving a
# dangling pointer will cause a crash on deallocation of code objects if
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_ctypes/test_refcounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ class PyObjectRestypeTest(unittest.TestCase):
def test_restype_py_object_with_null_return(self):
# Test that a function which returns a NULL PyObject *
# without setting an exception does not crash.
PyErr_Occurred = ctypes.pythonapi.PyErr_Occurred
PyErr_Occurred.argtypes = []
PyErr_Occurred.restype = ctypes.py_object
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyErr_Occurred() -> ctypes.py_object:
pass

# At this point, there's no exception set, so PyErr_Occurred
# returns NULL. Given the restype is py_object, the
Expand Down
10 changes: 8 additions & 2 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,16 @@ def test_WindowsError(self):
def test_windows_message(self):
"""Should fill in unknown error code in Windows error message"""
ctypes = import_module('ctypes')
import ctypes.util # noqa: F811

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyErr_SetFromWindowsErr(ierr: ctypes.c_int) -> ctypes.py_object:
pass

# this error code has no message, Python formats it as hexadecimal
code = 3765269347
with self.assertRaisesRegex(OSError, 'Windows Error 0x%x' % code):
ctypes.pythonapi.PyErr_SetFromWindowsErr(code)
with self.assertRaisesRegex(OSError, f'Windows Error 0x{code:x}'):
PyErr_SetFromWindowsErr(code)

def testAttributes(self):
# test that exception attributes are happy
Expand Down
27 changes: 18 additions & 9 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,28 +821,37 @@ class TestFrameCApi(unittest.TestCase):
def test_basic(self):
x = 1
ctypes = import_helper.import_module('ctypes')
PyEval_GetFrameLocals = ctypes.pythonapi.PyEval_GetFrameLocals
PyEval_GetFrameLocals.restype = ctypes.py_object
import ctypes.util # noqa: F811

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyEval_GetFrameLocals() -> ctypes.py_object:
pass

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyEval_GetFrameGlobals() -> ctypes.py_object:
pass

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyEval_GetFrameBuiltins() -> ctypes.py_object:
pass

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyFrame_GetLocals(frame: ctypes.py_object) -> ctypes.py_object:
pass

frame_locals = PyEval_GetFrameLocals()
self.assertTrue(type(frame_locals), dict)
self.assertEqual(frame_locals['x'], 1)
frame_locals['x'] = 2
self.assertEqual(x, 1)

PyEval_GetFrameGlobals = ctypes.pythonapi.PyEval_GetFrameGlobals
PyEval_GetFrameGlobals.restype = ctypes.py_object
frame_globals = PyEval_GetFrameGlobals()
self.assertTrue(type(frame_globals), dict)
self.assertIs(frame_globals, globals())

PyEval_GetFrameBuiltins = ctypes.pythonapi.PyEval_GetFrameBuiltins
PyEval_GetFrameBuiltins.restype = ctypes.py_object
frame_builtins = PyEval_GetFrameBuiltins()
self.assertEqual(frame_builtins, __builtins__)

PyFrame_GetLocals = ctypes.pythonapi.PyFrame_GetLocals
PyFrame_GetLocals.argtypes = [ctypes.py_object]
PyFrame_GetLocals.restype = ctypes.py_object
frame = sys._getframe()
f_locals = PyFrame_GetLocals(frame)
self.assertTrue(f_locals['x'], 1)
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_free_threading/test_capi.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import ctypes
import ctypes.util
import sys
import unittest

from test.support import threading_helper
from test.support.threading_helper import run_concurrently


_PyImport_AddModuleRef = ctypes.pythonapi.PyImport_AddModuleRef
_PyImport_AddModuleRef.argtypes = (ctypes.c_char_p,)
_PyImport_AddModuleRef.restype = ctypes.py_object
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyImport_AddModuleRef(name: ctypes.c_char_p) -> ctypes.py_object:
pass


@threading_helper.requires_working_threading()
Expand All @@ -26,7 +26,7 @@ def test_pyimport_addmoduleref_thread_safe(self):
results = []

def worker():
module = _PyImport_AddModuleRef(module_name_bytes)
module = PyImport_AddModuleRef(module_name_bytes)
results.append(module)

for _ in range(NUM_ITERS):
Expand Down
35 changes: 19 additions & 16 deletions Lib/test/test_free_threading/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,28 @@
from test.support.threading_helper import run_concurrently

if ctypes is not None:
capi = ctypes.pythonapi
import ctypes.util

freefunc = ctypes.CFUNCTYPE(None, ctypes.c_voidp)

RequestCodeExtraIndex = capi.PyUnstable_Eval_RequestCodeExtraIndex
RequestCodeExtraIndex.argtypes = (freefunc,)
RequestCodeExtraIndex.restype = ctypes.c_ssize_t

SetExtra = capi.PyUnstable_Code_SetExtra
SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
SetExtra.restype = ctypes.c_int

GetExtra = capi.PyUnstable_Code_GetExtra
GetExtra.argtypes = (
ctypes.py_object,
ctypes.c_ssize_t,
ctypes.POINTER(ctypes.c_voidp),
)
GetExtra.restype = ctypes.c_int
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Eval_RequestCodeExtraIndex(free: freefunc) -> ctypes.c_ssize_t:
pass
RequestCodeExtraIndex = PyUnstable_Eval_RequestCodeExtraIndex

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Code_SetExtra(code: ctypes.py_object,
index: ctypes.c_ssize_t,
extra: ctypes.c_voidp) -> ctypes.c_int:
pass
SetExtra = PyUnstable_Code_SetExtra

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyUnstable_Code_GetExtra(code: ctypes.py_object,
index: ctypes.c_ssize_t,
extra: ctypes.POINTER(ctypes.c_voidp)) -> ctypes.c_int:
pass
GetExtra = PyUnstable_Code_GetExtra

# Note: each call to RequestCodeExtraIndex permanently allocates a slot
# (the counter is monotonically increasing), up to MAX_CO_EXTRA_USERS (255).
Expand Down
8 changes: 6 additions & 2 deletions Lib/test/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,11 @@ def test_refcount_errors(self):
import subprocess
code = textwrap.dedent('''
from test.support import gc_collect, SuppressCrashReport
import ctypes.util

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def Py_DecRef(o: ctypes.py_object) -> None:
pass

a = [1, 2, 3]
b = [a, a]
Expand All @@ -1445,8 +1450,7 @@ def test_refcount_errors(self):
# Simulate the refcount of "a" being too low (compared to the
# references held on it by live data), but keeping it above zero
# (to avoid deallocating it):
import ctypes
ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
Py_DecRef(a)
del a
del b

Expand Down
24 changes: 19 additions & 5 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,13 @@ def f(mutex):
@cpython_only
def test_PyThreadState_SetAsyncExc(self):
ctypes = import_module("ctypes")
import ctypes.util # noqa: F811

set_async_exc = ctypes.pythonapi.PyThreadState_SetAsyncExc
set_async_exc.argtypes = (ctypes.c_ulong, ctypes.py_object)
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyThreadState_SetAsyncExc(id: ctypes.c_ulong,
exc: ctypes.py_object) -> ctypes.c_int:
pass
set_async_exc = PyThreadState_SetAsyncExc

class AsyncExc(Exception):
pass
Expand Down Expand Up @@ -485,7 +489,17 @@ def test_finalize_running_thread(self):
import_module("ctypes")

rc, out, err = assert_python_failure("-c", """if 1:
import ctypes, sys, time, _thread
import ctypes.util, sys, time, _thread

PyGILState_STATE = ctypes.c_int # enum

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyGILState_Ensure() -> PyGILState_STATE:
pass

@ctypes.util.wrap_dll_function(ctypes.pythonapi)
def PyGILState_Release(oldstate: PyGILState_STATE) -> None:
pass

# This lock is used as a simple event variable.
ready = _thread.allocate_lock()
Expand All @@ -494,8 +508,8 @@ def test_finalize_running_thread(self):
# Module globals are cleared before __del__ is run
# So we save the functions in class dict
class C:
ensure = ctypes.pythonapi.PyGILState_Ensure
release = ctypes.pythonapi.PyGILState_Release
ensure = PyGILState_Ensure
release = PyGILState_Release
def __del__(self):
state = self.ensure()
self.release(state)
Expand Down
Loading