Merge pull request #6457 from youknowone/ctypes

Enable ctypes
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2025-12-24 08:40:19 +09:00
commit 2785bd8224
84 files changed
+3559 -1736

No files matched your search

+51 -46
View File
@@ -1,6 +1,8 @@
"""create and manipulate C data types in Python"""
import os as _os, sys as _sys
import os as _os
import sys as _sys
import sysconfig as _sysconfig
import types as _types
__version__ = "1.1.0"
@@ -36,9 +38,6 @@ from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
# TODO: RUSTPYTHON remove this
from _ctypes import _non_existing_function
# WINOLEAPI -> HRESULT
# WINOLEAPI_(type)
#
@@ -110,7 +109,7 @@ def CFUNCTYPE(restype, *argtypes, **kw):
return CFunctionType
if _os.name == "nt":
from _ctypes import LoadLibrary as _dlopen
from _ctypes import LoadLibrary as _LoadLibrary
from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
_win_functype_cache = {}
@@ -305,8 +304,9 @@ def create_unicode_buffer(init, size=None):
raise TypeError(init)
# XXX Deprecated
def SetPointerType(pointer, cls):
import warnings
warnings._deprecated("ctypes.SetPointerType", remove=(3, 15))
if _pointer_type_cache.get(cls, None) is not None:
raise RuntimeError("This type already exists in the cache")
if id(pointer) not in _pointer_type_cache:
@@ -315,7 +315,6 @@ def SetPointerType(pointer, cls):
_pointer_type_cache[cls] = pointer
del _pointer_type_cache[id(pointer)]
# XXX Deprecated
def ARRAY(typ, len):
return typ * len
@@ -347,52 +346,59 @@ class CDLL(object):
use_errno=False,
use_last_error=False,
winmode=None):
class _FuncPtr(_CFuncPtr):
_flags_ = self._func_flags_
_restype_ = self._func_restype_
if use_errno:
_flags_ |= _FUNCFLAG_USE_ERRNO
if use_last_error:
_flags_ |= _FUNCFLAG_USE_LASTERROR
self._FuncPtr = _FuncPtr
if name:
name = _os.fspath(name)
self._handle = self._load_library(name, mode, handle, winmode)
if _os.name == "nt":
def _load_library(self, name, mode, handle, winmode):
if winmode is None:
import nt as _nt
winmode = _nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
# WINAPI LoadLibrary searches for a DLL if the given name
# is not fully qualified with an explicit drive. For POSIX
# compatibility, and because the DLL search path no longer
# contains the working directory, begin by fully resolving
# any name that contains a path separator.
if name is not None and ('/' in name or '\\' in name):
name = _nt._getfullpathname(name)
winmode |= _nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
self._name = name
if handle is not None:
return handle
return _LoadLibrary(self._name, winmode)
else:
def _load_library(self, name, mode, handle, winmode):
# If the filename that has been provided is an iOS/tvOS/watchOS
# .fwork file, dereference the location to the true origin of the
# binary.
if name.endswith(".fwork"):
if name and name.endswith(".fwork"):
with open(name) as f:
name = _os.path.join(
_os.path.dirname(_sys.executable),
f.read().strip()
)
self._name = name
flags = self._func_flags_
if use_errno:
flags |= _FUNCFLAG_USE_ERRNO
if use_last_error:
flags |= _FUNCFLAG_USE_LASTERROR
if _sys.platform.startswith("aix"):
"""When the name contains ".a(" and ends with ")",
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
archive(member) syntax for dlopen(), and the mode is adjusted.
Otherwise, name is presented to dlopen() as a file argument.
"""
if name and name.endswith(")") and ".a(" in name:
mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
if _os.name == "nt":
if winmode is not None:
mode = winmode
else:
import nt
mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
if '/' in name or '\\' in name:
self._name = nt._getfullpathname(self._name)
mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
class _FuncPtr(_CFuncPtr):
_flags_ = flags
_restype_ = self._func_restype_
self._FuncPtr = _FuncPtr
if handle is None:
self._handle = _dlopen(self._name, mode)
else:
self._handle = handle
if _sys.platform.startswith("aix"):
"""When the name contains ".a(" and ends with ")",
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
archive(member) syntax for dlopen(), and the mode is adjusted.
Otherwise, name is presented to dlopen() as a file argument.
"""
if name and name.endswith(")") and ".a(" in name:
mode |= _os.RTLD_MEMBER | _os.RTLD_NOW
self._name = name
return _dlopen(name, mode)
def __repr__(self):
return "<%s '%s', handle %x at %#x>" % \
@@ -480,10 +486,9 @@ pydll = LibraryLoader(PyDLL)
if _os.name == "nt":
pythonapi = PyDLL("python dll", None, _sys.dllhandle)
elif _sys.platform == "android":
pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2])
elif _sys.platform == "cygwin":
pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
elif _sys.platform in ["android", "cygwin"]:
# These are Unix-like platforms which use a dynamically-linked libpython.
pythonapi = PyDLL(_sysconfig.get_config_var("LDLIBRARY"))
else:
pythonapi = PyDLL(None)
+4 -4
View File
@@ -1,5 +1,5 @@
import sys
from ctypes import *
from ctypes import Array, Structure, Union
_array_type = type(Array)
@@ -15,8 +15,8 @@ def _other_endian(typ):
# if typ is array
if isinstance(typ, _array_type):
return _other_endian(typ._type_) * typ._length_
# if typ is structure
if issubclass(typ, Structure):
# if typ is structure or union
if issubclass(typ, (Structure, Union)):
return typ
raise TypeError("This type does not support other endian: %s" % typ)
@@ -37,7 +37,7 @@ class _swapped_union_meta(_swapped_meta, type(Union)): pass
################################################################
# Note: The Structure metaclass checks for the *presence* (not the
# value!) of a _swapped_bytes_ attribute to determine the bit order in
# value!) of a _swappedbytes_ attribute to determine the bit order in
# structures containing bit fields.
if sys.byteorder == "little":
-16
View File
@@ -1,16 +0,0 @@
import os
import unittest
from test import support
from test.support import import_helper
# skip tests if _ctypes was not built
ctypes = import_helper.import_module('ctypes')
ctypes_symbols = dir(ctypes)
def need_symbol(name):
return unittest.skipUnless(name in ctypes_symbols,
'{!r} is required'.format(name))
def load_tests(*args):
return support.load_package_tests(os.path.dirname(__file__), *args)
-4
View File
@@ -1,4 +0,0 @@
from ctypes.test import load_tests
import unittest
unittest.main()
-21
View File
@@ -1,21 +0,0 @@
import unittest
from ctypes import *
class X(Structure):
_fields_ = [("foo", c_int)]
class TestCase(unittest.TestCase):
def test_simple(self):
self.assertRaises(TypeError,
delattr, c_int(42), "value")
def test_chararray(self):
self.assertRaises(TypeError,
delattr, (c_char * 5)(), "value")
def test_struct(self):
self.assertRaises(TypeError,
delattr, X(), "foo")
if __name__ == "__main__":
unittest.main()
-55
View File
@@ -1,55 +0,0 @@
import unittest
from ctypes import *
class MyInt(c_int):
def __eq__(self, other):
if type(other) != MyInt:
return NotImplementedError
return self.value == other.value
class Test(unittest.TestCase):
def test_compare(self):
self.assertEqual(MyInt(3), MyInt(3))
self.assertNotEqual(MyInt(42), MyInt(43))
def test_ignore_retval(self):
# Test if the return value of a callback is ignored
# if restype is None
proto = CFUNCTYPE(None)
def func():
return (1, "abc", None)
cb = proto(func)
self.assertEqual(None, cb())
def test_int_callback(self):
args = []
def func(arg):
args.append(arg)
return arg
cb = CFUNCTYPE(None, MyInt)(func)
self.assertEqual(None, cb(42))
self.assertEqual(type(args[-1]), MyInt)
cb = CFUNCTYPE(c_int, c_int)(func)
self.assertEqual(42, cb(42))
self.assertEqual(type(args[-1]), int)
def test_int_struct(self):
class X(Structure):
_fields_ = [("x", MyInt)]
self.assertEqual(X().x, MyInt())
s = X()
s.x = MyInt(42)
self.assertEqual(s.x, MyInt(42))
if __name__ == "__main__":
unittest.main()
+15 -3
View File
@@ -67,7 +67,7 @@ if os.name == "nt":
return fname
return None
elif os.name == "posix" and sys.platform == "darwin":
elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos"}:
from ctypes.macholib.dyld import dyld_find as _dyld_find
def find_library(name):
possible = ['lib%s.dylib' % name,
@@ -89,6 +89,15 @@ elif sys.platform.startswith("aix"):
from ctypes._aix import find_library
elif sys.platform == "android":
def find_library(name):
directory = "/system/lib"
if "64" in os.uname().machine:
directory += "64"
fname = f"{directory}/lib{name}.so"
return fname if os.path.isfile(fname) else None
elif os.name == "posix":
# Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
import re, tempfile
@@ -96,8 +105,11 @@ elif os.name == "posix":
def _is_elf(filename):
"Return True if the given file is an ELF file"
elf_header = b'\x7fELF'
with open(filename, 'br') as thefile:
return thefile.read(4) == elf_header
try:
with open(filename, 'br') as thefile:
return thefile.read(4) == elf_header
except FileNotFoundError:
return False
def _findLib_gcc(name):
# Run GCC's linker with the -t (aka --trace) option and examine the
+1 -1
View File
@@ -1080,7 +1080,7 @@ def _sys_version(sys_version=None):
match.groups()
# XXX: RUSTPYTHON support
if "rustc" in sys_version:
if "RustPython" in sys_version:
name = "RustPython"
else:
name = 'CPython'
-10
View File
@@ -1,10 +0,0 @@
import unittest
from test.support.import_helper import import_module
ctypes_test = import_module('ctypes.test')
load_tests = ctypes_test.load_tests
if __name__ == "__main__":
unittest.main()
+10
View File
@@ -0,0 +1,10 @@
import os
from test import support
from test.support import import_helper
# skip tests if the _ctypes extension was not built
import_helper.import_module('ctypes')
def load_tests(*args):
return support.load_package_tests(os.path.dirname(__file__), *args)
+4
View File
@@ -0,0 +1,4 @@
from test.test_ctypes import load_tests
import unittest
unittest.main()
+24
View File
@@ -0,0 +1,24 @@
# Some classes and types are not export to _ctypes module directly.
import ctypes
from _ctypes import Structure, Union, _Pointer, Array, _SimpleCData, CFuncPtr
_CData = Structure.__base__
assert _CData.__name__ == "_CData"
class _X(Structure):
_fields_ = [("x", ctypes.c_int)]
CField = type(_X.x)
# metaclasses
PyCStructType = type(Structure)
UnionType = type(Union)
PyCPointerType = type(_Pointer)
PyCArrayType = type(Array)
PyCSimpleType = type(_SimpleCData)
PyCFuncPtrType = type(CFuncPtr)
# type flags
Py_TPFLAGS_DISALLOW_INSTANTIATION = 1 << 7
Py_TPFLAGS_IMMUTABLETYPE = 1 << 8
+321
View File
@@ -0,0 +1,321 @@
from ctypes import (
c_char, c_uint32, c_uint16, c_ubyte, c_byte, alignment, sizeof,
BigEndianStructure, LittleEndianStructure,
BigEndianUnion, LittleEndianUnion, Structure
)
import struct
import unittest
class TestAlignedStructures(unittest.TestCase):
def test_aligned_string(self):
for base, e in (
(LittleEndianStructure, "<"),
(BigEndianStructure, ">"),
):
data = bytearray(struct.pack(f"{e}i12x16s", 7, b"hello world!"))
class Aligned(base):
_align_ = 16
_fields_ = [
('value', c_char * 12)
]
class Main(base):
_fields_ = [
('first', c_uint32),
('string', Aligned),
]
main = Main.from_buffer(data)
self.assertEqual(main.first, 7)
self.assertEqual(main.string.value, b'hello world!')
self.assertEqual(bytes(main.string), b'hello world!\0\0\0\0')
self.assertEqual(Main.string.offset, 16)
self.assertEqual(Main.string.size, 16)
self.assertEqual(alignment(main.string), 16)
self.assertEqual(alignment(main), 16)
def test_aligned_structures(self):
for base, data in (
(LittleEndianStructure, bytearray(b"\1\0\0\0\1\0\0\0\7\0\0\0")),
(BigEndianStructure, bytearray(b"\1\0\0\0\1\0\0\0\7\0\0\0")),
):
class SomeBools(base):
_align_ = 4
_fields_ = [
("bool1", c_ubyte),
("bool2", c_ubyte),
]
class Main(base):
_fields_ = [
("x", c_ubyte),
("y", SomeBools),
("z", c_ubyte),
]
main = Main.from_buffer(data)
self.assertEqual(alignment(SomeBools), 4)
self.assertEqual(alignment(main), 4)
self.assertEqual(alignment(main.y), 4)
self.assertEqual(Main.x.size, 1)
self.assertEqual(Main.y.offset, 4)
self.assertEqual(Main.y.size, 4)
self.assertEqual(main.y.bool1, True)
self.assertEqual(main.y.bool2, False)
self.assertEqual(Main.z.offset, 8)
self.assertEqual(main.z, 7)
def test_oversized_structure(self):
data = bytearray(b"\0" * 8)
for base in (LittleEndianStructure, BigEndianStructure):
class SomeBoolsTooBig(base):
_align_ = 8
_fields_ = [
("bool1", c_ubyte),
("bool2", c_ubyte),
("bool3", c_ubyte),
]
class Main(base):
_fields_ = [
("y", SomeBoolsTooBig),
("z", c_uint32),
]
with self.assertRaises(ValueError) as ctx:
Main.from_buffer(data)
self.assertEqual(
ctx.exception.args[0],
'Buffer size too small (4 instead of at least 8 bytes)'
)
def test_aligned_subclasses(self):
for base, e in (
(LittleEndianStructure, "<"),
(BigEndianStructure, ">"),
):
data = bytearray(struct.pack(f"{e}4i", 1, 2, 3, 4))
class UnalignedSub(base):
x: c_uint32
_fields_ = [
("x", c_uint32),
]
class AlignedStruct(UnalignedSub):
_align_ = 8
_fields_ = [
("y", c_uint32),
]
class Main(base):
_fields_ = [
("a", c_uint32),
("b", AlignedStruct)
]
main = Main.from_buffer(data)
self.assertEqual(alignment(main.b), 8)
self.assertEqual(alignment(main), 8)
self.assertEqual(sizeof(main.b), 8)
self.assertEqual(sizeof(main), 16)
self.assertEqual(main.a, 1)
self.assertEqual(main.b.x, 3)
self.assertEqual(main.b.y, 4)
self.assertEqual(Main.b.offset, 8)
self.assertEqual(Main.b.size, 8)
def test_aligned_union(self):
for sbase, ubase, e in (
(LittleEndianStructure, LittleEndianUnion, "<"),
(BigEndianStructure, BigEndianUnion, ">"),
):
data = bytearray(struct.pack(f"{e}4i", 1, 2, 3, 4))
class AlignedUnion(ubase):
_align_ = 8
_fields_ = [
("a", c_uint32),
("b", c_ubyte * 7),
]
class Main(sbase):
_fields_ = [
("first", c_uint32),
("union", AlignedUnion),
]
main = Main.from_buffer(data)
self.assertEqual(main.first, 1)
self.assertEqual(main.union.a, 3)
self.assertEqual(bytes(main.union.b), data[8:-1])
self.assertEqual(Main.union.offset, 8)
self.assertEqual(Main.union.size, 8)
self.assertEqual(alignment(main.union), 8)
self.assertEqual(alignment(main), 8)
def test_aligned_struct_in_union(self):
for sbase, ubase, e in (
(LittleEndianStructure, LittleEndianUnion, "<"),
(BigEndianStructure, BigEndianUnion, ">"),
):
data = bytearray(struct.pack(f"{e}4i", 1, 2, 3, 4))
class Sub(sbase):
_align_ = 8
_fields_ = [
("x", c_uint32),
("y", c_uint32),
]
class MainUnion(ubase):
_fields_ = [
("a", c_uint32),
("b", Sub),
]
class Main(sbase):
_fields_ = [
("first", c_uint32),
("union", MainUnion),
]
main = Main.from_buffer(data)
self.assertEqual(Main.first.size, 4)
self.assertEqual(alignment(main.union), 8)
self.assertEqual(alignment(main), 8)
self.assertEqual(Main.union.offset, 8)
self.assertEqual(Main.union.size, 8)
self.assertEqual(main.first, 1)
self.assertEqual(main.union.a, 3)
self.assertEqual(main.union.b.x, 3)
self.assertEqual(main.union.b.y, 4)
def test_smaller_aligned_subclassed_union(self):
for sbase, ubase, e in (
(LittleEndianStructure, LittleEndianUnion, "<"),
(BigEndianStructure, BigEndianUnion, ">"),
):
data = bytearray(struct.pack(f"{e}H2xI", 1, 0xD60102D7))
class SubUnion(ubase):
_align_ = 2
_fields_ = [
("unsigned", c_ubyte),
("signed", c_byte),
]
class MainUnion(SubUnion):
_fields_ = [
("num", c_uint32)
]
class Main(sbase):
_fields_ = [
("first", c_uint16),
("union", MainUnion),
]
main = Main.from_buffer(data)
self.assertEqual(main.union.num, 0xD60102D7)
self.assertEqual(main.union.unsigned, data[4])
self.assertEqual(main.union.signed, data[4] - 256)
self.assertEqual(alignment(main), 4)
self.assertEqual(alignment(main.union), 4)
self.assertEqual(Main.union.offset, 4)
self.assertEqual(Main.union.size, 4)
self.assertEqual(Main.first.size, 2)
def test_larger_aligned_subclassed_union(self):
for ubase, e in (
(LittleEndianUnion, "<"),
(BigEndianUnion, ">"),
):
data = bytearray(struct.pack(f"{e}I4x", 0xD60102D6))
class SubUnion(ubase):
_align_ = 8
_fields_ = [
("unsigned", c_ubyte),
("signed", c_byte),
]
class Main(SubUnion):
_fields_ = [
("num", c_uint32)
]
main = Main.from_buffer(data)
self.assertEqual(alignment(main), 8)
self.assertEqual(sizeof(main), 8)
self.assertEqual(main.num, 0xD60102D6)
self.assertEqual(main.unsigned, 0xD6)
self.assertEqual(main.signed, -42)
def test_aligned_packed_structures(self):
for sbase, e in (
(LittleEndianStructure, "<"),
(BigEndianStructure, ">"),
):
data = bytearray(struct.pack(f"{e}B2H4xB", 1, 2, 3, 4))
class Inner(sbase):
_align_ = 8
_fields_ = [
("x", c_uint16),
("y", c_uint16),
]
class Main(sbase):
_pack_ = 1
_fields_ = [
("a", c_ubyte),
("b", Inner),
("c", c_ubyte),
]
main = Main.from_buffer(data)
self.assertEqual(sizeof(main), 10)
self.assertEqual(Main.b.offset, 1)
# Alignment == 8 because _pack_ wins out.
self.assertEqual(alignment(main.b), 8)
# Size is still 8 though since inside this Structure, it will have
# effect.
self.assertEqual(sizeof(main.b), 8)
self.assertEqual(Main.c.offset, 9)
self.assertEqual(main.a, 1)
self.assertEqual(main.b.x, 2)
self.assertEqual(main.b.y, 3)
self.assertEqual(main.c, 4)
def test_negative_align(self):
for base in (Structure, LittleEndianStructure, BigEndianStructure):
with (
self.subTest(base=base),
self.assertRaisesRegex(
ValueError,
'_align_ must be a non-negative integer',
)
):
class MyStructure(base):
_align_ = -1
_fields_ = []
def test_zero_align_no_fields(self):
for base in (Structure, LittleEndianStructure, BigEndianStructure):
with self.subTest(base=base):
class MyStructure(base):
_align_ = 0
_fields_ = []
self.assertEqual(alignment(MyStructure), 1)
self.assertEqual(alignment(MyStructure()), 1)
def test_zero_align_with_fields(self):
for base in (Structure, LittleEndianStructure, BigEndianStructure):
with self.subTest(base=base):
class MyStructure(base):
_align_ = 0
_fields_ = [
("x", c_ubyte),
]
self.assertEqual(alignment(MyStructure), 1)
self.assertEqual(alignment(MyStructure()), 1)
if __name__ == '__main__':
unittest.main()
@@ -1,6 +1,7 @@
import unittest
import test.support
from ctypes import *
from ctypes import c_int, Union, Structure, sizeof
class AnonTest(unittest.TestCase):
@@ -69,5 +70,6 @@ class AnonTest(unittest.TestCase):
self.assertEqual(Y._.offset, sizeof(c_int))
self.assertEqual(Y.y.offset, sizeof(c_int) * 2)
if __name__ == "__main__":
unittest.main()
@@ -1,21 +1,24 @@
import unittest
from ctypes import *
from binascii import hexlify
import binascii
import re
import unittest
from ctypes import c_byte, Structure, POINTER, cast
def dump(obj):
# helper function to dump memory contents in hex, with a hyphen
# between the bytes.
h = hexlify(memoryview(obj)).decode()
h = binascii.hexlify(memoryview(obj)).decode()
return re.sub(r"(..)", r"\1-", h)[:-1]
class Value(Structure):
_fields_ = [("val", c_byte)]
class Container(Structure):
_fields_ = [("pvalues", POINTER(Value))]
class Test(unittest.TestCase):
def test(self):
# create an array of 4 values
@@ -60,5 +63,6 @@ class Test(unittest.TestCase):
([1, 2, 3, 4], "01-02-03-04")
)
if __name__ == "__main__":
unittest.main()
@@ -1,16 +1,50 @@
import unittest
from test.support import bigmemtest, _2G
import ctypes
import sys
from ctypes import *
import unittest
from ctypes import (Structure, Array, ARRAY, sizeof, addressof,
create_string_buffer, create_unicode_buffer,
c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
c_long, c_ulonglong, c_float, c_double, c_longdouble)
from test.support import bigmemtest, _2G
from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
from ctypes.test import need_symbol
formats = "bBhHiIlLqQfd"
formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \
c_long, c_ulonglong, c_float, c_double, c_longdouble
class ArrayTestCase(unittest.TestCase):
def test_inheritance_hierarchy(self):
self.assertEqual(Array.mro(), [Array, _CData, object])
self.assertEqual(PyCArrayType.__name__, "PyCArrayType")
self.assertEqual(type(PyCArrayType), type)
def test_type_flags(self):
for cls in Array, PyCArrayType:
with self.subTest(cls=cls):
self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Abstract classes (whose metaclass __init__ was not called) can't be
# instantiated directly
NewArray = PyCArrayType.__new__(PyCArrayType, 'NewArray', (Array,), {})
for cls in Array, NewArray:
with self.subTest(cls=cls):
with self.assertRaisesRegex(TypeError, "abstract class"):
obj = cls()
# Cannot call the metaclass __init__ more than once
class T(Array):
_type_ = c_int
_length_ = 13
with self.assertRaisesRegex(SystemError, "already initialized"):
PyCArrayType.__init__(T, 'ptr', (), {})
def test_simple(self):
# create classes holding simple numeric types, and check
# various properties.
@@ -34,9 +68,9 @@ class ArrayTestCase(unittest.TestCase):
with self.assertRaises(IndexError): ia[-alen-1]
# change the items
from operator import setitem
new_values = list(range(42, 42+alen))
[setitem(ia, n, new_values[n]) for n in range(alen)]
for n in range(alen):
ia[n] = new_values[n]
values = [ia[i] for i in range(alen)]
self.assertEqual(values, new_values)
@@ -66,8 +100,8 @@ class ArrayTestCase(unittest.TestCase):
self.assertEqual(len(ca), 3)
# cannot delete items
from operator import delitem
self.assertRaises(TypeError, delitem, ca, 0)
with self.assertRaises(TypeError):
del ca[0]
def test_step_overflow(self):
a = (c_int * 5)()
@@ -117,7 +151,6 @@ class ArrayTestCase(unittest.TestCase):
self.assertEqual(sz[1:4:2], b"o")
self.assertEqual(sz.value, b"foo")
@need_symbol('create_unicode_buffer')
def test_from_addressW(self):
p = create_unicode_buffer("foo")
sz = (c_wchar * 3).from_address(addressof(p))
@@ -178,10 +211,10 @@ class ArrayTestCase(unittest.TestCase):
class T(Array):
pass
with self.assertRaises(AttributeError):
class T(Array):
class T2(Array):
_type_ = c_int
with self.assertRaises(AttributeError):
class T(Array):
class T3(Array):
_length_ = 13
def test_bad_length(self):
@@ -190,15 +223,15 @@ class ArrayTestCase(unittest.TestCase):
_type_ = c_int
_length_ = - sys.maxsize * 2
with self.assertRaises(ValueError):
class T(Array):
class T2(Array):
_type_ = c_int
_length_ = -1
with self.assertRaises(TypeError):
class T(Array):
class T3(Array):
_type_ = c_int
_length_ = 1.87
with self.assertRaises(OverflowError):
class T(Array):
class T4(Array):
_type_ = c_int
_length_ = sys.maxsize * 2
@@ -212,7 +245,7 @@ class ArrayTestCase(unittest.TestCase):
class EmptyStruct(Structure):
_fields_ = []
obj = (EmptyStruct * 2)() # bpo37188: Floating point exception
obj = (EmptyStruct * 2)() # bpo37188: Floating-point exception
self.assertEqual(sizeof(obj), 0)
def test_empty_element_array(self):
@@ -220,7 +253,7 @@ class ArrayTestCase(unittest.TestCase):
_type_ = c_int
_length_ = 0
obj = (EmptyArray * 2)() # bpo37188: Floating point exception
obj = (EmptyArray * 2)() # bpo37188: Floating-point exception
self.assertEqual(sizeof(obj), 0)
def test_bpo36504_signed_int_overflow(self):
@@ -234,5 +267,6 @@ class ArrayTestCase(unittest.TestCase):
def test_large_array(self, size):
c_char * size
if __name__ == '__main__':
unittest.main()
@@ -1,24 +1,31 @@
import ctypes
import unittest
from ctypes import *
from ctypes.test import need_symbol
import _ctypes_test
from ctypes import (Structure, CDLL, CFUNCTYPE,
POINTER, pointer, byref,
c_short, c_int, c_long, c_longlong,
c_byte, c_wchar, c_float, c_double,
ArgumentError)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
dll = CDLL(_ctypes_test.__file__)
try:
CALLBACK_FUNCTYPE = WINFUNCTYPE
except NameError:
CALLBACK_FUNCTYPE = ctypes.WINFUNCTYPE
except AttributeError:
# fake to enable this test on Linux
CALLBACK_FUNCTYPE = CFUNCTYPE
class POINT(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
class BasicWrapTestCase(unittest.TestCase):
def wrap(self, param):
return param
@need_symbol('c_wchar')
def test_wchar_parm(self):
f = dll._testfunc_i_bhilfd
f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double]
@@ -67,8 +74,6 @@ class BasicWrapTestCase(unittest.TestCase):
f(self.wrap(2**18), self.wrap(cb))
self.assertEqual(args, expected)
################################################################
def test_callbacks(self):
f = dll._testfunc_callback_i_if
f.restype = c_int
@@ -77,7 +82,6 @@ class BasicWrapTestCase(unittest.TestCase):
MyCallback = CFUNCTYPE(c_int, c_int)
def callback(value):
#print "called back with", value
return value
cb = MyCallback(callback)
@@ -114,7 +118,6 @@ class BasicWrapTestCase(unittest.TestCase):
f.argtypes = [c_int, MyCallback]
def callback(value):
#print "called back with", value
self.assertEqual(type(value), int)
return value
@@ -122,9 +125,7 @@ class BasicWrapTestCase(unittest.TestCase):
result = f(self.wrap(-10), self.wrap(cb))
self.assertEqual(result, -18)
@need_symbol('c_longlong')
def test_longlong_callbacks(self):
f = dll._testfunc_callback_q_qf
f.restype = c_longlong
@@ -192,29 +193,32 @@ class BasicWrapTestCase(unittest.TestCase):
(9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
def test_recursive_as_param(self):
from ctypes import c_int
class A(object):
class A:
pass
a = A()
a._as_parameter_ = a
with self.assertRaises(RecursionError):
c_int.from_param(a)
for c_type in (
ctypes.c_wchar_p,
ctypes.c_char_p,
ctypes.c_void_p,
ctypes.c_int, # PyCSimpleType
POINT, # CDataType
):
with self.subTest(c_type=c_type):
with self.assertRaises(RecursionError):
c_type.from_param(a)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AsParamWrapper(object):
class AsParamWrapper:
def __init__(self, param):
self._as_parameter_ = param
class AsParamWrapperTestCase(BasicWrapTestCase):
wrap = AsParamWrapper
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AsParamPropertyWrapper(object):
class AsParamPropertyWrapper:
def __init__(self, param):
self._param = param
@@ -225,7 +229,17 @@ class AsParamPropertyWrapper(object):
class AsParamPropertyWrapperTestCase(BasicWrapTestCase):
wrap = AsParamPropertyWrapper
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AsParamNestedWrapperTestCase(BasicWrapTestCase):
"""Test that _as_parameter_ is evaluated recursively.
The _as_parameter_ attribute can be another object which
defines its own _as_parameter_ attribute.
"""
def wrap(self, param):
return AsParamWrapper(AsParamWrapper(AsParamWrapper(param)))
if __name__ == '__main__':
unittest.main()
@@ -1,10 +1,14 @@
from ctypes import *
from ctypes.test import need_symbol
from test import support
import unittest
import os
import unittest
from ctypes import (CDLL, Structure, sizeof, POINTER, byref, alignment,
LittleEndianStructure, BigEndianStructure,
c_byte, c_ubyte, c_char, c_char_p, c_void_p, c_wchar,
c_uint32, c_uint64,
c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong)
from test import support
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
class BITS(Structure):
_fields_ = [("A", c_int, 1),
@@ -28,8 +32,6 @@ class BITS(Structure):
func = CDLL(_ctypes_test.__file__).unpack_bitfields
func.argtypes = POINTER(BITS), c_char
##for n in "ABCDEFGHIMNOPQRS":
## print n, hex(getattr(BITS, n).size), getattr(BITS, n).offset
class C_Test(unittest.TestCase):
@@ -53,6 +55,7 @@ class C_Test(unittest.TestCase):
setattr(b, name, i)
self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii')))
signed_int_types = (c_byte, c_short, c_int, c_long, c_longlong)
unsigned_int_types = (c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong)
int_types = unsigned_int_types + signed_int_types
@@ -114,7 +117,6 @@ class BitFieldTest(unittest.TestCase):
x.a, x.b = 0, -1
self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 7, 0))
def fail_fields(self, *fields):
return self.get_except(type(Structure), "X", (),
{"_fields_": fields})
@@ -140,7 +142,6 @@ class BitFieldTest(unittest.TestCase):
result = self.fail_fields(("a", Dummy, 1))
self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy'))
@need_symbol('c_wchar')
def test_c_wchar(self):
result = self.fail_fields(("a", c_wchar, 1))
self.assertEqual(result,
@@ -192,7 +193,6 @@ class BitFieldTest(unittest.TestCase):
self.assertEqual(X.b.offset, sizeof(c_short)*1)
self.assertEqual(X.c.offset, sizeof(c_short)*2)
def get_except(self, func, *args, **kw):
try:
func(*args, **kw)
@@ -245,7 +245,6 @@ class BitFieldTest(unittest.TestCase):
_anonymous_ = ["_"]
_fields_ = [("_", X)]
@need_symbol('c_uint32')
def test_uint32(self):
class X(Structure):
_fields_ = [("a", c_uint32, 32)]
@@ -255,7 +254,6 @@ class BitFieldTest(unittest.TestCase):
x.a = 0xFDCBA987
self.assertEqual(x.a, 0xFDCBA987)
@need_symbol('c_uint64')
def test_uint64(self):
class X(Structure):
_fields_ = [("a", c_uint64, 64)]
@@ -265,7 +263,6 @@ class BitFieldTest(unittest.TestCase):
x.a = 0xFEDCBA9876543211
self.assertEqual(x.a, 0xFEDCBA9876543211)
@need_symbol('c_uint32')
def test_uint32_swap_little_endian(self):
# Issue #23319
class Little(LittleEndianStructure):
@@ -279,7 +276,6 @@ class BitFieldTest(unittest.TestCase):
x.c = 2
self.assertEqual(b, b'\xef\xcd\xab\x21')
@need_symbol('c_uint32')
def test_uint32_swap_big_endian(self):
# Issue #23319
class Big(BigEndianStructure):
@@ -293,5 +289,6 @@ class BitFieldTest(unittest.TestCase):
x.c = 2
self.assertEqual(b, b'\xab\xcd\xef\x12')
if __name__ == "__main__":
unittest.main()
@@ -1,9 +1,9 @@
from ctypes import *
from ctypes.test import need_symbol
import unittest
from ctypes import (create_string_buffer, create_unicode_buffer, sizeof,
c_char, c_wchar)
class StringBufferTestCase(unittest.TestCase):
def test_buffer(self):
b = create_string_buffer(32)
self.assertEqual(len(b), 32)
@@ -27,7 +27,6 @@ class StringBufferTestCase(unittest.TestCase):
self.assertEqual(len(bytearray(create_string_buffer(0))), 0)
self.assertEqual(len(bytearray(create_string_buffer(1))), 1)
@need_symbol('c_wchar')
def test_unicode_buffer(self):
b = create_unicode_buffer(32)
self.assertEqual(len(b), 32)
@@ -47,7 +46,6 @@ class StringBufferTestCase(unittest.TestCase):
self.assertRaises(TypeError, create_unicode_buffer, b"abc")
@need_symbol('c_wchar')
def test_unicode_conversion(self):
b = create_unicode_buffer("abc")
self.assertEqual(len(b), 4) # trailing nul char
@@ -60,7 +58,6 @@ class StringBufferTestCase(unittest.TestCase):
self.assertEqual(b[::2], "ac")
self.assertEqual(b[::5], "a")
@need_symbol('c_wchar')
def test_create_unicode_buffer_non_bmp(self):
expected = 5 if sizeof(c_wchar) == 2 else 3
for s in '\U00010000\U00100000', '\U00010000\U0010ffff':
@@ -1,7 +1,9 @@
"""Test where byte objects are accepted"""
import unittest
import sys
from ctypes import *
import unittest
from _ctypes import _SimpleCData
from ctypes import Structure, c_char, c_char_p, c_wchar, c_wchar_p
class BytesTest(unittest.TestCase):
def test_c_char(self):
@@ -55,7 +57,6 @@ class BytesTest(unittest.TestCase):
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
def test_BSTR(self):
from _ctypes import _SimpleCData
class BSTR(_SimpleCData):
_type_ = "X"
@@ -1,10 +1,21 @@
import sys, unittest, struct, math, ctypes
from binascii import hexlify
import binascii
import ctypes
import math
import struct
import sys
import unittest
from ctypes import (Structure, Union, LittleEndianUnion, BigEndianUnion,
BigEndianStructure, LittleEndianStructure,
POINTER, sizeof, cast,
c_byte, c_ubyte, c_char, c_wchar, c_void_p,
c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong,
c_uint32, c_float, c_double)
from ctypes import *
def bin(s):
return hexlify(memoryview(s)).decode().upper()
return binascii.hexlify(memoryview(s)).decode().upper()
# Each *simple* type that supports different byte orders has an
# __ctype_be__ attribute that specifies the same type in BIG ENDIAN
@@ -14,14 +25,6 @@ def bin(s):
# For Structures and Unions, these types are created on demand.
class Test(unittest.TestCase):
@unittest.skip('test disabled')
def test_X(self):
print(sys.byteorder, file=sys.stderr)
for i in range(32):
bits = BITS()
setattr(bits, "i%s" % i, 1)
dump(bits)
def test_slots(self):
class BigPoint(BigEndianStructure):
__slots__ = ()
@@ -360,5 +363,24 @@ class Test(unittest.TestCase):
self.assertEqual(s.point.x, 1)
self.assertEqual(s.point.y, 2)
def test_build_struct_union_opposite_system_byteorder(self):
# gh-105102
if sys.byteorder == "little":
_Structure = BigEndianStructure
_Union = BigEndianUnion
else:
_Structure = LittleEndianStructure
_Union = LittleEndianUnion
class S1(_Structure):
_fields_ = [("a", c_byte), ("b", c_byte)]
class U1(_Union):
_fields_ = [("s1", S1), ("ab", c_short)]
class S2(_Structure):
_fields_ = [("u1", U1), ("c", c_byte)]
if __name__ == "__main__":
unittest.main()
+152
View File
@@ -0,0 +1,152 @@
import unittest
import ctypes
from ctypes import POINTER, c_void_p
from ._support import PyCSimpleType
class PyCSimpleTypeAsMetaclassTest(unittest.TestCase):
def tearDown(self):
# to not leak references, we must clean _pointer_type_cache
ctypes._reset_cache()
def test_creating_pointer_in_dunder_new_1(self):
# Test metaclass whose instances are C types; when the type is
# created it automatically creates a pointer type for itself.
# The pointer type is also an instance of the metaclass.
# Such an implementation is used in `IUnknown` of the `comtypes`
# project. See gh-124520.
class ct_meta(type):
def __new__(cls, name, bases, namespace):
self = super().__new__(cls, name, bases, namespace)
# Avoid recursion: don't set up a pointer to
# a pointer (to a pointer...)
if bases == (c_void_p,):
# When creating PtrBase itself, the name
# is not yet available
return self
if issubclass(self, PtrBase):
return self
if bases == (object,):
ptr_bases = (self, PtrBase)
else:
ptr_bases = (self, POINTER(bases[0]))
p = p_meta(f"POINTER({self.__name__})", ptr_bases, {})
ctypes._pointer_type_cache[self] = p
return self
class p_meta(PyCSimpleType, ct_meta):
pass
class PtrBase(c_void_p, metaclass=p_meta):
pass
class CtBase(object, metaclass=ct_meta):
pass
class Sub(CtBase):
pass
class Sub2(Sub):
pass
self.assertIsInstance(POINTER(Sub2), p_meta)
self.assertTrue(issubclass(POINTER(Sub2), Sub2))
self.assertTrue(issubclass(POINTER(Sub2), POINTER(Sub)))
self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase)))
def test_creating_pointer_in_dunder_new_2(self):
# A simpler variant of the above, used in `CoClass` of the `comtypes`
# project.
class ct_meta(type):
def __new__(cls, name, bases, namespace):
self = super().__new__(cls, name, bases, namespace)
if isinstance(self, p_meta):
return self
p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {})
ctypes._pointer_type_cache[self] = p
return self
class p_meta(PyCSimpleType, ct_meta):
pass
class Core(object):
pass
class CtBase(Core, metaclass=ct_meta):
pass
class Sub(CtBase):
pass
self.assertIsInstance(POINTER(Sub), p_meta)
self.assertTrue(issubclass(POINTER(Sub), Sub))
def test_creating_pointer_in_dunder_init_1(self):
class ct_meta(type):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
# Avoid recursion.
# (See test_creating_pointer_in_dunder_new_1)
if bases == (c_void_p,):
return
if issubclass(self, PtrBase):
return
if bases == (object,):
ptr_bases = (self, PtrBase)
else:
ptr_bases = (self, POINTER(bases[0]))
p = p_meta(f"POINTER({self.__name__})", ptr_bases, {})
ctypes._pointer_type_cache[self] = p
class p_meta(PyCSimpleType, ct_meta):
pass
class PtrBase(c_void_p, metaclass=p_meta):
pass
class CtBase(object, metaclass=ct_meta):
pass
class Sub(CtBase):
pass
class Sub2(Sub):
pass
self.assertIsInstance(POINTER(Sub2), p_meta)
self.assertTrue(issubclass(POINTER(Sub2), Sub2))
self.assertTrue(issubclass(POINTER(Sub2), POINTER(Sub)))
self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase)))
def test_creating_pointer_in_dunder_init_2(self):
class ct_meta(type):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
# Avoid recursion.
# (See test_creating_pointer_in_dunder_new_2)
if isinstance(self, p_meta):
return
p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {})
ctypes._pointer_type_cache[self] = p
class p_meta(PyCSimpleType, ct_meta):
pass
class Core(object):
pass
class CtBase(Core, metaclass=ct_meta):
pass
class Sub(CtBase):
pass
self.assertIsInstance(POINTER(Sub), p_meta)
self.assertTrue(issubclass(POINTER(Sub), Sub))
@@ -1,19 +1,25 @@
import ctypes
import functools
import gc
import math
import sys
import unittest
from test import support
from ctypes import *
from ctypes.test import need_symbol
from _ctypes import CTYPES_MAX_ARGCOUNT
import _ctypes_test
from ctypes import (CDLL, cdll, Structure, CFUNCTYPE,
ArgumentError, POINTER, sizeof,
c_byte, c_ubyte, c_char,
c_short, c_ushort, c_int, c_uint,
c_long, c_longlong, c_ulonglong, c_ulong,
c_float, c_double, c_longdouble, py_object)
from ctypes.util import find_library
from test import support
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
class Callbacks(unittest.TestCase):
functype = CFUNCTYPE
## def tearDown(self):
## import gc
## gc.collect()
def callback(self, *args):
self.got_args = args
return args[-1]
@@ -35,8 +41,6 @@ class Callbacks(unittest.TestCase):
self.assertEqual(self.got_args, (-3, arg))
self.assertEqual(result, arg)
################
def test_byte(self):
self.check_type(c_byte, 42)
self.check_type(c_byte, -42)
@@ -65,18 +69,15 @@ class Callbacks(unittest.TestCase):
def test_ulong(self):
self.check_type(c_ulong, 42)
@need_symbol('c_longlong')
def test_longlong(self):
self.check_type(c_longlong, 42)
self.check_type(c_longlong, -42)
@need_symbol('c_ulonglong')
def test_ulonglong(self):
self.check_type(c_ulonglong, 42)
def test_float(self):
# only almost equal: double -> float -> double
import math
self.check_type(c_float, math.e)
self.check_type(c_float, -math.e)
@@ -84,7 +85,6 @@ class Callbacks(unittest.TestCase):
self.check_type(c_double, 3.14)
self.check_type(c_double, -3.14)
@need_symbol('c_longdouble')
def test_longdouble(self):
self.check_type(c_longdouble, 3.14)
self.check_type(c_longdouble, -3.14)
@@ -93,30 +93,21 @@ class Callbacks(unittest.TestCase):
self.check_type(c_char, b"x")
self.check_type(c_char, b"a")
# disabled: would now (correctly) raise a RuntimeWarning about
# a memory leak. A callback function cannot return a non-integral
# C type without causing a memory leak.
@unittest.skip('test disabled')
def test_char_p(self):
self.check_type(c_char_p, "abc")
self.check_type(c_char_p, "def")
def test_pyobject(self):
o = ()
from sys import getrefcount as grc
for o in (), [], object():
initial = grc(o)
initial = sys.getrefcount(o)
# This call leaks a reference to 'o'...
self.check_type(py_object, o)
before = grc(o)
before = sys.getrefcount(o)
# ...but this call doesn't leak any more. Where is the refcount?
self.check_type(py_object, o)
after = grc(o)
after = sys.getrefcount(o)
self.assertEqual((after, o), (before, o))
def test_unsupported_restype_1(self):
# Only "fundamental" result types are supported for callback
# functions, the type must have a non-NULL stgdict->setfunc.
# functions, the type must have a non-NULL stginfo->setfunc.
# POINTER(c_double), for example, is not supported.
prototype = self.functype.__func__(POINTER(c_double))
@@ -130,12 +121,11 @@ class Callbacks(unittest.TestCase):
def test_issue_7959(self):
proto = self.functype.__func__(None)
class X(object):
class X:
def func(self): pass
def __init__(self):
self.v = proto(self.func)
import gc
for i in range(32):
X()
gc.collect()
@@ -144,21 +134,30 @@ class Callbacks(unittest.TestCase):
self.assertEqual(len(live), 0)
def test_issue12483(self):
import gc
class Nasty:
def __del__(self):
gc.collect()
CFUNCTYPE(None)(lambda x=Nasty(): None)
@unittest.skipUnless(hasattr(ctypes, 'WINFUNCTYPE'),
'ctypes.WINFUNCTYPE is required')
def test_i38748_stackCorruption(self):
callback_funcType = ctypes.WINFUNCTYPE(c_long, c_long, c_longlong)
@callback_funcType
def callback(a, b):
c = a + b
print(f"a={a}, b={b}, c={c}")
return c
dll = cdll[_ctypes_test.__file__]
with support.captured_stdout() as out:
# With no fix for i38748, the next line will raise OSError and cause the test to fail.
self.assertEqual(dll._test_i38748_runCallback(callback, 5, 10), 15)
self.assertEqual(out.getvalue(), "a=5, b=10, c=15\n")
@need_symbol('WINFUNCTYPE')
class StdcallCallbacks(Callbacks):
try:
functype = WINFUNCTYPE
except NameError:
pass
if hasattr(ctypes, 'WINFUNCTYPE'):
class StdcallCallbacks(Callbacks):
functype = ctypes.WINFUNCTYPE
################################################################
class SampleCallbacksTestCase(unittest.TestCase):
@@ -183,7 +182,6 @@ class SampleCallbacksTestCase(unittest.TestCase):
self.assertLess(diff, 0.01, "%s not less than 0.01" % diff)
def test_issue_8959_a(self):
from ctypes.util import find_library
libc_path = find_library("c")
if not libc_path:
self.skipTest('could not find libc')
@@ -198,19 +196,21 @@ class SampleCallbacksTestCase(unittest.TestCase):
libc.qsort(array, len(array), sizeof(c_int), cmp_func)
self.assertEqual(array[:], [1, 5, 7, 33, 99])
@need_symbol('WINFUNCTYPE')
@unittest.skipUnless(hasattr(ctypes, 'WINFUNCTYPE'),
'ctypes.WINFUNCTYPE is required')
def test_issue_8959_b(self):
from ctypes.wintypes import BOOL, HWND, LPARAM
global windowCount
windowCount = 0
@WINFUNCTYPE(BOOL, HWND, LPARAM)
@ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
def EnumWindowsCallbackFunc(hwnd, lParam):
global windowCount
windowCount += 1
return True #Allow windows to keep enumerating
windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0)
user32 = ctypes.windll.user32
user32.EnumWindows(EnumWindowsCallbackFunc, 0)
def test_callback_register_int(self):
# Issue #8275: buggy handling of callback args under Win64
@@ -324,9 +324,9 @@ class SampleCallbacksTestCase(unittest.TestCase):
self.assertIsInstance(cm.unraisable.exc_value, TypeError)
self.assertEqual(cm.unraisable.err_msg,
"Exception ignored on converting result "
"of ctypes callback function")
self.assertIs(cm.unraisable.object, func)
f"Exception ignored on converting result "
f"of ctypes callback function {func!r}")
self.assertIsNone(cm.unraisable.object)
if __name__ == '__main__':
@@ -1,10 +1,11 @@
from ctypes import *
from ctypes.test import need_symbol
import unittest
import sys
import unittest
from ctypes import (Structure, Union, POINTER, cast, sizeof, addressof,
c_void_p, c_char_p, c_wchar_p,
c_byte, c_short, c_int)
class Test(unittest.TestCase):
def test_array2pointer(self):
array = (c_int * 3)(42, 17, 2)
@@ -12,7 +13,7 @@ class Test(unittest.TestCase):
ptr = cast(array, POINTER(c_int))
self.assertEqual([ptr[i] for i in range(3)], [42, 17, 2])
if 2*sizeof(c_short) == sizeof(c_int):
if 2 * sizeof(c_short) == sizeof(c_int):
ptr = cast(array, POINTER(c_short))
if sys.byteorder == "little":
self.assertEqual([ptr[i] for i in range(6)],
@@ -31,6 +32,8 @@ class Test(unittest.TestCase):
ptr = cast(address, POINTER(c_int))
self.assertEqual([ptr[i] for i in range(3)], [42, 17, 2])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_p2a_objects(self):
array = (c_char_p * 5)()
self.assertEqual(array._objects, None)
@@ -76,11 +79,10 @@ class Test(unittest.TestCase):
self.assertEqual(cast(cast(s, c_void_p), c_char_p).value,
b"hiho")
@need_symbol('c_wchar_p')
def test_wchar_p(self):
s = c_wchar_p("hiho")
self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value,
"hiho")
"hiho")
def test_bad_type_arg(self):
# The type argument must be a ctypes pointer type.
@@ -95,5 +97,6 @@ class Test(unittest.TestCase):
_fields_ = [("a", c_int)]
self.assertRaises(TypeError, cast, array, MyUnion)
if __name__ == "__main__":
unittest.main()
@@ -1,11 +1,13 @@
# A lot of failures in these tests on Mac OS X.
# Byte order related?
import ctypes
import unittest
from ctypes import *
from ctypes.test import need_symbol
from ctypes import (CDLL,
c_byte, c_ubyte, c_char,
c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong,
c_float, c_double, c_longdouble)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
class CFunctions(unittest.TestCase):
_dll = CDLL(_ctypes_test.__file__)
@@ -111,28 +113,24 @@ class CFunctions(unittest.TestCase):
self.assertEqual(self._dll.tf_bL(b' ', 4294967295), 1431655765)
self.assertEqual(self.U(), 4294967295)
@need_symbol('c_longlong')
def test_longlong(self):
self._dll.tf_q.restype = c_longlong
self._dll.tf_q.argtypes = (c_longlong, )
self.assertEqual(self._dll.tf_q(-9223372036854775806), -3074457345618258602)
self.assertEqual(self.S(), -9223372036854775806)
@need_symbol('c_longlong')
def test_longlong_plus(self):
self._dll.tf_bq.restype = c_longlong
self._dll.tf_bq.argtypes = (c_byte, c_longlong)
self.assertEqual(self._dll.tf_bq(0, -9223372036854775806), -3074457345618258602)
self.assertEqual(self.S(), -9223372036854775806)
@need_symbol('c_ulonglong')
def test_ulonglong(self):
self._dll.tf_Q.restype = c_ulonglong
self._dll.tf_Q.argtypes = (c_ulonglong, )
self.assertEqual(self._dll.tf_Q(18446744073709551615), 6148914691236517205)
self.assertEqual(self.U(), 18446744073709551615)
@need_symbol('c_ulonglong')
def test_ulonglong_plus(self):
self._dll.tf_bQ.restype = c_ulonglong
self._dll.tf_bQ.argtypes = (c_byte, c_ulonglong)
@@ -163,14 +161,12 @@ class CFunctions(unittest.TestCase):
self.assertEqual(self._dll.tf_bd(0, 42.), 14.)
self.assertEqual(self.S(), 42)
@need_symbol('c_longdouble')
def test_longdouble(self):
self._dll.tf_D.restype = c_longdouble
self._dll.tf_D.argtypes = (c_longdouble,)
self.assertEqual(self._dll.tf_D(42.), 14.)
self.assertEqual(self.S(), 42)
@need_symbol('c_longdouble')
def test_longdouble_plus(self):
self._dll.tf_bD.restype = c_longdouble
self._dll.tf_bD.argtypes = (c_byte, c_longdouble)
@@ -195,14 +191,11 @@ class CFunctions(unittest.TestCase):
self.assertEqual(self._dll.tv_i(-42), None)
self.assertEqual(self.S(), -42)
# The following repeats the above tests with stdcall functions (where
# they are available)
try:
WinDLL
except NameError:
def stdcall_dll(*_): pass
else:
class stdcall_dll(WinDLL):
if hasattr(ctypes, 'WinDLL'):
class stdcall_dll(ctypes.WinDLL):
def __getattr__(self, name):
if name[:2] == '__' and name[-2:] == '__':
raise AttributeError(name)
@@ -210,9 +203,9 @@ else:
setattr(self, name, func)
return func
@need_symbol('WinDLL')
class stdcallCFunctions(CFunctions):
_dll = stdcall_dll(_ctypes_test.__file__)
class stdcallCFunctions(CFunctions):
_dll = stdcall_dll(_ctypes_test.__file__)
if __name__ == '__main__':
unittest.main()
@@ -1,7 +1,9 @@
import ctypes
import unittest
from ctypes import CDLL, c_int
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ctypes import *
from ctypes.test import need_symbol
class CHECKED(c_int):
def _check_retval_(value):
@@ -9,11 +11,9 @@ class CHECKED(c_int):
return str(value.value)
_check_retval_ = staticmethod(_check_retval_)
class Test(unittest.TestCase):
def test_checkretval(self):
import _ctypes_test
dll = CDLL(_ctypes_test.__file__)
self.assertEqual(42, dll._testfunc_p_p(42))
@@ -26,11 +26,12 @@ class Test(unittest.TestCase):
del dll._testfunc_p_p.restype
self.assertEqual(42, dll._testfunc_p_p(42))
@need_symbol('oledll')
@unittest.skipUnless(hasattr(ctypes, 'oledll'),
'ctypes.oledll is required')
def test_oledll(self):
self.assertRaises(OSError,
oledll.oleaut32.CreateTypeLib2,
0, None, None)
oleaut32 = ctypes.oledll.oleaut32
self.assertRaises(OSError, oleaut32.CreateTypeLib2, 0, None, None)
if __name__ == "__main__":
unittest.main()
+26
View File
@@ -0,0 +1,26 @@
import unittest
from ctypes import Structure, c_char, c_int
class X(Structure):
_fields_ = [("foo", c_int)]
class TestCase(unittest.TestCase):
def test_simple(self):
with self.assertRaises(TypeError):
del c_int(42).value
def test_chararray(self):
chararray = (c_char * 5)()
with self.assertRaises(TypeError):
del chararray.value
def test_struct(self):
struct = X()
with self.assertRaises(TypeError):
del struct.foo
if __name__ == "__main__":
unittest.main()
+180
View File
@@ -0,0 +1,180 @@
import _ctypes
import os
import platform
import sys
import test.support
import unittest
from ctypes import CDLL, c_int
from ctypes.util import find_library
FOO_C = r"""
#include <unistd.h>
/* This is a 'GNU indirect function' (IFUNC) that will be called by
dlsym() to resolve the symbol "foo" to an address. Typically, such
a function would return the address of an actual function, but it
can also just return NULL. For some background on IFUNCs, see
https://willnewton.name/uncategorized/using-gnu-indirect-functions.
Adapted from Michael Kerrisk's answer: https://stackoverflow.com/a/53590014.
*/
asm (".type foo STT_GNU_IFUNC");
void *foo(void)
{
write($DESCRIPTOR, "OK", 2);
return NULL;
}
"""
@unittest.skipUnless(sys.platform.startswith('linux'),
'test requires GNU IFUNC support')
class TestNullDlsym(unittest.TestCase):
"""GH-126554: Ensure that we catch NULL dlsym return values
In rare cases, such as when using GNU IFUNCs, dlsym(),
the C function that ctypes' CDLL uses to get the address
of symbols, can return NULL.
The objective way of telling if an error during symbol
lookup happened is to call glibc's dlerror() and check
for a non-NULL return value.
However, there can be cases where dlsym() returns NULL
and dlerror() is also NULL, meaning that glibc did not
encounter any error.
In the case of ctypes, we subjectively treat that as
an error, and throw a relevant exception.
This test case ensures that we correctly enforce
this 'dlsym returned NULL -> throw Error' rule.
"""
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_null_dlsym(self):
import subprocess
import tempfile
try:
retcode = subprocess.call(["gcc", "--version"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
self.skipTest("gcc is missing")
if retcode != 0:
self.skipTest("gcc --version failed")
pipe_r, pipe_w = os.pipe()
self.addCleanup(os.close, pipe_r)
self.addCleanup(os.close, pipe_w)
with tempfile.TemporaryDirectory() as d:
# Create a C file with a GNU Indirect Function (FOO_C)
# and compile it into a shared library.
srcname = os.path.join(d, 'foo.c')
dstname = os.path.join(d, 'libfoo.so')
with open(srcname, 'w') as f:
f.write(FOO_C.replace('$DESCRIPTOR', str(pipe_w)))
args = ['gcc', '-fPIC', '-shared', '-o', dstname, srcname]
p = subprocess.run(args, capture_output=True)
if p.returncode != 0:
# IFUNC is not supported on all architectures.
if platform.machine() == 'x86_64':
# It should be supported here. Something else went wrong.
p.check_returncode()
else:
# IFUNC might not be supported on this machine.
self.skipTest(f"could not compile indirect function: {p}")
# Case #1: Test 'PyCFuncPtr_FromDll' from Modules/_ctypes/_ctypes.c
L = CDLL(dstname)
with self.assertRaisesRegex(AttributeError, "function 'foo' not found"):
# Try accessing the 'foo' symbol.
# It should resolve via dlsym() to NULL,
# and since we subjectively treat NULL
# addresses as errors, we should get
# an error.
L.foo
# Assert that the IFUNC was called
self.assertEqual(os.read(pipe_r, 2), b'OK')
# Case #2: Test 'CDataType_in_dll_impl' from Modules/_ctypes/_ctypes.c
with self.assertRaisesRegex(ValueError, "symbol 'foo' not found"):
c_int.in_dll(L, "foo")
# Assert that the IFUNC was called
self.assertEqual(os.read(pipe_r, 2), b'OK')
# Case #3: Test 'py_dl_sym' from Modules/_ctypes/callproc.c
dlopen = test.support.get_attribute(_ctypes, 'dlopen')
dlsym = test.support.get_attribute(_ctypes, 'dlsym')
L = dlopen(dstname)
with self.assertRaisesRegex(OSError, "symbol 'foo' not found"):
dlsym(L, "foo")
# Assert that the IFUNC was called
self.assertEqual(os.read(pipe_r, 2), b'OK')
@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls')
class TestLocalization(unittest.TestCase):
@staticmethod
def configure_locales(func):
return test.support.run_with_locale(
'LC_ALL',
'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk',
'fr_FR.utf8', 'en_US.utf8',
'',
)(func)
@classmethod
def setUpClass(cls):
cls.libc_filename = find_library("c")
if cls.libc_filename is None:
raise unittest.SkipTest('cannot find libc')
@configure_locales
def test_localized_error_from_dll(self):
dll = CDLL(self.libc_filename)
with self.assertRaises(AttributeError):
dll.this_name_does_not_exist
@configure_locales
def test_localized_error_in_dll(self):
dll = CDLL(self.libc_filename)
with self.assertRaises(ValueError):
c_int.in_dll(dll, 'this_name_does_not_exist')
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
'test requires _ctypes.dlopen()')
@configure_locales
def test_localized_error_dlopen(self):
missing_filename = b'missing\xff.so'
# Depending whether the locale, we may encode '\xff' differently
# but we are only interested in avoiding a UnicodeDecodeError
# when reporting the dlerror() error message which contains
# the localized filename.
filename_pattern = r'missing.*?\.so'
with self.assertRaisesRegex(OSError, filename_pattern):
_ctypes.dlopen(missing_filename, 2)
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
'test requires _ctypes.dlopen()')
@unittest.skipUnless(hasattr(_ctypes, 'dlsym'),
'test requires _ctypes.dlsym()')
@configure_locales
def test_localized_error_dlsym(self):
dll = _ctypes.dlopen(self.libc_filename)
with self.assertRaises(OSError):
_ctypes.dlsym(dll, 'this_name_does_not_exist')
if __name__ == "__main__":
unittest.main()
@@ -1,14 +1,18 @@
import unittest, os, errno
import ctypes
import errno
import os
import threading
from ctypes import *
import unittest
from ctypes import CDLL, c_int, c_char_p, c_wchar_p, get_errno, set_errno
from ctypes.util import find_library
class Test(unittest.TestCase):
def test_open(self):
libc_name = find_library("c")
if libc_name is None:
raise unittest.SkipTest("Unable to find C library")
self.skipTest("Unable to find C library")
libc = CDLL(libc_name, use_errno=True)
if os.name == "nt":
libc_open = libc._open
@@ -44,33 +48,34 @@ class Test(unittest.TestCase):
@unittest.skipUnless(os.name == "nt", 'Test specific to Windows')
def test_GetLastError(self):
dll = WinDLL("kernel32", use_last_error=True)
dll = ctypes.WinDLL("kernel32", use_last_error=True)
GetModuleHandle = dll.GetModuleHandleA
GetModuleHandle.argtypes = [c_wchar_p]
self.assertEqual(0, GetModuleHandle("foo"))
self.assertEqual(get_last_error(), 126)
self.assertEqual(ctypes.get_last_error(), 126)
self.assertEqual(set_last_error(32), 126)
self.assertEqual(get_last_error(), 32)
self.assertEqual(ctypes.set_last_error(32), 126)
self.assertEqual(ctypes.get_last_error(), 32)
def _worker():
set_last_error(0)
ctypes.set_last_error(0)
dll = WinDLL("kernel32", use_last_error=False)
dll = ctypes.WinDLL("kernel32", use_last_error=False)
GetModuleHandle = dll.GetModuleHandleW
GetModuleHandle.argtypes = [c_wchar_p]
GetModuleHandle("bar")
self.assertEqual(get_last_error(), 0)
self.assertEqual(ctypes.get_last_error(), 0)
t = threading.Thread(target=_worker)
t.start()
t.join()
self.assertEqual(get_last_error(), 32)
self.assertEqual(ctypes.get_last_error(), 32)
ctypes.set_last_error(0)
set_last_error(0)
if __name__ == "__main__":
unittest.main()
@@ -1,11 +1,12 @@
import unittest
import unittest.mock
import os.path
import sys
import test.support
from test.support import os_helper
from ctypes import *
import unittest
import unittest.mock
from ctypes import CDLL, RTLD_GLOBAL
from ctypes.util import find_library
from test.support import os_helper
# On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode.
class Test_OpenGL_libs(unittest.TestCase):
@@ -22,7 +23,7 @@ class Test_OpenGL_libs(unittest.TestCase):
lib_glu = find_library("GLU")
lib_gle = find_library("gle")
## print, for debugging
# print, for debugging
if test.support.verbose:
print("OpenGL libraries:")
for item in (("GL", lib_gl),
@@ -36,11 +37,13 @@ class Test_OpenGL_libs(unittest.TestCase):
cls.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
except OSError:
pass
if lib_glu:
try:
cls.glu = CDLL(lib_glu, RTLD_GLOBAL)
except OSError:
pass
if lib_gle:
try:
cls.gle = CDLL(lib_gle)
@@ -122,6 +125,32 @@ class FindLibraryLinux(unittest.TestCase):
unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None):
self.assertNotEqual(find_library('c'), None)
def test_gh114257(self):
self.assertIsNone(find_library("libc"))
@unittest.skipUnless(sys.platform == 'android', 'Test only valid for Android')
class FindLibraryAndroid(unittest.TestCase):
def test_find(self):
for name in [
"c", "m", # POSIX
"z", # Non-POSIX, but present on Linux
"log", # Not present on Linux
]:
with self.subTest(name=name):
path = find_library(name)
self.assertIsInstance(path, str)
self.assertEqual(
os.path.dirname(path),
"/system/lib64" if "64" in os.uname().machine
else "/system/lib")
self.assertEqual(os.path.basename(path), f"lib{name}.so")
self.assertTrue(os.path.isfile(path), path)
for name in ["libc", "nonexistent"]:
with self.subTest(name=name):
self.assertIsNone(find_library(name))
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,10 @@
from ctypes import *
import array
import gc
import unittest
from ctypes import (Structure, Union, Array, sizeof,
_Pointer, _SimpleCData, _CFuncPtr,
c_char, c_int)
class X(Structure):
_fields_ = [("c_int", c_int)]
@@ -9,6 +12,7 @@ class X(Structure):
def __init__(self):
self._init_called = True
class Test(unittest.TestCase):
def test_from_buffer(self):
a = array.array("i", range(16))
@@ -121,8 +125,6 @@ class Test(unittest.TestCase):
(c_int * 1).from_buffer_copy(a, 16 * sizeof(c_int))
def test_abstract(self):
from ctypes import _Pointer, _SimpleCData, _CFuncPtr
self.assertRaises(TypeError, Array.from_buffer, bytearray(10))
self.assertRaises(TypeError, Structure.from_buffer, bytearray(10))
self.assertRaises(TypeError, Union.from_buffer, bytearray(10))
@@ -137,5 +139,6 @@ class Test(unittest.TestCase):
self.assertRaises(TypeError, _Pointer.from_buffer_copy, b"123")
self.assertRaises(TypeError, _SimpleCData.from_buffer_copy, b"123")
if __name__ == '__main__':
unittest.main()
@@ -1,16 +1,41 @@
import ctypes
import unittest
from ctypes import *
from ctypes import (CDLL, Structure, CFUNCTYPE, sizeof, _CFuncPtr,
c_void_p, c_char_p, c_char, c_int, c_uint, c_long)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ._support import (_CData, PyCFuncPtrType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
try:
WINFUNCTYPE
except NameError:
WINFUNCTYPE = ctypes.WINFUNCTYPE
except AttributeError:
# fake to enable this test on Linux
WINFUNCTYPE = CFUNCTYPE
import _ctypes_test
lib = CDLL(_ctypes_test.__file__)
class CFuncPtrTestCase(unittest.TestCase):
def test_inheritance_hierarchy(self):
self.assertEqual(_CFuncPtr.mro(), [_CFuncPtr, _CData, object])
self.assertEqual(PyCFuncPtrType.__name__, "PyCFuncPtrType")
self.assertEqual(type(PyCFuncPtrType), type)
def test_type_flags(self):
for cls in _CFuncPtr, PyCFuncPtrType:
with self.subTest(cls=cls):
self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(_CFuncPtr.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Cannot call the metaclass __init__ more than once
CdeclCallback = CFUNCTYPE(c_int, c_int, c_int)
with self.assertRaisesRegex(SystemError, "already initialized"):
PyCFuncPtrType.__init__(CdeclCallback, 'ptr', (), {})
def test_basic(self):
X = WINFUNCTYPE(c_int, c_int, c_int)
@@ -20,8 +45,8 @@ class CFuncPtrTestCase(unittest.TestCase):
x = X(func)
self.assertEqual(x.restype, c_int)
self.assertEqual(x.argtypes, (c_int, c_int))
self.assertEqual(sizeof(x), sizeof(c_voidp))
self.assertEqual(sizeof(X), sizeof(c_voidp))
self.assertEqual(sizeof(x), sizeof(c_void_p))
self.assertEqual(sizeof(X), sizeof(c_void_p))
def test_first(self):
StdCallback = WINFUNCTYPE(c_int, c_int, c_int)
@@ -39,7 +64,7 @@ class CFuncPtrTestCase(unittest.TestCase):
# possible, as in C, to call cdecl functions with more parameters.
#self.assertRaises(TypeError, c, 1, 2, 3)
self.assertEqual(c(1, 2, 3, 4, 5, 6), 3)
if not WINFUNCTYPE is CFUNCTYPE:
if WINFUNCTYPE is not CFUNCTYPE:
self.assertRaises(TypeError, s, 1, 2, 3)
def test_structures(self):
@@ -69,17 +94,9 @@ class CFuncPtrTestCase(unittest.TestCase):
WNDPROC_2 = WINFUNCTYPE(c_long, c_int, c_int, c_int, c_int)
# This is no longer true, now that WINFUNCTYPE caches created types internally.
## # CFuncPtr subclasses are compared by identity, so this raises a TypeError:
## self.assertRaises(TypeError, setattr, wndclass,
## "lpfnWndProc", WNDPROC_2(wndproc))
# instead:
self.assertIs(WNDPROC, WNDPROC_2)
# 'wndclass.lpfnWndProc' leaks 94 references. Why?
self.assertEqual(wndclass.lpfnWndProc(1, 2, 3, 4), 10)
f = wndclass.lpfnWndProc
del wndclass
@@ -88,24 +105,14 @@ class CFuncPtrTestCase(unittest.TestCase):
self.assertEqual(f(10, 11, 12, 13), 46)
def test_dllfunctions(self):
def NoNullHandle(value):
if not value:
raise WinError()
return value
strchr = lib.my_strchr
strchr.restype = c_char_p
strchr.argtypes = (c_char_p, c_char)
self.assertEqual(strchr(b"abcdefghi", b"b"), b"bcdefghi")
self.assertEqual(strchr(b"abcdefghi", b"x"), None)
strtok = lib.my_strtok
strtok.restype = c_char_p
# Neither of this does work: strtok changes the buffer it is passed
## strtok.argtypes = (c_char_p, c_char_p)
## strtok.argtypes = (c_string, c_char_p)
def c_string(init):
size = len(init) + 1
@@ -114,19 +121,14 @@ class CFuncPtrTestCase(unittest.TestCase):
s = b"a\nb\nc"
b = c_string(s)
## b = (c_char * (len(s)+1))()
## b.value = s
## b = c_string(s)
self.assertEqual(strtok(b, b"\n"), b"a")
self.assertEqual(strtok(None, b"\n"), b"b")
self.assertEqual(strtok(None, b"\n"), b"c")
self.assertEqual(strtok(None, b"\n"), None)
def test_abstract(self):
from ctypes import _CFuncPtr
self.assertRaises(TypeError, _CFuncPtr, 13, "name", 42, "iid")
if __name__ == '__main__':
unittest.main()
@@ -1,30 +1,36 @@
"""
Here is probably the place to write the docs, since the test-cases
show how the type behave.
import ctypes
import sys
import unittest
from ctypes import (CDLL, Structure, Array, CFUNCTYPE,
byref, POINTER, pointer, ArgumentError,
c_char, c_wchar, c_byte, c_char_p, c_wchar_p,
c_short, c_int, c_long, c_longlong, c_void_p,
c_float, c_double, c_longdouble)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from _ctypes import _Pointer, _SimpleCData
Later...
"""
from ctypes import *
from ctypes.test import need_symbol
import sys, unittest
try:
WINFUNCTYPE
except NameError:
WINFUNCTYPE = ctypes.WINFUNCTYPE
except AttributeError:
# fake to enable this test on Linux
WINFUNCTYPE = CFUNCTYPE
import _ctypes_test
dll = CDLL(_ctypes_test.__file__)
if sys.platform == "win32":
windll = WinDLL(_ctypes_test.__file__)
windll = ctypes.WinDLL(_ctypes_test.__file__)
class POINT(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
class RECT(Structure):
_fields_ = [("left", c_int), ("top", c_int),
("right", c_int), ("bottom", c_int)]
class FunctionTestCase(unittest.TestCase):
def test_mro(self):
@@ -40,21 +46,34 @@ class FunctionTestCase(unittest.TestCase):
_length_ = 5
_type_ = "i"
from _ctypes import _Pointer
with self.assertRaises(TypeError):
class X(object, _Pointer):
class X2(object, _Pointer):
pass
from _ctypes import _SimpleCData
with self.assertRaises(TypeError):
class X(object, _SimpleCData):
class X3(object, _SimpleCData):
_type_ = "i"
with self.assertRaises(TypeError):
class X(object, Structure):
class X4(object, Structure):
_fields_ = []
@need_symbol('c_wchar')
def test_c_char_parm(self):
proto = CFUNCTYPE(c_int, c_char)
def callback(*args):
return 0
callback = proto(callback)
self.assertEqual(callback(b"a"), 0)
with self.assertRaises(ArgumentError) as cm:
callback(b"abc")
self.assertEqual(str(cm.exception),
"argument 1: TypeError: one character bytes, "
"bytearray or integer expected")
def test_wchar_parm(self):
f = dll._testfunc_i_bhilfd
f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double]
@@ -62,7 +81,66 @@ class FunctionTestCase(unittest.TestCase):
self.assertEqual(result, 139)
self.assertEqual(type(result), int)
@need_symbol('c_wchar')
with self.assertRaises(ArgumentError) as cm:
f(1, 2, 3, 4, 5.0, 6.0)
self.assertEqual(str(cm.exception),
"argument 2: TypeError: unicode string expected "
"instead of int instance")
with self.assertRaises(ArgumentError) as cm:
f(1, "abc", 3, 4, 5.0, 6.0)
self.assertEqual(str(cm.exception),
"argument 2: TypeError: one character unicode string "
"expected")
def test_c_char_p_parm(self):
"""Test the error message when converting an incompatible type to c_char_p."""
proto = CFUNCTYPE(c_int, c_char_p)
def callback(*args):
return 0
callback = proto(callback)
self.assertEqual(callback(b"abc"), 0)
with self.assertRaises(ArgumentError) as cm:
callback(10)
self.assertEqual(str(cm.exception),
"argument 1: TypeError: 'int' object cannot be "
"interpreted as ctypes.c_char_p")
def test_c_wchar_p_parm(self):
"""Test the error message when converting an incompatible type to c_wchar_p."""
proto = CFUNCTYPE(c_int, c_wchar_p)
def callback(*args):
return 0
callback = proto(callback)
self.assertEqual(callback("abc"), 0)
with self.assertRaises(ArgumentError) as cm:
callback(10)
self.assertEqual(str(cm.exception),
"argument 1: TypeError: 'int' object cannot be "
"interpreted as ctypes.c_wchar_p")
def test_c_void_p_parm(self):
"""Test the error message when converting an incompatible type to c_void_p."""
proto = CFUNCTYPE(c_int, c_void_p)
def callback(*args):
return 0
callback = proto(callback)
self.assertEqual(callback(5), 0)
with self.assertRaises(ArgumentError) as cm:
callback(2.5)
self.assertEqual(str(cm.exception),
"argument 1: TypeError: 'float' object cannot be "
"interpreted as ctypes.c_void_p")
def test_wchar_result(self):
f = dll._testfunc_i_bhilfd
f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
@@ -128,7 +206,6 @@ class FunctionTestCase(unittest.TestCase):
self.assertEqual(result, -21)
self.assertEqual(type(result), float)
@need_symbol('c_longdouble')
def test_longdoubleresult(self):
f = dll._testfunc_D_bhilfD
f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_longdouble]
@@ -141,7 +218,6 @@ class FunctionTestCase(unittest.TestCase):
self.assertEqual(result, -21)
self.assertEqual(type(result), float)
@need_symbol('c_longlong')
def test_longlongresult(self):
f = dll._testfunc_q_bhilfd
f.restype = c_longlong
@@ -200,7 +276,6 @@ class FunctionTestCase(unittest.TestCase):
result = f(byref(c_int(99)))
self.assertNotEqual(result.contents, 99)
################################################################
def test_shorts(self):
f = dll._testfunc_callback_i_if
@@ -218,9 +293,6 @@ class FunctionTestCase(unittest.TestCase):
f(2**18, cb)
self.assertEqual(args, expected)
################################################################
def test_callbacks(self):
f = dll._testfunc_callback_i_if
f.restype = c_int
@@ -229,7 +301,6 @@ class FunctionTestCase(unittest.TestCase):
MyCallback = CFUNCTYPE(c_int, c_int)
def callback(value):
#print "called back with", value
return value
cb = MyCallback(callback)
@@ -262,7 +333,6 @@ class FunctionTestCase(unittest.TestCase):
f.argtypes = [c_int, MyCallback]
def callback(value):
#print "called back with", value
self.assertEqual(type(value), int)
return value
@@ -270,7 +340,6 @@ class FunctionTestCase(unittest.TestCase):
result = f(-10, cb)
self.assertEqual(result, -18)
@need_symbol('c_longlong')
def test_longlong_callbacks(self):
f = dll._testfunc_callback_q_qf
@@ -371,7 +440,7 @@ class FunctionTestCase(unittest.TestCase):
(9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
def test_sf1651235(self):
# see https://www.python.org/sf/1651235
# see https://bugs.python.org/issue1651235
proto = CFUNCTYPE(c_int, RECT, POINT)
def callback(*args):
@@ -380,5 +449,6 @@ class FunctionTestCase(unittest.TestCase):
callback = proto(callback)
self.assertRaises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT()))
if __name__ == '__main__':
unittest.main()
@@ -1,12 +1,14 @@
import ctypes
import unittest
from ctypes import *
import warnings
from ctypes import Structure, POINTER, pointer, c_char_p
################################################################
#
# The incomplete pointer example from the tutorial
#
class MyTestCase(unittest.TestCase):
class TestSetPointerType(unittest.TestCase):
def tearDown(self):
# to not leak references, we must clean _pointer_type_cache
ctypes._reset_cache()
def test_incomplete_example(self):
lpcell = POINTER("cell")
@@ -14,7 +16,9 @@ class MyTestCase(unittest.TestCase):
_fields_ = [("name", c_char_p),
("next", lpcell)]
SetPointerType(lpcell, cell)
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
ctypes.SetPointerType(lpcell, cell)
c1 = cell()
c1.name = b"foo"
@@ -32,11 +36,15 @@ class MyTestCase(unittest.TestCase):
p = p.next[0]
self.assertEqual(result, [b"foo", b"bar"] * 4)
# to not leak references, we must clean _pointer_type_cache
from ctypes import _pointer_type_cache
del _pointer_type_cache[cell]
def test_deprecation(self):
lpcell = POINTER("cell")
class cell(Structure):
_fields_ = [("name", c_char_p),
("next", lpcell)]
with self.assertWarns(DeprecationWarning):
ctypes.SetPointerType(lpcell, cell)
################################################################
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,6 @@
from ctypes import *
import unittest
from ctypes import Structure, c_int
class X(Structure):
_fields_ = [("a", c_int),
@@ -15,6 +16,7 @@ class X(Structure):
self.a = 9
self.b = 12
class Y(Structure):
_fields_ = [("x", X)]
@@ -36,5 +38,6 @@ class InitTest(unittest.TestCase):
self.assertEqual((y.x.a, y.x.b), (9, 12))
self.assertEqual(y.x.new_was_called, False)
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,4 @@
# This tests the internal _objects attribute
import unittest
from ctypes import *
from sys import getrefcount as grc
# XXX This test must be reviewed for correctness!!!
@@ -14,22 +11,29 @@ from sys import getrefcount as grc
#
# What about pointers?
import sys
import unittest
from ctypes import Structure, POINTER, c_char_p, c_int
class ObjectsTestCase(unittest.TestCase):
def assertSame(self, a, b):
self.assertEqual(id(a), id(b))
def test_ints(self):
i = 42000123
refcnt = grc(i)
refcnt = sys.getrefcount(i)
ci = c_int(i)
self.assertEqual(refcnt, grc(i))
self.assertEqual(refcnt, sys.getrefcount(i))
self.assertEqual(ci._objects, None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_c_char_p(self):
s = b"Hello, World"
refcnt = grc(s)
s = "Hello, World".encode("ascii")
refcnt = sys.getrefcount(s)
cs = c_char_p(s)
self.assertEqual(refcnt + 1, grc(s))
self.assertEqual(refcnt + 1, sys.getrefcount(s))
self.assertSame(cs._objects, s)
def test_simple_struct(self):
@@ -60,6 +64,8 @@ class ObjectsTestCase(unittest.TestCase):
x1.a, x2.b = 42, 93
self.assertEqual(y._objects, {"0": {}, "1": {}})
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_xxx(self):
class X(Structure):
_fields_ = [("a", c_char_p), ("b", c_char_p)]
@@ -78,9 +84,6 @@ class ObjectsTestCase(unittest.TestCase):
y = Y()
y.x = x
self.assertEqual(y._objects, {"0": {"0": s1, "1": s2}})
## x = y.x
## del y
## print x._b_base_._objects
def test_ptr_struct(self):
class X(Structure):
@@ -92,9 +95,7 @@ class ObjectsTestCase(unittest.TestCase):
x = X()
x.data = a
##XXX print x._objects
##XXX print x.data[0]
##XXX print x.data._objects
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,7 @@
from ctypes import *
import unittest
from ctypes import (Structure, POINTER, pointer, _pointer_type_cache,
c_char_p, c_int)
class SimpleTestCase(unittest.TestCase):
def test_cint(self):
@@ -10,6 +12,8 @@ class SimpleTestCase(unittest.TestCase):
x = c_int(99)
self.assertEqual(x._objects, None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_ccharp(self):
x = c_char_p()
self.assertEqual(x._objects, None)
@@ -18,6 +22,7 @@ class SimpleTestCase(unittest.TestCase):
x = c_char_p(b"spam")
self.assertEqual(x._objects, b"spam")
class StructureTestCase(unittest.TestCase):
def test_cint_struct(self):
class X(Structure):
@@ -30,6 +35,8 @@ class StructureTestCase(unittest.TestCase):
x.b = 99
self.assertEqual(x._objects, None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_ccharp_struct(self):
class X(Structure):
_fields_ = [("a", c_char_p),
@@ -64,6 +71,7 @@ class StructureTestCase(unittest.TestCase):
r.lr = POINT()
self.assertEqual(r._objects, {'0': {}, '1': {}})
class ArrayTestCase(unittest.TestCase):
def test_cint_array(self):
INTARR = c_int * 3
@@ -87,43 +95,13 @@ class ArrayTestCase(unittest.TestCase):
x.a = ia
self.assertEqual(x._objects, {'1': {}})
class PointerTestCase(unittest.TestCase):
def test_p_cint(self):
i = c_int(42)
x = pointer(i)
self.assertEqual(x._objects, {'1': i})
class DeletePointerTestCase(unittest.TestCase):
@unittest.skip('test disabled')
def test_X(self):
class X(Structure):
_fields_ = [("p", POINTER(c_char_p))]
x = X()
i = c_char_p("abc def")
from sys import getrefcount as grc
print("2?", grc(i))
x.p = pointer(i)
print("3?", grc(i))
for i in range(320):
c_int(99)
x.p[0]
print(x.p[0])
## del x
## print "2?", grc(i)
## del i
import gc
gc.collect()
for i in range(320):
c_int(99)
x.p[0]
print(x.p[0])
print(x.p.contents)
## print x._objects
x.p[0] = "spam spam"
## print x.p[0]
print("+" * 42)
print(x._objects)
class PointerToStructure(unittest.TestCase):
def test(self):
@@ -137,17 +115,14 @@ class PointerToStructure(unittest.TestCase):
r.a = pointer(p1)
r.b = pointer(p1)
## from pprint import pprint as pp
## pp(p1._objects)
## pp(r._objects)
r.a[0].x = 42
r.a[0].y = 99
# to avoid leaking when tests are run several times
# clean up the types left in the cache.
from ctypes import _pointer_type_cache
del _pointer_type_cache[POINT]
if __name__ == "__main__":
unittest.main()
@@ -1,20 +1,24 @@
import math
import unittest
from ctypes import (CDLL, CFUNCTYPE, POINTER, create_string_buffer, sizeof,
c_void_p, c_char, c_int, c_double, c_size_t)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ctypes import *
import _ctypes_test
lib = CDLL(_ctypes_test.__file__)
def three_way_cmp(x, y):
"""Return -1 if x < y, 0 if x == y and 1 if x > y"""
return (x > y) - (x < y)
class LibTest(unittest.TestCase):
def test_sqrt(self):
lib.my_sqrt.argtypes = c_double,
lib.my_sqrt.restype = c_double
self.assertEqual(lib.my_sqrt(4.0), 2.0)
import math
self.assertEqual(lib.my_sqrt(2.0), math.sqrt(2.0))
def test_qsort(self):
@@ -29,5 +33,6 @@ class LibTest(unittest.TestCase):
lib.my_qsort(chars, len(chars)-1, sizeof(c_char), comparefunc(sort))
self.assertEqual(chars.raw, b" ,,aaaadmmmnpppsss\x00")
if __name__ == "__main__":
unittest.main()
@@ -1,16 +1,20 @@
from ctypes import *
import _ctypes
import ctypes
import os
import shutil
import subprocess
import sys
import unittest
import test.support
from test.support import import_helper
from test.support import os_helper
import unittest
from ctypes import CDLL, cdll, addressof, c_void_p, c_char_p
from ctypes.util import find_library
from test.support import import_helper, os_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
libc_name = None
def setUpModule():
global libc_name
if os.name == "nt":
@@ -23,15 +27,22 @@ def setUpModule():
if test.support.verbose:
print("libc_name is", libc_name)
class LoaderTest(unittest.TestCase):
unknowndll = "xxrandomnamexx"
def test_load(self):
if libc_name is None:
self.skipTest('could not find libc')
CDLL(libc_name)
CDLL(os.path.basename(libc_name))
if libc_name is not None:
test_lib = libc_name
else:
if os.name == "nt":
test_lib = _ctypes_test.__file__
else:
self.skipTest('could not find library to load')
CDLL(test_lib)
CDLL(os.path.basename(test_lib))
CDLL(os_helper.FakePath(test_lib))
self.assertRaises(OSError, CDLL, self.unknowndll)
def test_load_version(self):
@@ -45,11 +56,15 @@ class LoaderTest(unittest.TestCase):
self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll)
def test_find(self):
found = False
for name in ("c", "m"):
lib = find_library(name)
if lib:
found = True
cdll.LoadLibrary(lib)
CDLL(lib)
if not found:
self.skipTest("Could not find c and m libraries")
@unittest.skipUnless(os.name == "nt",
'test specific to Windows')
@@ -62,18 +77,17 @@ class LoaderTest(unittest.TestCase):
print(find_library("user32"))
if os.name == "nt":
windll.kernel32.GetModuleHandleW
windll["kernel32"].GetModuleHandleW
windll.LoadLibrary("kernel32").GetModuleHandleW
WinDLL("kernel32").GetModuleHandleW
ctypes.windll.kernel32.GetModuleHandleW
ctypes.windll["kernel32"].GetModuleHandleW
ctypes.windll.LoadLibrary("kernel32").GetModuleHandleW
ctypes.WinDLL("kernel32").GetModuleHandleW
# embedded null character
self.assertRaises(ValueError, windll.LoadLibrary, "kernel32\0")
self.assertRaises(ValueError, ctypes.windll.LoadLibrary, "kernel32\0")
@unittest.skipUnless(os.name == "nt",
'test specific to Windows')
def test_load_ordinal_functions(self):
import _ctypes_test
dll = WinDLL(_ctypes_test.__file__)
dll = ctypes.WinDLL(_ctypes_test.__file__)
# We load the same function both via ordinal and name
func_ord = dll[2]
func_name = dll.GetString
@@ -86,16 +100,21 @@ class LoaderTest(unittest.TestCase):
self.assertRaises(AttributeError, dll.__getitem__, 1234)
@unittest.skipUnless(os.name == "nt", 'Windows-specific test')
def test_load_without_name_and_with_handle(self):
handle = ctypes.windll.kernel32._handle
lib = ctypes.WinDLL(name=None, handle=handle)
self.assertIs(handle, lib._handle)
@unittest.skipUnless(os.name == "nt", 'Windows-specific test')
def test_1703286_A(self):
from _ctypes import LoadLibrary, FreeLibrary
# On winXP 64-bit, advapi32 loads at an address that does
# NOT fit into a 32-bit integer. FreeLibrary must be able
# to accept this address.
# These are tests for https://www.python.org/sf/1703286
handle = LoadLibrary("advapi32")
FreeLibrary(handle)
# These are tests for https://bugs.python.org/issue1703286
handle = _ctypes.LoadLibrary("advapi32")
_ctypes.FreeLibrary(handle)
@unittest.skipUnless(os.name == "nt", 'Windows-specific test')
def test_1703286_B(self):
@@ -103,25 +122,33 @@ class LoaderTest(unittest.TestCase):
# above, the (arbitrarily selected) CloseEventLog function
# also has a high address. 'call_function' should accept
# addresses so large.
from _ctypes import call_function
advapi32 = windll.advapi32
advapi32 = ctypes.windll.advapi32
# Calling CloseEventLog with a NULL argument should fail,
# but the call should not segfault or so.
self.assertEqual(0, advapi32.CloseEventLog(None))
windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
windll.kernel32.GetProcAddress.restype = c_void_p
proc = windll.kernel32.GetProcAddress(advapi32._handle,
b"CloseEventLog")
kernel32 = ctypes.windll.kernel32
kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
kernel32.GetProcAddress.restype = c_void_p
proc = kernel32.GetProcAddress(advapi32._handle, b"CloseEventLog")
self.assertTrue(proc)
# This is the real test: call the function via 'call_function'
self.assertEqual(0, call_function(proc, (None,)))
self.assertEqual(0, _ctypes.call_function(proc, (None,)))
@unittest.skipUnless(os.name == "nt",
'test specific to Windows')
def test_load_hasattr(self):
# bpo-34816: shouldn't raise OSError
self.assertFalse(hasattr(ctypes.windll, 'test'))
@unittest.skipUnless(os.name == "nt",
'test specific to Windows')
def test_load_dll_with_flags(self):
_sqlite3 = import_helper.import_module("_sqlite3")
src = _sqlite3.__file__
if src.lower().endswith("_d.pyd"):
if os.path.basename(src).partition(".")[0].lower().endswith("_d"):
ext = "_d.dll"
else:
ext = ".dll"
@@ -177,6 +204,5 @@ class LoaderTest(unittest.TestCase):
"WinDLL('_sqlite3.dll'); p.close()")
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,3 @@
import os
import sys
import unittest
# Bob Ippolito:
#
# Ok.. the code to find the filename for __getattr__ should look
@@ -31,10 +27,15 @@ import unittest
#
# -bob
import os
import sys
import unittest
from ctypes.macholib.dyld import dyld_find
from ctypes.macholib.dylib import dylib_info
from ctypes.macholib.framework import framework_info
def find_lib(name):
possible = ['lib'+name+'.dylib', name+'.dylib', name+'.framework/'+name]
for dylib in possible:
@@ -106,5 +107,6 @@ class MachOTest(unittest.TestCase):
self.assertEqual(framework_info('P/F.framework/Versions/A/F_debug'),
d('P', 'F.framework/Versions/A/F_debug', 'F', 'A', 'debug'))
if __name__ == "__main__":
unittest.main()
@@ -1,20 +1,23 @@
import sys
from test import support
import unittest
from ctypes import *
from ctypes.test import need_symbol
from test import support
from ctypes import (POINTER, sizeof, cast,
create_string_buffer, string_at,
create_unicode_buffer, wstring_at,
memmove, memset,
c_char_p, c_byte, c_ubyte, c_wchar)
class MemFunctionsTest(unittest.TestCase):
@unittest.skip('test disabled')
def test_overflow(self):
# string_at and wstring_at must use the Python calling
# convention (which acquires the GIL and checks the Python
# error flag). Provoke an error and catch it; see also issue
# #3554: <http://bugs.python.org/issue3554>
# gh-47804.
self.assertRaises((OverflowError, MemoryError, SystemError),
lambda: wstring_at(u"foo", sys.maxint - 1))
lambda: wstring_at(u"foo", sys.maxsize - 1))
self.assertRaises((OverflowError, MemoryError, SystemError),
lambda: string_at("foo", sys.maxint - 1))
lambda: string_at("foo", sys.maxsize - 1))
def test_memmove(self):
# large buffers apparently increase the chance that the memory
@@ -63,7 +66,6 @@ class MemFunctionsTest(unittest.TestCase):
self.assertEqual(string_at(b"foo bar", 7), b"foo bar")
self.assertEqual(string_at(b"foo bar", 3), b"foo")
@need_symbol('create_unicode_buffer')
def test_wstring_at(self):
p = create_unicode_buffer("Hello, World")
a = create_unicode_buffer(1000000)
@@ -75,5 +77,6 @@ class MemFunctionsTest(unittest.TestCase):
self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0")
self.assertEqual(wstring_at(a, 0), "")
if __name__ == "__main__":
unittest.main()
@@ -1,6 +1,13 @@
from ctypes import *
import unittest
import array
import struct
import sys
import unittest
from operator import truth
from ctypes import (byref, sizeof, alignment,
c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong,
c_float, c_double, c_longdouble, c_bool)
def valid_ranges(*types):
# given a sequence of numeric types, collect their _type_
@@ -19,36 +26,18 @@ def valid_ranges(*types):
result.append((min(a, b, c, d), max(a, b, c, d)))
return result
ArgType = type(byref(c_int(0)))
unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong]
unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong]
signed_types = [c_byte, c_short, c_int, c_long, c_longlong]
bool_types = []
bool_types = [c_bool]
float_types = [c_double, c_float]
try:
c_ulonglong
c_longlong
except NameError:
pass
else:
unsigned_types.append(c_ulonglong)
signed_types.append(c_longlong)
try:
c_bool
except NameError:
pass
else:
bool_types.append(c_bool)
unsigned_ranges = valid_ranges(*unsigned_types)
signed_ranges = valid_ranges(*signed_types)
bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]]
################################################################
class NumberTestCase(unittest.TestCase):
@@ -71,7 +60,6 @@ class NumberTestCase(unittest.TestCase):
self.assertEqual(t(h).value, h)
def test_bool_values(self):
from operator import truth
for t, v in zip(bool_types, bool_values):
self.assertEqual(t(v).value, truth(v))
@@ -147,21 +135,20 @@ class NumberTestCase(unittest.TestCase):
# alignment of the type...
self.assertEqual((code, alignment(t)),
(code, align))
(code, align))
# and alignment of an instance
self.assertEqual((code, alignment(t())),
(code, align))
(code, align))
def test_int_from_address(self):
from array import array
for t in signed_types + unsigned_types:
# the array module doesn't support all format codes
# (no 'q' or 'Q')
try:
array(t._type_)
array.array(t._type_)
except ValueError:
continue
a = array(t._type_, [100])
a = array.array(t._type_, [100])
# v now is an integer at an 'external' memory location
v = t.from_address(a.buffer_info()[0])
@@ -174,9 +161,8 @@ class NumberTestCase(unittest.TestCase):
def test_float_from_address(self):
from array import array
for t in float_types:
a = array(t._type_, [3.14])
a = array.array(t._type_, [3.14])
v = t.from_address(a.buffer_info()[0])
self.assertEqual(v.value, a[0])
self.assertIs(type(v), t)
@@ -185,10 +171,7 @@ class NumberTestCase(unittest.TestCase):
self.assertIs(type(v), t)
def test_char_from_address(self):
from ctypes import c_char
from array import array
a = array('b', [0])
a = array.array('b', [0])
a[0] = ord('x')
v = c_char.from_address(a.buffer_info()[0])
self.assertEqual(v.value, b'x')
@@ -204,7 +187,6 @@ class NumberTestCase(unittest.TestCase):
self.assertRaises(TypeError, c_int, c_long(42))
def test_float_overflow(self):
import sys
big_int = int(sys.float_info.max) * 2
for t in float_types + [c_longdouble]:
self.assertRaises(OverflowError, t, big_int)
@@ -11,7 +11,7 @@ is None.
Here is an array of string pointers:
>>> from ctypes import *
>>> from ctypes import Structure, c_int, c_char_p
>>> array = (c_char_p * 5)()
>>> print(array._objects)
None
@@ -42,7 +42,7 @@ The'array' attribute of the 'x' object shares part of the memory buffer
of 'x' ('_b_base_' is either None, or the root object owning the memory block):
>>> print(x.array._b_base_) # doctest: +ELLIPSIS
<ctypes.test.test_objects.X object at 0x...>
<test.test_ctypes.test_objects.X object at 0x...>
>>>
>>> x.array[0] = b'spam spam spam'
@@ -51,17 +51,17 @@ of 'x' ('_b_base_' is either None, or the root object owning the memory block):
>>> x.array._b_base_._objects
{'0:2': b'spam spam spam'}
>>>
'''
import unittest, doctest
import doctest
import unittest
import ctypes.test.test_objects
class TestCase(unittest.TestCase):
def test(self):
failures, tests = doctest.testmod(ctypes.test.test_objects)
self.assertFalse(failures, 'doctests failed, see output above')
def load_tests(loader, tests, pattern):
# TODO: RUSTPYTHON - doctest disabled due to null terminator in _objects
# tests.addTest(doctest.DocTestSuite())
return tests
if __name__ == '__main__':
doctest.testmod(ctypes.test.test_objects)
unittest.main()
@@ -1,11 +1,25 @@
import sys
import unittest
from ctypes.test import need_symbol
import test.support
from ctypes import (CDLL, PyDLL, ArgumentError,
Structure, Array, Union,
_Pointer, _SimpleCData, _CFuncPtr,
POINTER, pointer, byref,
c_void_p, c_char_p, c_wchar_p, py_object,
c_bool,
c_char, c_wchar,
c_byte, c_ubyte,
c_short, c_ushort,
c_int, c_uint,
c_long, c_ulong,
c_longlong, c_ulonglong,
c_float, c_double, c_longdouble)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
class SimpleTypesTestCase(unittest.TestCase):
def setUp(self):
import ctypes
try:
from _ctypes import set_conversion_mode
except ImportError:
@@ -22,7 +36,6 @@ class SimpleTypesTestCase(unittest.TestCase):
set_conversion_mode(*self.prev_conv_mode)
def test_subclasses(self):
from ctypes import c_void_p, c_char_p
# ctypes 0.9.5 and before did overwrite from_param in SimpleType_new
class CVOIDP(c_void_p):
def from_param(cls, value):
@@ -37,10 +50,7 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertEqual(CVOIDP.from_param("abc"), "abcabc")
self.assertEqual(CCHARP.from_param("abc"), "abcabcabcabc")
@need_symbol('c_wchar_p')
def test_subclasses_c_wchar_p(self):
from ctypes import c_wchar_p
class CWCHARP(c_wchar_p):
def from_param(cls, value):
return value * 3
@@ -50,8 +60,6 @@ class SimpleTypesTestCase(unittest.TestCase):
# XXX Replace by c_char_p tests
def test_cstrings(self):
from ctypes import c_char_p
# c_char_p.from_param on a Python String packs the string
# into a cparam object
s = b"123"
@@ -67,10 +75,7 @@ class SimpleTypesTestCase(unittest.TestCase):
a = c_char_p(b"123")
self.assertIs(c_char_p.from_param(a), a)
@need_symbol('c_wchar_p')
def test_cw_strings(self):
from ctypes import c_wchar_p
c_wchar_p.from_param("123")
self.assertRaises(TypeError, c_wchar_p.from_param, 42)
@@ -79,12 +84,27 @@ class SimpleTypesTestCase(unittest.TestCase):
pa = c_wchar_p.from_param(c_wchar_p("123"))
self.assertEqual(type(pa), c_wchar_p)
def test_c_char(self):
with self.assertRaises(TypeError) as cm:
c_char.from_param(b"abc")
self.assertEqual(str(cm.exception),
"one character bytes, bytearray or integer expected")
def test_c_wchar(self):
with self.assertRaises(TypeError) as cm:
c_wchar.from_param("abc")
self.assertEqual(str(cm.exception),
"one character unicode string expected")
with self.assertRaises(TypeError) as cm:
c_wchar.from_param(123)
self.assertEqual(str(cm.exception),
"unicode string expected instead of int instance")
def test_int_pointers(self):
from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer
LPINT = POINTER(c_int)
## p = pointer(c_int(42))
## x = LPINT.from_param(p)
x = LPINT.from_param(pointer(c_int(42)))
self.assertEqual(x.contents.value, 42)
self.assertEqual(LPINT(c_int(42)).contents.value, 42)
@@ -99,7 +119,6 @@ class SimpleTypesTestCase(unittest.TestCase):
def test_byref_pointer(self):
# The from_param class method of POINTER(typ) classes accepts what is
# returned by byref(obj), it type(obj) == typ
from ctypes import c_short, c_uint, c_int, c_long, POINTER, byref
LPINT = POINTER(c_int)
LPINT.from_param(byref(c_int(42)))
@@ -111,7 +130,6 @@ class SimpleTypesTestCase(unittest.TestCase):
def test_byref_pointerpointer(self):
# See above
from ctypes import c_short, c_uint, c_int, c_long, pointer, POINTER, byref
LPLPINT = POINTER(POINTER(c_int))
LPLPINT.from_param(byref(pointer(c_int(42))))
@@ -122,7 +140,6 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertRaises(TypeError, LPLPINT.from_param, byref(pointer(c_uint(22))))
def test_array_pointers(self):
from ctypes import c_short, c_uint, c_int, c_long, POINTER
INTARRAY = c_int * 3
ia = INTARRAY()
self.assertEqual(len(ia), 3)
@@ -137,15 +154,12 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertRaises(TypeError, LPINT.from_param, c_uint*3)
def test_noctypes_argtype(self):
import _ctypes_test
from ctypes import CDLL, c_void_p, ArgumentError
func = CDLL(_ctypes_test.__file__)._testfunc_p_p
func.restype = c_void_p
# TypeError: has no from_param method
self.assertRaises(TypeError, setattr, func, "argtypes", (object,))
class Adapter(object):
class Adapter:
def from_param(cls, obj):
return None
@@ -153,7 +167,7 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertEqual(func(None), None)
self.assertEqual(func(object()), None)
class Adapter(object):
class Adapter:
def from_param(cls, obj):
return obj
@@ -162,7 +176,7 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertRaises(ArgumentError, func, object())
self.assertEqual(func(c_void_p(42)), 42)
class Adapter(object):
class Adapter:
def from_param(cls, obj):
raise ValueError(obj)
@@ -171,9 +185,6 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertRaises(ArgumentError, func, 99)
def test_abstract(self):
from ctypes import (Array, Structure, Union, _Pointer,
_SimpleCData, _CFuncPtr)
self.assertRaises(TypeError, Array.from_param, 42)
self.assertRaises(TypeError, Structure.from_param, 42)
self.assertRaises(TypeError, Union.from_param, 42)
@@ -185,7 +196,6 @@ class SimpleTypesTestCase(unittest.TestCase):
def test_issue31311(self):
# __setstate__ should neither raise a SystemError nor crash in case
# of a bad __dict__.
from ctypes import Structure
class BadStruct(Structure):
@property
@@ -202,27 +212,6 @@ class SimpleTypesTestCase(unittest.TestCase):
WorseStruct().__setstate__({}, b'foo')
def test_parameter_repr(self):
from ctypes import (
c_bool,
c_char,
c_wchar,
c_byte,
c_ubyte,
c_short,
c_ushort,
c_int,
c_uint,
c_long,
c_ulong,
c_longlong,
c_ulonglong,
c_float,
c_double,
c_longdouble,
c_char_p,
c_wchar_p,
c_void_p,
)
self.assertRegex(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
self.assertEqual(repr(c_char.from_param(97)), "<cparam 'c' ('a')>")
self.assertRegex(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
@@ -238,13 +227,62 @@ class SimpleTypesTestCase(unittest.TestCase):
self.assertRegex(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
if sys.float_repr_style == 'short':
self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
self.assertRegex(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
self.assertRegex(repr(c_char_p.from_param(b'hihi')), r"^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
self.assertRegex(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
################################################################
@test.support.cpython_only
def test_from_param_result_refcount(self):
# Issue #99952
class X(Structure):
"""This struct size is <= sizeof(void*)."""
_fields_ = [("a", c_void_p)]
def __del__(self):
trace.append(4)
@classmethod
def from_param(cls, value):
trace.append(2)
return cls()
PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append
PyList_Append.restype = c_int
PyList_Append.argtypes = [py_object, py_object, X]
trace = []
trace.append(1)
PyList_Append(trace, 3, "dummy")
trace.append(5)
self.assertEqual(trace, [1, 2, 3, 4, 5])
class Y(Structure):
"""This struct size is > sizeof(void*)."""
_fields_ = [("a", c_void_p), ("b", c_void_p)]
def __del__(self):
trace.append(4)
@classmethod
def from_param(cls, value):
trace.append(2)
return cls()
PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append
PyList_Append.restype = c_int
PyList_Append.argtypes = [py_object, py_object, Y]
trace = []
trace.append(1)
PyList_Append(trace, 3, "dummy")
trace.append(5)
self.assertEqual(trace, [1, 2, 3, 4, 5])
if __name__ == '__main__':
unittest.main()
@@ -1,6 +1,13 @@
import re
import sys
import unittest
from ctypes import *
import re, sys
from ctypes import (CFUNCTYPE, POINTER, sizeof, Union,
Structure, LittleEndianStructure, BigEndianStructure,
c_char, c_byte, c_ubyte,
c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong, c_uint64,
c_bool, c_float, c_double, c_longdouble, py_object)
if sys.byteorder == "little":
THIS_ENDIAN = "<"
@@ -9,6 +16,7 @@ else:
THIS_ENDIAN = ">"
OTHER_ENDIAN = "<"
def normalize(format):
# Remove current endian specifier and white space from a format
# string
@@ -17,65 +25,54 @@ def normalize(format):
format = format.replace(OTHER_ENDIAN, THIS_ENDIAN)
return re.sub(r"\s", "", format)
class Test(unittest.TestCase):
class Test(unittest.TestCase):
def test_native_types(self):
for tp, fmt, shape, itemtp in native_types:
ob = tp()
v = memoryview(ob)
try:
self.assertEqual(normalize(v.format), normalize(fmt))
if shape:
self.assertEqual(len(v), shape[0])
else:
self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
self.assertEqual(v.itemsize, sizeof(itemtp))
self.assertEqual(v.shape, shape)
# XXX Issue #12851: PyCData_NewGetBuffer() must provide strides
# if requested. memoryview currently reconstructs missing
# stride information, so this assert will fail.
# self.assertEqual(v.strides, ())
self.assertEqual(normalize(v.format), normalize(fmt))
if shape:
self.assertEqual(len(v), shape[0])
else:
self.assertRaises(TypeError, len, v)
self.assertEqual(v.itemsize, sizeof(itemtp))
self.assertEqual(v.shape, shape)
# XXX Issue #12851: PyCData_NewGetBuffer() must provide strides
# if requested. memoryview currently reconstructs missing
# stride information, so this assert will fail.
# self.assertEqual(v.strides, ())
# they are always read/write
self.assertFalse(v.readonly)
# they are always read/write
self.assertFalse(v.readonly)
if v.shape:
n = 1
for dim in v.shape:
n = n * dim
self.assertEqual(n * v.itemsize, len(v.tobytes()))
except:
# so that we can see the failing type
print(tp)
raise
n = 1
for dim in v.shape:
n = n * dim
self.assertEqual(n * v.itemsize, len(v.tobytes()))
def test_endian_types(self):
for tp, fmt, shape, itemtp in endian_types:
ob = tp()
v = memoryview(ob)
try:
self.assertEqual(v.format, fmt)
if shape:
self.assertEqual(len(v), shape[0])
else:
self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
self.assertEqual(v.itemsize, sizeof(itemtp))
self.assertEqual(v.shape, shape)
# XXX Issue #12851
# self.assertEqual(v.strides, ())
self.assertEqual(v.format, fmt)
if shape:
self.assertEqual(len(v), shape[0])
else:
self.assertRaises(TypeError, len, v)
self.assertEqual(v.itemsize, sizeof(itemtp))
self.assertEqual(v.shape, shape)
# XXX Issue #12851
# self.assertEqual(v.strides, ())
# they are always read/write
self.assertFalse(v.readonly)
# they are always read/write
self.assertFalse(v.readonly)
n = 1
for dim in v.shape:
n = n * dim
self.assertEqual(n * v.itemsize, len(v.tobytes()))
if v.shape:
n = 1
for dim in v.shape:
n = n * dim
self.assertEqual(n, len(v))
except:
# so that we can see the failing type
print(tp)
raise
# define some structure classes
@@ -86,6 +83,20 @@ class PackedPoint(Structure):
_pack_ = 2
_fields_ = [("x", c_long), ("y", c_long)]
class PointMidPad(Structure):
_fields_ = [("x", c_byte), ("y", c_uint)]
class PackedPointMidPad(Structure):
_pack_ = 2
_fields_ = [("x", c_byte), ("y", c_uint64)]
class PointEndPad(Structure):
_fields_ = [("x", c_uint), ("y", c_byte)]
class PackedPointEndPad(Structure):
_pack_ = 2
_fields_ = [("x", c_uint64), ("y", c_byte)]
class Point2(Structure):
pass
Point2._fields_ = [("x", c_long), ("y", c_long)]
@@ -107,6 +118,7 @@ class Complete(Structure):
PComplete = POINTER(Complete)
Complete._fields_ = [("a", c_long)]
################################################################
#
# This table contains format strings as they look on little endian
@@ -185,11 +197,14 @@ native_types = [
## structures and unions
(Point, "T{<l:x:<l:y:}".replace('l', s_long), (), Point),
# packed structures do not implement the pep
(PackedPoint, "B", (), PackedPoint),
(Point2, "T{<l:x:<l:y:}".replace('l', s_long), (), Point2),
(EmptyStruct, "T{}", (), EmptyStruct),
(Point2, "T{<l:x:<l:y:}".replace('l', s_long), (), Point2),
(Point, "T{<l:x:<l:y:}".replace('l', s_long), (), Point),
(PackedPoint, "T{<l:x:<l:y:}".replace('l', s_long), (), PackedPoint),
(PointMidPad, "T{<b:x:3x<I:y:}".replace('I', s_uint), (), PointMidPad),
(PackedPointMidPad, "T{<b:x:x<Q:y:}", (), PackedPointMidPad),
(PointEndPad, "T{<I:x:<b:y:3x}".replace('I', s_uint), (), PointEndPad),
(PackedPointEndPad, "T{<Q:x:<b:y:x}", (), PackedPointEndPad),
(EmptyStruct, "T{}", (), EmptyStruct),
# the pep doesn't support unions
(aUnion, "B", (), aUnion),
# structure with sub-arrays
@@ -213,23 +228,23 @@ native_types = [
]
class BEPoint(BigEndianStructure):
_fields_ = [("x", c_long), ("y", c_long)]
class LEPoint(LittleEndianStructure):
_fields_ = [("x", c_long), ("y", c_long)]
################################################################
#
# This table contains format strings as they really look, on both big
# and little endian machines.
#
endian_types = [
(BEPoint, "T{>l:x:>l:y:}".replace('l', s_long), (), BEPoint),
(LEPoint, "T{<l:x:<l:y:}".replace('l', s_long), (), LEPoint),
(LEPoint * 1, "T{<l:x:<l:y:}".replace('l', s_long), (1,), LEPoint),
(POINTER(BEPoint), "&T{>l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)),
(POINTER(LEPoint), "&T{<l:x:<l:y:}".replace('l', s_long), (), POINTER(LEPoint)),
]
if __name__ == "__main__":
unittest.main()
@@ -1,9 +1,15 @@
import unittest
import pickle
from ctypes import *
import _ctypes_test
import unittest
from ctypes import (CDLL, Structure, CFUNCTYPE, pointer,
c_void_p, c_char_p, c_wchar_p,
c_char, c_wchar, c_int, c_double)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
dll = CDLL(_ctypes_test.__file__)
class X(Structure):
_fields_ = [("a", c_int), ("b", c_double)]
init_called = 0
@@ -11,9 +17,11 @@ class X(Structure):
X.init_called += 1
self.x = 42
class Y(X):
_fields_ = [("str", c_char_p)]
class PickleTest:
def dumps(self, item):
return pickle.dumps(item, self.proto)
@@ -71,11 +79,13 @@ class PickleTest:
# Issue 5049
self.dumps(c_wchar("x"))
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
name = 'PickleTest_%s' % proto
globals()[name] = type(name,
(PickleTest, unittest.TestCase),
{'proto': proto})
if __name__ == "__main__":
unittest.main()
@@ -1,14 +1,43 @@
import unittest, sys
import array
import ctypes
import sys
import unittest
from ctypes import (CDLL, CFUNCTYPE, Structure,
POINTER, pointer, _Pointer, _pointer_type_cache,
byref, sizeof,
c_void_p, c_char_p,
c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong,
c_float, c_double)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ._support import (_CData, PyCPointerType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
from ctypes import *
import _ctypes_test
ctype_types = [c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong, c_double, c_float]
python_types = [int, int, int, int, int, int,
int, int, int, int, float, float]
class PointersTestCase(unittest.TestCase):
def test_inheritance_hierarchy(self):
self.assertEqual(_Pointer.mro(), [_Pointer, _CData, object])
self.assertEqual(PyCPointerType.__name__, "PyCPointerType")
self.assertEqual(type(PyCPointerType), type)
def test_type_flags(self):
for cls in _Pointer, PyCPointerType:
with self.subTest(cls=cls):
self.assertTrue(_Pointer.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(_Pointer.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Cannot call the metaclass __init__ more than once
with self.assertRaisesRegex(SystemError, "already initialized"):
PyCPointerType.__init__(POINTER(c_byte), 'ptr', (), {})
def test_pointer_crash(self):
@@ -28,7 +57,6 @@ class PointersTestCase(unittest.TestCase):
func.restype = c_long
i = c_int(12345678)
## func.argtypes = (POINTER(c_int),)
address = func(byref(i))
self.assertEqual(c_int.from_address(address).value, 12345678)
@@ -71,9 +99,7 @@ class PointersTestCase(unittest.TestCase):
def func(arg):
for i in range(10):
## print arg[i],
self.result.append(arg[i])
## print
return 0
callback = PROTOTYPE(func)
@@ -83,38 +109,23 @@ class PointersTestCase(unittest.TestCase):
# The int pointer points to a table containing the numbers 1..10
doit = dll._testfunc_callback_with_pointer
## i = c_int(42)
## callback(byref(i))
## self.assertEqual(i.value, 84)
doit(callback)
## print self.result
doit(callback)
## print self.result
def test_basics(self):
from operator import delitem
for ct, pt in zip(ctype_types, python_types):
i = ct(42)
p = pointer(i)
## print type(p.contents), ct
self.assertIs(type(p.contents), ct)
# p.contents is the same as p[0]
## print p.contents
## self.assertEqual(p.contents, 42)
## self.assertEqual(p[0], 42)
self.assertRaises(TypeError, delitem, p, 0)
with self.assertRaises(TypeError):
del p[0]
def test_from_address(self):
from array import array
a = array('i', [100, 200, 300, 400, 500])
a = array.array('i', [100, 200, 300, 400, 500])
addr = a.buffer_info()[0]
p = POINTER(POINTER(c_int))
## print dir(p)
## print p.from_address
## print p.from_address(addr)[0][0]
def test_other(self):
class Table(Structure):
@@ -130,7 +141,6 @@ class PointersTestCase(unittest.TestCase):
pt.contents.c = 33
from ctypes import _pointer_type_cache
del _pointer_type_cache[Table]
def test_basic(self):
@@ -193,7 +203,7 @@ class PointersTestCase(unittest.TestCase):
# COM methods are boolean True:
if sys.platform == "win32":
mth = WINFUNCTYPE(None)(42, "name", (), None)
mth = ctypes.WINFUNCTYPE(None)(42, "name", (), None)
self.assertEqual(bool(mth), True)
def test_pointer_type_name(self):
@@ -201,7 +211,6 @@ class PointersTestCase(unittest.TestCase):
self.assertTrue(POINTER(LargeNamedType))
# to not leak references, we must clean _pointer_type_cache
from ctypes import _pointer_type_cache
del _pointer_type_cache[LargeNamedType]
def test_pointer_type_str_name(self):
@@ -210,14 +219,22 @@ class PointersTestCase(unittest.TestCase):
self.assertTrue(P)
# to not leak references, we must clean _pointer_type_cache
from ctypes import _pointer_type_cache
del _pointer_type_cache[id(P)]
def test_abstract(self):
from ctypes import _Pointer
self.assertRaises(TypeError, _Pointer.set_type, 42)
def test_repeated_set_type(self):
# Regression test for gh-133290
class C(Structure):
_fields_ = [('a', c_int)]
ptr = POINTER(C)
# Read _type_ several times to warm up cache
for i in range(5):
self.assertIs(ptr._type_, C)
ptr.set_type(c_int)
self.assertIs(ptr._type_, c_int)
if __name__ == '__main__':
unittest.main()
@@ -1,7 +1,3 @@
from ctypes import *
from ctypes.test import need_symbol
import unittest
# IMPORTANT INFO:
#
# Consider this call:
@@ -22,9 +18,18 @@ import unittest
#
# In this case, there would have to be an additional reference to the argument...
import _ctypes_test
import unittest
from ctypes import (CDLL, CFUNCTYPE, POINTER, ArgumentError,
pointer, byref, sizeof, addressof, create_string_buffer,
c_void_p, c_char_p, c_wchar_p, c_char, c_wchar,
c_short, c_int, c_long, c_longlong, c_double)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
testdll = CDLL(_ctypes_test.__file__)
# Return machine address `a` as a (possibly long) non-negative integer.
# Starting with Python 2.5, id(anything) is always non-negative, and
# the ctypes addressof() inherits that via PyLong_FromVoidPtr().
@@ -38,12 +43,13 @@ def positive_address(a):
assert a >= 0
return a
def c_wbuffer(init):
n = len(init) + 1
return (c_wchar * n)(*init)
class CharPointersTestCase(unittest.TestCase):
class CharPointersTestCase(unittest.TestCase):
def setUp(self):
func = testdll._testfunc_p_p
func.restype = c_long
@@ -66,6 +72,32 @@ class CharPointersTestCase(unittest.TestCase):
self.assertEqual(func(None), None)
self.assertEqual(func(input=None), None)
def test_invalid_paramflags(self):
proto = CFUNCTYPE(c_int, c_char_p)
with self.assertRaises(ValueError):
func = proto(("myprintf", testdll), ((1, "fmt"), (1, "arg1")))
def test_invalid_setattr_argtypes(self):
proto = CFUNCTYPE(c_int, c_char_p)
func = proto(("myprintf", testdll), ((1, "fmt"),))
with self.assertRaisesRegex(TypeError, "_argtypes_ must be a sequence of types"):
func.argtypes = 123
self.assertEqual(func.argtypes, (c_char_p,))
with self.assertRaisesRegex(ValueError, "paramflags must have the same length as argtypes"):
func.argtypes = (c_char_p, c_int)
self.assertEqual(func.argtypes, (c_char_p,))
def test_paramflags_outarg(self):
proto = CFUNCTYPE(c_int, c_char_p, c_int)
with self.assertRaisesRegex(TypeError, "must be a pointer type"):
func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
proto = CFUNCTYPE(c_int, c_char_p, c_void_p)
func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
with self.assertRaisesRegex(TypeError, "must be a pointer type"):
func.argtypes = (c_char_p, c_int)
def test_int_pointer_arg(self):
func = testdll._testfunc_p_p
@@ -100,7 +132,7 @@ class CharPointersTestCase(unittest.TestCase):
self.assertEqual(None, func(c_char_p(None)))
self.assertEqual(b"123", func(c_char_p(b"123")))
self.assertEqual(b"123", func(c_buffer(b"123")))
self.assertEqual(b"123", func(create_string_buffer(b"123")))
ca = c_char(b"a")
self.assertEqual(ord(b"a"), func(pointer(ca))[0])
self.assertEqual(ord(b"a"), func(byref(ca))[0])
@@ -115,7 +147,7 @@ class CharPointersTestCase(unittest.TestCase):
self.assertEqual(None, func(c_char_p(None)))
self.assertEqual(b"123", func(c_char_p(b"123")))
self.assertEqual(b"123", func(c_buffer(b"123")))
self.assertEqual(b"123", func(create_string_buffer(b"123")))
ca = c_char(b"a")
self.assertEqual(ord(b"a"), func(pointer(ca))[0])
self.assertEqual(ord(b"a"), func(byref(ca))[0])
@@ -130,7 +162,7 @@ class CharPointersTestCase(unittest.TestCase):
self.assertEqual(b"123", func(c_char_p(b"123")))
self.assertEqual(None, func(c_char_p(None)))
self.assertEqual(b"123", func(c_buffer(b"123")))
self.assertEqual(b"123", func(create_string_buffer(b"123")))
ca = c_char(b"a")
self.assertEqual(ord(b"a"), func(pointer(ca))[0])
self.assertEqual(ord(b"a"), func(byref(ca))[0])
@@ -139,7 +171,6 @@ class CharPointersTestCase(unittest.TestCase):
func(pointer(c_int()))
func((c_int * 3)())
@need_symbol('c_wchar_p')
def test_c_void_p_arg_with_c_wchar_p(self):
func = testdll._testfunc_p_p
func.restype = c_wchar_p
@@ -161,9 +192,8 @@ class CharPointersTestCase(unittest.TestCase):
func.argtypes = None
self.assertEqual(None, func(X()))
@need_symbol('c_wchar')
class WCharPointersTestCase(unittest.TestCase):
class WCharPointersTestCase(unittest.TestCase):
def setUp(self):
func = testdll._testfunc_p_p
func.restype = c_int
@@ -203,6 +233,7 @@ class WCharPointersTestCase(unittest.TestCase):
self.assertEqual("a", func(pointer(ca))[0])
self.assertEqual("a", func(byref(ca))[0])
class ArrayTest(unittest.TestCase):
def test(self):
func = testdll._testfunc_ai8
@@ -216,7 +247,6 @@ class ArrayTest(unittest.TestCase):
def func(): pass
CFUNCTYPE(None, c_int * 3)(func)
################################################################
if __name__ == '__main__':
unittest.main()
@@ -1,18 +1,14 @@
from ctypes import *
import _ctypes
import sys
import unittest
from test import support
from ctypes import (pythonapi, POINTER, create_string_buffer, sizeof,
py_object, c_char_p, c_char, c_long, c_size_t)
################################################################
# This section should be moved into ctypes\__init__.py, when it's ready.
from _ctypes import PyObj_FromPtr
################################################################
from sys import getrefcount as grc
class PythonAPITestCase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_PyBytes_FromStringAndSize(self):
PyBytes_FromStringAndSize = pythonapi.PyBytes_FromStringAndSize
@@ -27,46 +23,49 @@ class PythonAPITestCase(unittest.TestCase):
pythonapi.PyBytes_FromString.argtypes = (c_char_p,)
s = b"abc"
refcnt = grc(s)
refcnt = sys.getrefcount(s)
pyob = pythonapi.PyBytes_FromString(s)
self.assertEqual(grc(s), refcnt)
self.assertEqual(sys.getrefcount(s), refcnt)
self.assertEqual(s, pyob)
del pyob
self.assertEqual(grc(s), refcnt)
self.assertEqual(sys.getrefcount(s), refcnt)
@support.refcount_test
def test_PyLong_Long(self):
ref42 = grc(42)
ref42 = sys.getrefcount(42)
pythonapi.PyLong_FromLong.restype = py_object
self.assertEqual(pythonapi.PyLong_FromLong(42), 42)
self.assertEqual(grc(42), ref42)
self.assertEqual(sys.getrefcount(42), ref42)
pythonapi.PyLong_AsLong.argtypes = (py_object,)
pythonapi.PyLong_AsLong.restype = c_long
res = pythonapi.PyLong_AsLong(42)
self.assertEqual(grc(res), ref42 + 1)
# Small int refcnts don't change
self.assertEqual(sys.getrefcount(res), ref42)
del res
self.assertEqual(grc(42), ref42)
self.assertEqual(sys.getrefcount(42), ref42)
@support.refcount_test
def test_PyObj_FromPtr(self):
s = "abc def ghi jkl"
ref = grc(s)
s = object()
ref = sys.getrefcount(s)
# id(python-object) is the address
pyobj = PyObj_FromPtr(id(s))
pyobj = _ctypes.PyObj_FromPtr(id(s))
self.assertIs(s, pyobj)
self.assertEqual(grc(s), ref + 1)
self.assertEqual(sys.getrefcount(s), ref + 1)
del pyobj
self.assertEqual(grc(s), ref)
self.assertEqual(sys.getrefcount(s), ref)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_PyOS_snprintf(self):
PyOS_snprintf = pythonapi.PyOS_snprintf
PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p
buf = c_buffer(256)
buf = create_string_buffer(256)
PyOS_snprintf(buf, sizeof(buf), b"Hello from %s", b"ctypes")
self.assertEqual(buf.value, b"Hello from ctypes")
@@ -81,5 +80,6 @@ class PythonAPITestCase(unittest.TestCase):
self.assertEqual(repr(py_object(42)), "py_object(42)")
self.assertEqual(repr(py_object(object)), "py_object(%r)" % object)
if __name__ == "__main__":
unittest.main()
@@ -1,30 +1,34 @@
from ctypes import *
import _ctypes
import contextlib
from test import support
import unittest
import ctypes
import sys
import unittest
from test import support
from ctypes import CFUNCTYPE, c_void_p, c_char_p, c_int, c_double
def callback_func(arg):
42 / arg
raise ValueError(arg)
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class call_function_TestCase(unittest.TestCase):
# _ctypes.call_function is deprecated and private, but used by
# Gary Bishp's readline module. If we have it, we must test it as well.
def test(self):
from _ctypes import call_function
windll.kernel32.LoadLibraryA.restype = c_void_p
windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
windll.kernel32.GetProcAddress.restype = c_void_p
kernel32 = ctypes.windll.kernel32
kernel32.LoadLibraryA.restype = c_void_p
kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
kernel32.GetProcAddress.restype = c_void_p
hdll = windll.kernel32.LoadLibraryA(b"kernel32")
funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA")
hdll = kernel32.LoadLibraryA(b"kernel32")
funcaddr = kernel32.GetProcAddress(hdll, b"GetModuleHandleA")
self.assertEqual(_ctypes.call_function(funcaddr, (None,)),
kernel32.GetModuleHandleA(None))
self.assertEqual(call_function(funcaddr, (None,)),
windll.kernel32.GetModuleHandleA(None))
class CallbackTracbackTestCase(unittest.TestCase):
# When an exception is raised in a ctypes callback function, the C
@@ -47,9 +51,9 @@ class CallbackTracbackTestCase(unittest.TestCase):
if exc_msg is not None:
self.assertEqual(str(cm.unraisable.exc_value), exc_msg)
self.assertEqual(cm.unraisable.err_msg,
"Exception ignored on calling ctypes "
"callback function")
self.assertIs(cm.unraisable.object, callback_func)
f"Exception ignored on calling ctypes "
f"callback function {callback_func!r}")
self.assertIsNone(cm.unraisable.object)
def test_ValueError(self):
cb = CFUNCTYPE(c_int, c_int)(callback_func)
@@ -66,6 +70,8 @@ class CallbackTracbackTestCase(unittest.TestCase):
with self.expect_unraisable(ZeroDivisionError):
cb(0.0)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_TypeErrorDivisionError(self):
cb = CFUNCTYPE(c_int, c_char_p)(callback_func)
err_msg = "unsupported operand type(s) for /: 'int' and 'bytes'"
@@ -1,60 +1,59 @@
import unittest
from test import support
import ctypes
import gc
import sys
import unittest
from test import support
from test.support import import_helper
from test.support import script_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
MyCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong)
import _ctypes_test
dll = ctypes.CDLL(_ctypes_test.__file__)
class RefcountTestCase(unittest.TestCase):
class RefcountTestCase(unittest.TestCase):
@support.refcount_test
def test_1(self):
from sys import getrefcount as grc
f = dll._testfunc_callback_i_if
f.restype = ctypes.c_int
f.argtypes = [ctypes.c_int, MyCallback]
def callback(value):
#print "called back with", value
return value
self.assertEqual(grc(callback), 2)
self.assertEqual(sys.getrefcount(callback), 2)
cb = MyCallback(callback)
self.assertGreater(grc(callback), 2)
self.assertGreater(sys.getrefcount(callback), 2)
result = f(-10, cb)
self.assertEqual(result, -18)
cb = None
gc.collect()
self.assertEqual(grc(callback), 2)
self.assertEqual(sys.getrefcount(callback), 2)
@support.refcount_test
def test_refcount(self):
from sys import getrefcount as grc
def func(*args):
pass
# this is the standard refcount for func
self.assertEqual(grc(func), 2)
self.assertEqual(sys.getrefcount(func), 2)
# the CFuncPtr instance holds at least one refcount on func:
f = OtherCallback(func)
self.assertGreater(grc(func), 2)
self.assertGreater(sys.getrefcount(func), 2)
# and may release it again
del f
self.assertGreaterEqual(grc(func), 2)
self.assertGreaterEqual(sys.getrefcount(func), 2)
# but now it must be gone
gc.collect()
self.assertEqual(grc(func), 2)
self.assertEqual(sys.getrefcount(func), 2)
class X(ctypes.Structure):
_fields_ = [("a", OtherCallback)]
@@ -62,32 +61,31 @@ class RefcountTestCase(unittest.TestCase):
x.a = OtherCallback(func)
# the CFuncPtr instance holds at least one refcount on func:
self.assertGreater(grc(func), 2)
self.assertGreater(sys.getrefcount(func), 2)
# and may release it again
del x
self.assertGreaterEqual(grc(func), 2)
self.assertGreaterEqual(sys.getrefcount(func), 2)
# and now it must be gone again
gc.collect()
self.assertEqual(grc(func), 2)
self.assertEqual(sys.getrefcount(func), 2)
f = OtherCallback(func)
# the CFuncPtr instance holds at least one refcount on func:
self.assertGreater(grc(func), 2)
self.assertGreater(sys.getrefcount(func), 2)
# create a cycle
f.cycle = f
del f
gc.collect()
self.assertEqual(grc(func), 2)
self.assertEqual(sys.getrefcount(func), 2)
class AnotherLeak(unittest.TestCase):
def test_callback(self):
import sys
proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)
def func(a, b):
return a * b * 2
@@ -112,5 +110,34 @@ class AnotherLeak(unittest.TestCase):
for _ in range(10000):
func()
class ModuleIsolationTest(unittest.TestCase):
def test_finalize(self):
# check if gc_decref() succeeds
script = (
"import ctypes;"
"import sys;"
"del sys.modules['_ctypes'];"
"import _ctypes;"
"exit()"
)
script_helper.assert_python_ok("-c", script)
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
# At this point, there's no exception set, so PyErr_Occurred
# returns NULL. Given the restype is py_object, the
# ctypes machinery will raise a custom error.
with self.assertRaisesRegex(ValueError, "PyObject is NULL"):
PyErr_Occurred()
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,8 @@
from ctypes import *
import unittest
from ctypes import (c_byte, c_short, c_int, c_long, c_longlong,
c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong,
c_float, c_double, c_longdouble, c_bool, c_char)
subclasses = []
for base in [c_byte, c_short, c_int, c_long, c_longlong,
@@ -9,11 +12,12 @@ for base in [c_byte, c_short, c_int, c_long, c_longlong,
pass
subclasses.append(X)
class X(c_char):
pass
# This test checks if the __repr__ is correct for subclasses of simple types
# This test checks if the __repr__ is correct for subclasses of simple types
class ReprTest(unittest.TestCase):
def test_numbers(self):
for typ in subclasses:
@@ -25,5 +29,6 @@ class ReprTest(unittest.TestCase):
self.assertEqual("c_char(b'x')", repr(c_char(b'x')))
self.assertEqual("<X object at", repr(X(b'x'))[:12])
if __name__ == "__main__":
unittest.main()
@@ -1,10 +1,10 @@
import unittest
from ctypes import *
from ctypes import CDLL, CFUNCTYPE, ArgumentError, c_char_p, c_void_p, c_char
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
class ReturnFuncPtrTestCase(unittest.TestCase):
def test_with_prototype(self):
# The _ctypes_test shared lib/dll exports quite some functions for testing.
# The get_strchr function returns a *pointer* to the C strchr function.
@@ -62,5 +62,6 @@ class ReturnFuncPtrTestCase(unittest.TestCase):
self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
self.assertRaises(TypeError, strchr, b"abcdef")
if __name__ == "__main__":
unittest.main()
+96
View File
@@ -0,0 +1,96 @@
import unittest
from ctypes import Structure, CFUNCTYPE, c_int, _SimpleCData
from ._support import (_CData, PyCSimpleType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
class MyInt(c_int):
def __eq__(self, other):
if type(other) != MyInt:
return NotImplementedError
return self.value == other.value
class Test(unittest.TestCase):
def test_inheritance_hierarchy(self):
self.assertEqual(_SimpleCData.mro(), [_SimpleCData, _CData, object])
self.assertEqual(PyCSimpleType.__name__, "PyCSimpleType")
self.assertEqual(type(PyCSimpleType), type)
self.assertEqual(c_int.mro(), [c_int, _SimpleCData, _CData, object])
def test_type_flags(self):
for cls in _SimpleCData, PyCSimpleType:
with self.subTest(cls=cls):
self.assertTrue(_SimpleCData.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(_SimpleCData.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Abstract classes (whose metaclass __init__ was not called) can't be
# instantiated directly
NewT = PyCSimpleType.__new__(PyCSimpleType, 'NewT', (_SimpleCData,), {})
for cls in _SimpleCData, NewT:
with self.subTest(cls=cls):
with self.assertRaisesRegex(TypeError, "abstract class"):
obj = cls()
# Cannot call the metaclass __init__ more than once
class T(_SimpleCData):
_type_ = "i"
with self.assertRaisesRegex(SystemError, "already initialized"):
PyCSimpleType.__init__(T, 'ptr', (), {})
def test_swapped_type_creation(self):
cls = PyCSimpleType.__new__(PyCSimpleType, '', (), {'_type_': 'i'})
with self.assertRaises(TypeError):
PyCSimpleType.__init__(cls)
PyCSimpleType.__init__(cls, '', (), {'_type_': 'i'})
self.assertEqual(cls.__ctype_le__.__dict__.get('_type_'), 'i')
self.assertEqual(cls.__ctype_be__.__dict__.get('_type_'), 'i')
def test_compare(self):
self.assertEqual(MyInt(3), MyInt(3))
self.assertNotEqual(MyInt(42), MyInt(43))
def test_ignore_retval(self):
# Test if the return value of a callback is ignored
# if restype is None
proto = CFUNCTYPE(None)
def func():
return (1, "abc", None)
cb = proto(func)
self.assertEqual(None, cb())
def test_int_callback(self):
args = []
def func(arg):
args.append(arg)
return arg
cb = CFUNCTYPE(None, MyInt)(func)
self.assertEqual(None, cb(42))
self.assertEqual(type(args[-1]), MyInt)
cb = CFUNCTYPE(c_int, c_int)(func)
self.assertEqual(42, cb(42))
self.assertEqual(type(args[-1]), int)
def test_int_struct(self):
class X(Structure):
_fields_ = [("x", MyInt)]
self.assertEqual(X().x, MyInt())
s = X()
s.x = MyInt(42)
self.assertEqual(s.x, MyInt(42))
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,9 @@
# Test specifically-sized containers.
from ctypes import *
from ctypes import (sizeof,
c_int8, c_uint8, c_int16, c_uint16,
c_int32, c_uint32, c_int64, c_uint64,
c_void_p, c_size_t, c_ssize_t, c_time_t, SIZEOF_TIME_T)
import unittest
@@ -28,6 +30,9 @@ class SizesTestCase(unittest.TestCase):
def test_ssize_t(self):
self.assertEqual(sizeof(c_void_p), sizeof(c_ssize_t))
def test_time_t(self):
self.assertEqual(sizeof(c_time_t), SIZEOF_TIME_T)
if __name__ == "__main__":
unittest.main()
@@ -1,8 +1,9 @@
import unittest
from ctypes import *
from ctypes.test import need_symbol
from ctypes import (CDLL, POINTER, sizeof,
c_byte, c_short, c_int, c_long, c_char, c_wchar, c_char_p)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
class SlicesTestCase(unittest.TestCase):
def test_getslice_cint(self):
@@ -46,18 +47,21 @@ class SlicesTestCase(unittest.TestCase):
b[33::-3] = range(12)
self.assertEqual(a[:], b)
from operator import setitem
# TypeError: int expected instead of str instance
with self.assertRaises(TypeError):
a[:5] = "abcde"
# TypeError: int expected instead of str instance
self.assertRaises(TypeError, setitem, a, slice(0, 5), "abcde")
# TypeError: int expected instead of str instance
self.assertRaises(TypeError, setitem, a, slice(0, 5),
["a", "b", "c", "d", "e"])
with self.assertRaises(TypeError):
a[:5] = ["a", "b", "c", "d", "e"]
# TypeError: int expected instead of float instance
self.assertRaises(TypeError, setitem, a, slice(0, 5),
[1, 2, 3, 4, 3.14])
with self.assertRaises(TypeError):
a[:5] = [1, 2, 3, 4, 3.14]
# ValueError: Can only assign sequence of same size
self.assertRaises(ValueError, setitem, a, slice(0, 5), range(32))
with self.assertRaises(ValueError):
a[:5] = range(32)
def test_char_ptr(self):
s = b"abcdefghijklmnopqrstuvwxyz"
@@ -73,18 +77,20 @@ class SlicesTestCase(unittest.TestCase):
self.assertEqual(res[len(s)-1:5:-7], s[:5:-7])
self.assertEqual(res[0:-1:-1], s[0::-1])
import operator
self.assertRaises(ValueError, operator.getitem,
res, slice(None, None, None))
self.assertRaises(ValueError, operator.getitem,
res, slice(0, None, None))
self.assertRaises(ValueError, operator.getitem,
res, slice(None, 5, -1))
self.assertRaises(ValueError, operator.getitem,
res, slice(-5, None, None))
# get items
with self.assertRaises(ValueError):
res[:]
with self.assertRaises(ValueError):
res[0:]
with self.assertRaises(ValueError):
res[:5:-1]
with self.assertRaises(ValueError):
res[-5:]
# set items
with self.assertRaises(TypeError):
res[:5] = "abcde"
self.assertRaises(TypeError, operator.setitem,
res, slice(0, 5), "abcde")
dll.my_free(res)
dll.my_strdup.restype = POINTER(c_byte)
@@ -126,7 +132,6 @@ class SlicesTestCase(unittest.TestCase):
self.assertEqual(p[2:5:-3], s[2:5:-3])
@need_symbol('c_wchar')
def test_wchar_ptr(self):
s = "abcdefghijklmnopqrstuvwxyz\0"
@@ -140,9 +145,8 @@ class SlicesTestCase(unittest.TestCase):
self.assertEqual(res[len(s)-1:-1:-1], s[::-1])
self.assertEqual(res[len(s)-1:5:-7], s[:5:-7])
import operator
self.assertRaises(TypeError, operator.setitem,
res, slice(0, 5), "abcde")
with self.assertRaises(TypeError):
res[:5] = "abcde"
dll.my_free(res)
if sizeof(c_wchar) == sizeof(c_short):
@@ -161,7 +165,6 @@ class SlicesTestCase(unittest.TestCase):
self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7])
dll.my_free(res)
################################################################
if __name__ == "__main__":
unittest.main()
@@ -1,13 +1,16 @@
import sys
import unittest
from test import support
from ctypes import *
from ctypes import (CDLL, Structure, POINTER, create_string_buffer,
c_char, c_char_p)
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
lib = CDLL(_ctypes_test.__file__)
class StringPtrTestCase(unittest.TestCase):
class StringPtrTestCase(unittest.TestCase):
@support.refcount_test
def test__POINTER_c_char(self):
class X(Structure):
@@ -16,14 +19,13 @@ class StringPtrTestCase(unittest.TestCase):
# NULL pointer access
self.assertRaises(ValueError, getattr, x.str, "contents")
b = c_buffer(b"Hello, World")
from sys import getrefcount as grc
self.assertEqual(grc(b), 2)
b = create_string_buffer(b"Hello, World")
self.assertEqual(sys.getrefcount(b), 2)
x.str = b
self.assertEqual(grc(b), 3)
self.assertEqual(sys.getrefcount(b), 3)
# POINTER(c_char) and Python string is NOT compatible
# POINTER(c_char) and c_buffer() is compatible
# POINTER(c_char) and create_string_buffer() is compatible
for i in range(len(b)):
self.assertEqual(b[i], x.str[i])
@@ -35,11 +37,11 @@ class StringPtrTestCase(unittest.TestCase):
x = X()
# c_char_p and Python string is compatible
# c_char_p and c_buffer is NOT compatible
# c_char_p and create_string_buffer is NOT compatible
self.assertEqual(x.str, None)
x.str = b"Hello, World"
self.assertEqual(x.str, b"Hello, World")
b = c_buffer(b"Hello, World")
b = create_string_buffer(b"Hello, World")
self.assertRaises(TypeError, setattr, x, b"str", b)
@@ -48,15 +50,16 @@ class StringPtrTestCase(unittest.TestCase):
strchr.restype = c_char_p
# c_char_p and Python string is compatible
# c_char_p and c_buffer are now compatible
# c_char_p and create_string_buffer are now compatible
strchr.argtypes = c_char_p, c_char
self.assertEqual(strchr(b"abcdef", b"c"), b"cdef")
self.assertEqual(strchr(c_buffer(b"abcdef"), b"c"), b"cdef")
self.assertEqual(strchr(create_string_buffer(b"abcdef"), b"c"),
b"cdef")
# POINTER(c_char) and Python string is NOT compatible
# POINTER(c_char) and c_buffer() is compatible
# POINTER(c_char) and create_string_buffer() is compatible
strchr.argtypes = POINTER(c_char), c_char
buf = c_buffer(b"abcdef")
buf = create_string_buffer(b"abcdef")
self.assertEqual(strchr(buf, b"c"), b"cdef")
self.assertEqual(strchr(b"abcdef", b"c"), b"cdef")
@@ -65,7 +68,7 @@ class StringPtrTestCase(unittest.TestCase):
# So we must keep a reference to buf separately
strchr.restype = POINTER(c_char)
buf = c_buffer(b"abcdef")
buf = create_string_buffer(b"abcdef")
r = strchr(buf, b"c")
x = r[0], r[1], r[2], r[3], r[4]
self.assertEqual(x, (b"c", b"d", b"e", b"f", b"\000"))
@@ -73,5 +76,6 @@ class StringPtrTestCase(unittest.TestCase):
# Because r is a pointer to memory that is freed after deleting buf,
# the pointer is hanging and using it would reference freed memory.
if __name__ == '__main__':
unittest.main()
@@ -1,6 +1,7 @@
import unittest
from ctypes import *
from ctypes.test import need_symbol
from ctypes import (create_string_buffer, create_unicode_buffer,
sizeof, byref, c_char, c_wchar)
class StringArrayTestCase(unittest.TestCase):
def test(self):
@@ -25,8 +26,8 @@ class StringArrayTestCase(unittest.TestCase):
self.assertRaises(ValueError, setattr, buf, "value", b"aaaaaaaa")
self.assertRaises(TypeError, setattr, buf, "value", 42)
def test_c_buffer_value(self):
buf = c_buffer(32)
def test_create_string_buffer_value(self):
buf = create_string_buffer(32)
buf.value = b"Hello, World"
self.assertEqual(buf.value, b"Hello, World")
@@ -35,8 +36,8 @@ class StringArrayTestCase(unittest.TestCase):
self.assertRaises(TypeError, setattr, buf, "value", memoryview(b"abc"))
self.assertRaises(ValueError, setattr, buf, "raw", memoryview(b"x" * 100))
def test_c_buffer_raw(self):
buf = c_buffer(32)
def test_create_string_buffer_raw(self):
buf = create_string_buffer(32)
buf.raw = memoryview(b"Hello, World")
self.assertEqual(buf.value, b"Hello, World")
@@ -46,13 +47,10 @@ class StringArrayTestCase(unittest.TestCase):
def test_param_1(self):
BUF = c_char * 4
buf = BUF()
## print c_char_p.from_param(buf)
def test_param_2(self):
BUF = c_char * 4
buf = BUF()
## print BUF.from_param(c_char_p("python"))
## print BUF.from_param(BUF(*"pyth"))
def test_del_segfault(self):
BUF = c_char * 4
@@ -61,7 +59,6 @@ class StringArrayTestCase(unittest.TestCase):
del buf.raw
@need_symbol('c_wchar')
class WStringArrayTestCase(unittest.TestCase):
def test(self):
BUF = c_wchar * 4
@@ -86,49 +83,41 @@ class WStringArrayTestCase(unittest.TestCase):
self.assertEqual(w.value, u)
@need_symbol('c_wchar')
class WStringTestCase(unittest.TestCase):
def test_wchar(self):
c_wchar("x")
repr(byref(c_wchar("x")))
c_wchar("x")
@unittest.skip('test disabled')
def test_basic_wstrings(self):
cs = c_wstring("abcdef")
# XXX This behaviour is about to change:
# len returns the size of the internal buffer in bytes.
# This includes the terminating NUL character.
self.assertEqual(sizeof(cs), 14)
# The value property is the string up to the first terminating NUL.
cs = create_unicode_buffer("abcdef")
self.assertEqual(cs.value, "abcdef")
self.assertEqual(c_wstring("abc\000def").value, "abc")
self.assertEqual(c_wstring("abc\000def").value, "abc")
# value can be changed
cs.value = "abc"
self.assertEqual(cs.value, "abc")
# The raw property is the total buffer contents:
self.assertEqual(cs.raw, "abcdef\000")
self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000")
# string is truncated at NUL character
cs.value = "def\0z"
self.assertEqual(cs.value, "def")
# We can change the value:
cs.value = "ab"
self.assertEqual(cs.value, "ab")
self.assertEqual(cs.raw, "ab\000\000\000\000\000")
self.assertEqual(create_unicode_buffer("abc\0def").value, "abc")
self.assertRaises(TypeError, c_wstring, "123")
self.assertRaises(ValueError, c_wstring, 0)
# created with an empty string
cs = create_unicode_buffer(3)
self.assertEqual(cs.value, "")
cs.value = "abc"
self.assertEqual(cs.value, "abc")
@unittest.skip('test disabled')
def test_toolong(self):
cs = c_wstring("abcdef")
# Much too long string:
self.assertRaises(ValueError, setattr, cs, "value", "123456789012345")
cs = create_unicode_buffer("abc")
with self.assertRaises(ValueError):
cs.value = "abcdef"
# One char too long values:
self.assertRaises(ValueError, setattr, cs, "value", "1234567")
cs = create_unicode_buffer(4)
with self.assertRaises(ValueError):
cs.value = "abcdef"
def run_test(rep, msg, func, arg):
@@ -1,5 +1,9 @@
import unittest
from ctypes import *
import sys
from ctypes import Structure, Union, sizeof, c_char, c_int
from ._support import (CField, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
class StructFieldsTestCase(unittest.TestCase):
# Structure/Union classes must get 'finalized' sooner or
@@ -11,7 +15,6 @@ class StructFieldsTestCase(unittest.TestCase):
# 4. The type is subclassed
#
# When they are finalized, assigning _fields_ is no longer allowed.
def test_1_A(self):
class X(Structure):
pass
@@ -55,20 +58,45 @@ class StructFieldsTestCase(unittest.TestCase):
self.assertEqual(bytes(x), b'a\x00###')
def test_6(self):
class X(Structure):
_fields_ = [("x", c_int)]
CField = type(X.x)
self.assertRaises(TypeError, CField)
def test_cfield_type_flags(self):
self.assertTrue(CField.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
def test_cfield_inheritance_hierarchy(self):
self.assertEqual(CField.mro(), [CField, object])
def test_gh99275(self):
class BrokenStructure(Structure):
def __init_subclass__(cls, **kwargs):
cls._fields_ = [] # This line will fail, `stgdict` is not ready
cls._fields_ = [] # This line will fail, `stginfo` is not ready
with self.assertRaisesRegex(TypeError,
'ctypes state is not initialized'):
class Subclass(BrokenStructure): ...
def test_max_field_size_gh126937(self):
# Classes for big structs should be created successfully.
# (But they most likely can't be instantiated.)
# The size must fit in Py_ssize_t.
class X(Structure):
_fields_ = [('char', c_char),]
max_field_size = sys.maxsize
class Y(Structure):
_fields_ = [('largeField', X * max_field_size)]
class Z(Structure):
_fields_ = [('largeField', c_char * max_field_size)]
with self.assertRaises(OverflowError):
class TooBig(Structure):
_fields_ = [('largeField', X * (max_field_size + 1))]
with self.assertRaises(OverflowError):
class TooBig(Structure):
_fields_ = [('largeField', c_char * (max_field_size + 1))]
# __set__ and __get__ should raise a TypeError in case their self
# argument is not a ctype instance.
def test___set__(self):
@@ -93,5 +121,6 @@ class StructFieldsTestCase(unittest.TestCase):
self.assertRaises(TypeError,
MyCUnion.field.__get__, 'wrong type self', 42)
if __name__ == "__main__":
unittest.main()
@@ -1,16 +1,21 @@
import platform
from platform import architecture as _architecture
import struct
import sys
import unittest
from ctypes import *
from ctypes.test import need_symbol
from ctypes import (CDLL, Array, Structure, Union, POINTER, sizeof, byref, alignment,
c_void_p, c_char, c_wchar, c_byte, c_ubyte,
c_uint8, c_uint16, c_uint32,
c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
from ctypes.util import find_library
from struct import calcsize
import _ctypes_test
from collections import namedtuple
from test import support
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ._support import (_CData, PyCStructType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
# The following definition is meant to be used from time to time to assist
# temporarily disabling tests on specific architectures while investigations
# are in progress, to keep buildbots happy.
MACHINE = platform.machine()
class SubclassesTest(unittest.TestCase):
def test_subclass(self):
@@ -51,6 +56,7 @@ class SubclassesTest(unittest.TestCase):
self.assertEqual(Y._fields_, [("b", c_int)])
self.assertEqual(Z._fields_, [("a", c_int)])
class StructureTestCase(unittest.TestCase):
formats = {"c": c_char,
"b": c_byte,
@@ -67,6 +73,36 @@ class StructureTestCase(unittest.TestCase):
"d": c_double,
}
def test_inheritance_hierarchy(self):
self.assertEqual(Structure.mro(), [Structure, _CData, object])
self.assertEqual(PyCStructType.__name__, "PyCStructType")
self.assertEqual(type(PyCStructType), type)
def test_type_flags(self):
for cls in Structure, PyCStructType:
with self.subTest(cls=cls):
self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Abstract classes (whose metaclass __init__ was not called) can't be
# instantiated directly
NewStructure = PyCStructType.__new__(PyCStructType, 'NewStructure',
(Structure,), {})
for cls in Structure, NewStructure:
with self.subTest(cls=cls):
with self.assertRaisesRegex(TypeError, "abstract class"):
obj = cls()
# Cannot call the metaclass __init__ more than once
class T(Structure):
_fields_ = [("x", c_char),
("y", c_char)]
with self.assertRaisesRegex(SystemError, "already initialized"):
PyCStructType.__init__(T, 'ptr', (), {})
def test_simple_structs(self):
for code, tp in self.formats.items():
class X(Structure):
@@ -184,7 +220,6 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual(sizeof(X), 10)
self.assertEqual(X.b.offset, 2)
import struct
longlong_size = struct.calcsize("q")
longlong_align = struct.calcsize("bq") - longlong_size
@@ -303,7 +338,6 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual(p.phone.number, b"5678")
self.assertEqual(p.age, 5)
@need_symbol('c_wchar')
def test_structures_with_wchar(self):
class PersonW(Structure):
_fields_ = [("name", c_wchar * 12),
@@ -359,15 +393,6 @@ class StructureTestCase(unittest.TestCase):
except Exception as detail:
return detail.__class__, str(detail)
@unittest.skip('test disabled')
def test_subclass_creation(self):
meta = type(Structure)
# same as 'class X(Structure): pass'
# fails, since we need either a _fields_ or a _abstract_ attribute
cls, msg = self.get_except(meta, "X", (Structure,), {})
self.assertEqual((cls, msg),
(AttributeError, "class must define a '_fields_' attribute"))
def test_abstract_class(self):
class X(Structure):
_abstract_ = "something"
@@ -376,9 +401,6 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual((cls, msg), (TypeError, "abstract class"))
def test_methods(self):
## class X(Structure):
## _fields_ = []
self.assertIn("in_dll", dir(type(Structure)))
self.assertIn("from_address", dir(type(Structure)))
self.assertIn("in_dll", dir(type(Structure)))
@@ -484,37 +506,116 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual(s.first, got.first)
self.assertEqual(s.second, got.second)
def _test_issue18060(self, Vector):
# The call to atan2() should succeed if the
# class fields were correctly cloned in the
# subclasses. Otherwise, it will segfault.
if sys.platform == 'win32':
libm = CDLL(find_library('msvcrt.dll'))
else:
libm = CDLL(find_library('m'))
libm.atan2.argtypes = [Vector]
libm.atan2.restype = c_double
arg = Vector(y=0.0, x=-1.0)
self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793)
@unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
@unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
def test_issue18060_a(self):
# This test case calls
# PyCStructUnionType_update_stginfo() for each
# _fields_ assignment, and PyCStgInfo_clone()
# for the Mid and Vector class definitions.
class Base(Structure):
_fields_ = [('y', c_double),
('x', c_double)]
class Mid(Base):
pass
Mid._fields_ = []
class Vector(Mid): pass
self._test_issue18060(Vector)
@unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
@unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
def test_issue18060_b(self):
# This test case calls
# PyCStructUnionType_update_stginfo() for each
# _fields_ assignment.
class Base(Structure):
_fields_ = [('y', c_double),
('x', c_double)]
class Mid(Base):
_fields_ = []
class Vector(Mid):
_fields_ = []
self._test_issue18060(Vector)
@unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
@unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
def test_issue18060_c(self):
# This test case calls
# PyCStructUnionType_update_stginfo() for each
# _fields_ assignment.
class Base(Structure):
_fields_ = [('y', c_double)]
class Mid(Base):
_fields_ = []
class Vector(Mid):
_fields_ = [('x', c_double)]
self._test_issue18060(Vector)
def test_array_in_struct(self):
# See bpo-22273
# Load the shared library
dll = CDLL(_ctypes_test.__file__)
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
class Test2(Structure):
_fields_ = [
('data', c_ubyte * 16),
]
class Test3(Structure):
_fields_ = [
('data', c_double * 2),
]
class Test3A(Structure):
class Test3AParent(Structure):
_fields_ = [
('data', c_float * 2),
]
class Test3B(Test3A):
class Test3A(Test3AParent):
_fields_ = [
('more_data', c_float * 2),
]
class Test3B(Structure):
_fields_ = [
('data', c_double * 2),
]
class Test3C(Structure):
_fields_ = [
("data", c_double * 4)
]
class Test3D(Structure):
_fields_ = [
("data", c_double * 8)
]
class Test3E(Structure):
_fields_ = [
("data", c_double * 9)
]
# Tests for struct Test2
s = Test2()
expected = 0
for i in range(16):
s.data[i] = i
expected += i
dll = CDLL(_ctypes_test.__file__)
func = dll._testfunc_array_in_struct1
func = dll._testfunc_array_in_struct2
func.restype = c_int
func.argtypes = (Test2,)
result = func(s)
@@ -523,29 +624,16 @@ class StructureTestCase(unittest.TestCase):
for i in range(16):
self.assertEqual(s.data[i], i)
s = Test3()
s.data[0] = 3.14159
s.data[1] = 2.71828
expected = 3.14159 + 2.71828
func = dll._testfunc_array_in_struct2
func.restype = c_double
func.argtypes = (Test3,)
result = func(s)
self.assertEqual(result, expected)
# check the passed-in struct hasn't changed
self.assertEqual(s.data[0], 3.14159)
self.assertEqual(s.data[1], 2.71828)
s = Test3B()
# Tests for struct Test3A
s = Test3A()
s.data[0] = 3.14159
s.data[1] = 2.71828
s.more_data[0] = -3.0
s.more_data[1] = -2.0
expected = 3.14159 + 2.71828 - 5.0
func = dll._testfunc_array_in_struct2a
expected = 3.14159 + 2.71828 - 3.0 - 2.0
func = dll._testfunc_array_in_struct3A
func.restype = c_double
func.argtypes = (Test3B,)
func.argtypes = (Test3A,)
result = func(s)
self.assertAlmostEqual(result, expected, places=6)
# check the passed-in struct hasn't changed
@@ -554,6 +642,61 @@ class StructureTestCase(unittest.TestCase):
self.assertAlmostEqual(s.more_data[0], -3.0, places=6)
self.assertAlmostEqual(s.more_data[1], -2.0, places=6)
# Test3B, Test3C, Test3D, Test3E have the same logic with different
# sizes hence putting them in a loop.
StructCtype = namedtuple(
"StructCtype",
["cls", "cfunc1", "cfunc2", "items"]
)
structs_to_test = [
StructCtype(
Test3B,
dll._testfunc_array_in_struct3B,
dll._testfunc_array_in_struct3B_set_defaults,
2),
StructCtype(
Test3C,
dll._testfunc_array_in_struct3C,
dll._testfunc_array_in_struct3C_set_defaults,
4),
StructCtype(
Test3D,
dll._testfunc_array_in_struct3D,
dll._testfunc_array_in_struct3D_set_defaults,
8),
StructCtype(
Test3E,
dll._testfunc_array_in_struct3E,
dll._testfunc_array_in_struct3E_set_defaults,
9),
]
for sut in structs_to_test:
s = sut.cls()
# Test for cfunc1
expected = 0
for i in range(sut.items):
float_i = float(i)
s.data[i] = float_i
expected += float_i
func = sut.cfunc1
func.restype = c_double
func.argtypes = (sut.cls,)
result = func(s)
self.assertEqual(result, expected)
# check the passed-in struct hasn't changed
for i in range(sut.items):
self.assertEqual(s.data[i], float(i))
# Test for cfunc2
func = sut.cfunc2
func.restype = sut.cls
result = func()
# check if the default values have been set correctly
for i in range(sut.items):
self.assertEqual(result.data[i], float(i+1))
def test_38368(self):
class U(Union):
_fields_ = [
@@ -738,6 +881,7 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
'a union by value, which is unsupported.')
class PointerMemberTestCase(unittest.TestCase):
def test(self):
@@ -764,8 +908,6 @@ class PointerMemberTestCase(unittest.TestCase):
s.array[0] = 1
## s.array[1] = 42
items = [s.array[i] for i in range(3)]
self.assertEqual(items, [1, 2, 3])
@@ -779,6 +921,7 @@ class PointerMemberTestCase(unittest.TestCase):
s.p = None
self.assertEqual(s.x, 12345678)
class TestRecursiveStructure(unittest.TestCase):
def test_contains_itself(self):
class Recursive(Structure):
@@ -808,5 +951,6 @@ class TestRecursiveStructure(unittest.TestCase):
else:
self.fail("AttributeError not raised")
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,9 @@
import sys, unittest
from ctypes import *
from ctypes import (Structure, BigEndianStructure, LittleEndianStructure,
c_byte, c_short, c_int, c_long, c_longlong,
c_float, c_double,
c_ushort, c_uint, c_ulong, c_ulonglong)
structures = []
byteswapped_structures = []
@@ -24,6 +28,7 @@ for typ in [c_short, c_int, c_long, c_longlong,
structures.append(X)
byteswapped_structures.append(Y)
class TestStructures(unittest.TestCase):
def test_native(self):
for typ in structures:
@@ -39,5 +44,6 @@ class TestStructures(unittest.TestCase):
o.value = 4
self.assertEqual(o.value, 4)
if __name__ == '__main__':
unittest.main()
@@ -1,10 +1,9 @@
import unittest
import ctypes
from ctypes.test import need_symbol
import unittest
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
import _ctypes_test
@need_symbol('c_wchar')
class UnicodeTestCase(unittest.TestCase):
def test_wcslen(self):
dll = ctypes.CDLL(_ctypes_test.__file__)
+35
View File
@@ -0,0 +1,35 @@
import unittest
from ctypes import Union, c_char
from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
class ArrayTestCase(unittest.TestCase):
def test_inheritance_hierarchy(self):
self.assertEqual(Union.mro(), [Union, _CData, object])
self.assertEqual(UnionType.__name__, "UnionType")
self.assertEqual(type(UnionType), type)
def test_type_flags(self):
for cls in Union, UnionType:
with self.subTest(cls=Union):
self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
def test_metaclass_details(self):
# Abstract classes (whose metaclass __init__ was not called) can't be
# instantiated directly
NewUnion = UnionType.__new__(UnionType, 'NewUnion',
(Union,), {})
for cls in Union, NewUnion:
with self.subTest(cls=cls):
with self.assertRaisesRegex(TypeError, "abstract class"):
obj = cls()
# Cannot call the metaclass __init__ more than once
class T(Union):
_fields_ = [("x", c_char),
("y", c_char)]
with self.assertRaisesRegex(SystemError, "already initialized"):
UnionType.__init__(T, 'ptr', (), {})
@@ -4,19 +4,24 @@ A testcase which accesses *values* in a dll.
import _imp
import importlib.util
import unittest
import sys
from ctypes import *
import unittest
from ctypes import (Structure, CDLL, POINTER, pythonapi,
_pointer_type_cache,
c_ubyte, c_char_p, c_int)
from test.support import import_helper
import _ctypes_test
class ValuesTestCase(unittest.TestCase):
def setUp(self):
_ctypes_test = import_helper.import_module("_ctypes_test")
self.ctdll = CDLL(_ctypes_test.__file__)
def test_an_integer(self):
# This test checks and changes an integer stored inside the
# _ctypes_test dll/shared lib.
ctdll = CDLL(_ctypes_test.__file__)
ctdll = self.ctdll
an_integer = c_int.in_dll(ctdll, "an_integer")
x = an_integer.value
self.assertEqual(x, ctdll.get_an_integer())
@@ -28,12 +33,14 @@ class ValuesTestCase(unittest.TestCase):
self.assertEqual(x, ctdll.get_an_integer())
def test_undefined(self):
ctdll = CDLL(_ctypes_test.__file__)
self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol")
self.assertRaises(ValueError, c_int.in_dll, self.ctdll, "Undefined_Symbol")
class PythonValuesTestCase(unittest.TestCase):
"""This test only works when python itself is a dll/shared library"""
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optimizeflag(self):
# This test accesses the Py_OptimizeFlag integer, which is
# exported by the Python dll and should match the sys.flags value
@@ -41,6 +48,8 @@ class PythonValuesTestCase(unittest.TestCase):
opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value
self.assertEqual(opt, sys.flags.optimize)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_frozentable(self):
# Python exports a PyImport_FrozenModules symbol. This is a
# pointer to an array of struct _frozen entries. The end of the
@@ -55,7 +64,6 @@ class PythonValuesTestCase(unittest.TestCase):
("code", POINTER(c_ubyte)),
("size", c_int),
("is_package", c_int),
("get_code", POINTER(c_ubyte)), # Function ptr
]
FrozenTable = POINTER(struct_frozen)
@@ -92,12 +100,12 @@ class PythonValuesTestCase(unittest.TestCase):
"_PyImport_FrozenBootstrap example "
"in Doc/library/ctypes.rst may be out of date")
from ctypes import _pointer_type_cache
del _pointer_type_cache[struct_frozen]
def test_undefined(self):
self.assertRaises(ValueError, c_int.in_dll, pythonapi,
"Undefined_Symbol")
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,6 @@
from ctypes import *
import unittest
from ctypes import Structure, sizeof, resize, c_int
class VarSizeTest(unittest.TestCase):
def test_resize(self):
@@ -46,5 +47,6 @@ class VarSizeTest(unittest.TestCase):
self.assertRaises(IndexError, array.__setitem__, -1, None)
self.assertRaises(IndexError, array.__getitem__, -1)
if __name__ == "__main__":
unittest.main()
@@ -1,33 +1,44 @@
# Windows specific tests
from ctypes import *
import unittest, sys
import ctypes
import errno
import sys
import unittest
from ctypes import (CDLL, Structure, POINTER, pointer, sizeof, byref,
_pointer_type_cache,
c_void_p, c_char, c_int, c_long)
from test import support
from test.support import import_helper
from ._support import Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE
import _ctypes_test
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class FunctionCallTestCase(unittest.TestCase):
@unittest.skipUnless('MSC' in sys.version, "SEH only supported by MSC")
@unittest.skipIf(sys.executable.lower().endswith('_d.exe'),
"SEH not enabled in debug builds")
# TODO: RUSTPYTHON - SEH not implemented
@unittest.skipIf("RustPython" in sys.version, "SEH not implemented in RustPython")
def test_SEH(self):
# Disable faulthandler to prevent logging the warning:
# "Windows fatal exception: access violation"
kernel32 = ctypes.windll.kernel32
with support.disable_faulthandler():
# Call functions with invalid arguments, and make sure
# that access violations are trapped and raise an
# exception.
self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32)
self.assertRaises(OSError, kernel32.GetModuleHandleA, 32)
def test_noargs(self):
# This is a special case on win32 x64
windll.user32.GetDesktopWindow()
user32 = ctypes.windll.user32
user32.GetDesktopWindow()
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class ReturnStructSizesTestCase(unittest.TestCase):
def test_sizes(self):
_ctypes_test = import_helper.import_module("_ctypes_test")
dll = CDLL(_ctypes_test.__file__)
for i in range(1, 11):
fields = [ (f"f{f}", c_char) for f in range(1, i + 1)]
@@ -42,7 +53,6 @@ class ReturnStructSizesTestCase(unittest.TestCase):
self.assertEqual(value, expected)
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class TestWintypes(unittest.TestCase):
def test_HWND(self):
@@ -67,29 +77,36 @@ class TestWintypes(unittest.TestCase):
self.assertEqual(ex.text, "text")
self.assertEqual(ex.details, ("details",))
self.assertEqual(COMError.mro(),
[COMError, Exception, BaseException, object])
self.assertFalse(COMError.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
self.assertTrue(COMError.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class TestWinError(unittest.TestCase):
def test_winerror(self):
# see Issue 16169
import errno
ERROR_INVALID_PARAMETER = 87
msg = FormatError(ERROR_INVALID_PARAMETER).strip()
msg = ctypes.FormatError(ERROR_INVALID_PARAMETER).strip()
args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER)
e = WinError(ERROR_INVALID_PARAMETER)
e = ctypes.WinError(ERROR_INVALID_PARAMETER)
self.assertEqual(e.args, args)
self.assertEqual(e.errno, errno.EINVAL)
self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)
windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER)
kernel32 = ctypes.windll.kernel32
kernel32.SetLastError(ERROR_INVALID_PARAMETER)
try:
raise WinError()
raise ctypes.WinError()
except OSError as exc:
e = exc
self.assertEqual(e.args, args)
self.assertEqual(e.errno, errno.EINVAL)
self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)
class Structures(unittest.TestCase):
def test_struct_by_value(self):
class POINT(Structure):
@@ -102,6 +119,7 @@ class Structures(unittest.TestCase):
("right", c_long),
("bottom", c_long)]
_ctypes_test = import_helper.import_module("_ctypes_test")
dll = CDLL(_ctypes_test.__file__)
pt = POINT(15, 25)
@@ -129,8 +147,8 @@ class Structures(unittest.TestCase):
self.assertEqual(ret.bottom, bottom.value)
# to not leak references, we must clean _pointer_type_cache
from ctypes import _pointer_type_cache
del _pointer_type_cache[RECT]
if __name__ == '__main__':
unittest.main()
+288
View File
@@ -0,0 +1,288 @@
import ctypes
import gc
import sys
import unittest
from ctypes import POINTER, byref, c_void_p
from ctypes.wintypes import BYTE, DWORD, WORD
if sys.platform != "win32":
raise unittest.SkipTest("Windows-specific test")
from _ctypes import COMError, CopyComPointer
from ctypes import HRESULT
COINIT_APARTMENTTHREADED = 0x2
CLSCTX_SERVER = 5
S_OK = 0
OUT = 2
TRUE = 1
E_NOINTERFACE = -2147467262
class GUID(ctypes.Structure):
# https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
_fields_ = [
("Data1", DWORD),
("Data2", WORD),
("Data3", WORD),
("Data4", BYTE * 8),
]
def create_proto_com_method(name, index, restype, *argtypes):
proto = ctypes.WINFUNCTYPE(restype, *argtypes)
def make_method(*args):
foreign_func = proto(index, name, *args)
def call(self, *args, **kwargs):
return foreign_func(self, *args, **kwargs)
return call
return make_method
def create_guid(name):
guid = GUID()
# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-clsidfromstring
ole32.CLSIDFromString(name, byref(guid))
return guid
def is_equal_guid(guid1, guid2):
# https://learn.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-isequalguid
return ole32.IsEqualGUID(byref(guid1), byref(guid2))
ole32 = ctypes.oledll.ole32
IID_IUnknown = create_guid("{00000000-0000-0000-C000-000000000046}")
IID_IStream = create_guid("{0000000C-0000-0000-C000-000000000046}")
IID_IPersist = create_guid("{0000010C-0000-0000-C000-000000000046}")
CLSID_ShellLink = create_guid("{00021401-0000-0000-C000-000000000046}")
# https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
proto_query_interface = create_proto_com_method(
"QueryInterface", 0, HRESULT, POINTER(GUID), POINTER(c_void_p)
)
# https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
proto_add_ref = create_proto_com_method("AddRef", 1, ctypes.c_long)
# https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
proto_release = create_proto_com_method("Release", 2, ctypes.c_long)
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ipersist-getclassid
proto_get_class_id = create_proto_com_method(
"GetClassID", 3, HRESULT, POINTER(GUID)
)
def create_shelllink_persist(typ):
ppst = typ()
# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
ole32.CoCreateInstance(
byref(CLSID_ShellLink),
None,
CLSCTX_SERVER,
byref(IID_IPersist),
byref(ppst),
)
return ppst
class ForeignFunctionsThatWillCallComMethodsTests(unittest.TestCase):
def setUp(self):
# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex
ole32.CoInitializeEx(None, COINIT_APARTMENTTHREADED)
def tearDown(self):
# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize
ole32.CoUninitialize()
gc.collect()
def test_without_paramflags_and_iid(self):
class IUnknown(c_void_p):
QueryInterface = proto_query_interface()
AddRef = proto_add_ref()
Release = proto_release()
class IPersist(IUnknown):
GetClassID = proto_get_class_id()
ppst = create_shelllink_persist(IPersist)
clsid = GUID()
hr_getclsid = ppst.GetClassID(byref(clsid))
self.assertEqual(S_OK, hr_getclsid)
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
self.assertEqual(2, ppst.AddRef())
self.assertEqual(3, ppst.AddRef())
punk = IUnknown()
hr_qi = ppst.QueryInterface(IID_IUnknown, punk)
self.assertEqual(S_OK, hr_qi)
self.assertEqual(3, punk.Release())
with self.assertRaises(OSError) as e:
punk.QueryInterface(IID_IStream, IUnknown())
self.assertEqual(E_NOINTERFACE, e.exception.winerror)
self.assertEqual(2, ppst.Release())
self.assertEqual(1, ppst.Release())
self.assertEqual(0, ppst.Release())
def test_with_paramflags_and_without_iid(self):
class IUnknown(c_void_p):
QueryInterface = proto_query_interface(None)
AddRef = proto_add_ref()
Release = proto_release()
class IPersist(IUnknown):
GetClassID = proto_get_class_id(((OUT, "pClassID"),))
ppst = create_shelllink_persist(IPersist)
clsid = ppst.GetClassID()
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
punk = IUnknown()
hr_qi = ppst.QueryInterface(IID_IUnknown, punk)
self.assertEqual(S_OK, hr_qi)
self.assertEqual(1, punk.Release())
with self.assertRaises(OSError) as e:
ppst.QueryInterface(IID_IStream, IUnknown())
self.assertEqual(E_NOINTERFACE, e.exception.winerror)
self.assertEqual(0, ppst.Release())
# TODO: RUSTPYTHON - COM iid parameter handling not implemented
@unittest.expectedFailure
def test_with_paramflags_and_iid(self):
class IUnknown(c_void_p):
QueryInterface = proto_query_interface(None, IID_IUnknown)
AddRef = proto_add_ref()
Release = proto_release()
class IPersist(IUnknown):
GetClassID = proto_get_class_id(((OUT, "pClassID"),), IID_IPersist)
ppst = create_shelllink_persist(IPersist)
clsid = ppst.GetClassID()
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
punk = IUnknown()
hr_qi = ppst.QueryInterface(IID_IUnknown, punk)
self.assertEqual(S_OK, hr_qi)
self.assertEqual(1, punk.Release())
with self.assertRaises(COMError) as e:
ppst.QueryInterface(IID_IStream, IUnknown())
self.assertEqual(E_NOINTERFACE, e.exception.hresult)
self.assertEqual(0, ppst.Release())
class CopyComPointerTests(unittest.TestCase):
def setUp(self):
ole32.CoInitializeEx(None, COINIT_APARTMENTTHREADED)
class IUnknown(c_void_p):
QueryInterface = proto_query_interface(None, IID_IUnknown)
AddRef = proto_add_ref()
Release = proto_release()
class IPersist(IUnknown):
GetClassID = proto_get_class_id(((OUT, "pClassID"),), IID_IPersist)
self.IUnknown = IUnknown
self.IPersist = IPersist
def tearDown(self):
ole32.CoUninitialize()
gc.collect()
def test_both_are_null(self):
src = self.IPersist()
dst = self.IPersist()
hr = CopyComPointer(src, byref(dst))
self.assertEqual(S_OK, hr)
self.assertIsNone(src.value)
self.assertIsNone(dst.value)
def test_src_is_nonnull_and_dest_is_null(self):
# The reference count of the COM pointer created by `CoCreateInstance`
# is initially 1.
src = create_shelllink_persist(self.IPersist)
dst = self.IPersist()
# `CopyComPointer` calls `AddRef` explicitly in the C implementation.
# The refcount of `src` is incremented from 1 to 2 here.
hr = CopyComPointer(src, byref(dst))
self.assertEqual(S_OK, hr)
self.assertEqual(src.value, dst.value)
# This indicates that the refcount was 2 before the `Release` call.
self.assertEqual(1, src.Release())
clsid = dst.GetClassID()
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
self.assertEqual(0, dst.Release())
def test_src_is_null_and_dest_is_nonnull(self):
src = self.IPersist()
dst_orig = create_shelllink_persist(self.IPersist)
dst = self.IPersist()
CopyComPointer(dst_orig, byref(dst))
self.assertEqual(1, dst_orig.Release())
clsid = dst.GetClassID()
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
# This does NOT affects the refcount of `dst_orig`.
hr = CopyComPointer(src, byref(dst))
self.assertEqual(S_OK, hr)
self.assertIsNone(dst.value)
with self.assertRaises(ValueError):
dst.GetClassID() # NULL COM pointer access
# This indicates that the refcount was 1 before the `Release` call.
self.assertEqual(0, dst_orig.Release())
def test_both_are_nonnull(self):
src = create_shelllink_persist(self.IPersist)
dst_orig = create_shelllink_persist(self.IPersist)
dst = self.IPersist()
CopyComPointer(dst_orig, byref(dst))
self.assertEqual(1, dst_orig.Release())
self.assertEqual(dst.value, dst_orig.value)
self.assertNotEqual(src.value, dst.value)
hr = CopyComPointer(src, byref(dst))
self.assertEqual(S_OK, hr)
self.assertEqual(src.value, dst.value)
self.assertNotEqual(dst.value, dst_orig.value)
self.assertEqual(1, src.Release())
clsid = dst.GetClassID()
self.assertEqual(TRUE, is_equal_guid(CLSID_ShellLink, clsid))
self.assertEqual(0, dst.Release())
self.assertEqual(0, dst_orig.Release())
if __name__ == '__main__':
unittest.main()
@@ -1,8 +1,10 @@
# See <https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types>
# for reference.
#
# Tests also work on POSIX
import unittest
# also work on POSIX
from ctypes import *
from ctypes import POINTER, cast, c_int16
from ctypes import wintypes
@@ -38,6 +40,22 @@ class WinTypesTest(unittest.TestCase):
vb.value = []
self.assertIs(vb.value, False)
def assertIsSigned(self, ctype):
self.assertLess(ctype(-1).value, 0)
def assertIsUnsigned(self, ctype):
self.assertGreater(ctype(-1).value, 0)
def test_signedness(self):
for ctype in (wintypes.BYTE, wintypes.WORD, wintypes.DWORD,
wintypes.BOOLEAN, wintypes.UINT, wintypes.ULONG):
with self.subTest(ctype=ctype):
self.assertIsUnsigned(ctype)
for ctype in (wintypes.BOOL, wintypes.INT, wintypes.LONG):
with self.subTest(ctype=ctype):
self.assertIsSigned(ctype)
if __name__ == "__main__":
unittest.main()
+1
View File
@@ -430,6 +430,7 @@ class ExceptionTests(unittest.TestCase):
@unittest.skipUnless(sys.platform == 'win32',
'test specific to Windows')
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_windows_message(self):
"""Should fill in unknown error code in Windows error message"""
ctypes = import_module('ctypes')
-3
View File
@@ -939,7 +939,6 @@ class UtimeTests(unittest.TestCase):
return buf.value
# return None if the filesystem is unknown
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
def test_large_time(self):
# Many filesystems are limited to the year 2038. At least, the test
# pass with NTFS filesystem.
@@ -2712,12 +2711,10 @@ class Win32KillTests(unittest.TestCase):
os.kill(proc.pid, sig)
self.assertEqual(proc.wait(), sig)
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
def test_kill_sigterm(self):
# SIGTERM doesn't mean anything special, but make sure it works
self._kill(signal.SIGTERM)
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
def test_kill_int(self):
# os.kill on Windows can take an int which gets set as the exit code
self._kill(100)
-1
View File
@@ -598,7 +598,6 @@ class TestSysConfig(unittest.TestCase):
self.assertTrue(suffix.endswith(f"-{expected_triplet}.so"),
f"{machine=}, {suffix=}")
@unittest.expectedFailure # TODO: RUSTPYTHON
@unittest.skipUnless(sys.platform == 'darwin', 'OS X-specific test')
def test_osx_ext_suffix(self):
suffix = sysconfig.get_config_var('EXT_SUFFIX')
+76 -26
View File
@@ -95,6 +95,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
pointer::PyCPointerType::make_class(ctx);
structure::PyCStructType::make_class(ctx);
union::PyCUnionType::make_class(ctx);
function::PyCFuncPtrType::make_class(ctx);
extend_module!(vm, &module, {
"_CData" => PyCData::make_class(ctx),
"_SimpleCData" => PyCSimple::make_class(ctx),
@@ -385,12 +386,8 @@ pub(crate) mod _ctypes {
#[pyattr]
const RTLD_GLOBAL: i32 = 0;
#[cfg(target_os = "windows")]
#[pyattr]
const SIZEOF_TIME_T: usize = 8;
#[cfg(not(target_os = "windows"))]
#[pyattr]
const SIZEOF_TIME_T: usize = 4;
const SIZEOF_TIME_T: usize = std::mem::size_of::<libc::time_t>();
#[pyattr]
const CTYPES_MAX_ARGCOUNT: usize = 1024;
@@ -578,30 +575,42 @@ pub(crate) mod _ctypes {
#[pyfunction(name = "dlopen")]
fn load_library_unix(
name: Option<crate::function::FsPath>,
_load_flags: OptionalArg<i32>,
load_flags: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
// TODO: audit functions first
// TODO: load_flags
// Default mode: RTLD_NOW | RTLD_LOCAL, always force RTLD_NOW
let mode = load_flags.unwrap_or(libc::RTLD_NOW | libc::RTLD_LOCAL) | libc::RTLD_NOW;
match name {
Some(name) => {
let cache = library::libcache();
let mut cache_write = cache.write();
let os_str = name.as_os_str(vm)?;
let (id, _) = cache_write.get_or_insert_lib(&*os_str, vm).map_err(|e| {
// Include filename in error message for better diagnostics
let name_str = os_str.to_string_lossy();
vm.new_os_error(format!("{}: {}", name_str, e))
})?;
let (id, _) = cache_write
.get_or_insert_lib_with_mode(&*os_str, mode, vm)
.map_err(|e| {
let name_str = os_str.to_string_lossy();
vm.new_os_error(format!("{}: {}", name_str, e))
})?;
Ok(id)
}
None => {
// If None, call libc::dlopen(null, mode) to get the current process handle
let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) };
// dlopen(NULL, mode) to get the current process handle (for pythonapi)
let handle = unsafe { libc::dlopen(std::ptr::null(), mode) };
if handle.is_null() {
return Err(vm.new_os_error("dlopen() error"));
let err = unsafe { libc::dlerror() };
let msg = if err.is_null() {
"dlopen() error".to_string()
} else {
unsafe { std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned() }
};
return Err(vm.new_os_error(msg));
}
Ok(handle as usize)
// Add to library cache so symbol lookup works
let cache = library::libcache();
let mut cache_write = cache.write();
let id = cache_write.insert_raw_handle(handle);
Ok(id)
}
}
}
@@ -614,6 +623,48 @@ pub(crate) mod _ctypes {
Ok(())
}
#[cfg(not(windows))]
#[pyfunction]
fn dlclose(handle: usize, _vm: &VirtualMachine) -> PyResult<()> {
// Remove from cache, which triggers SharedLibrary drop.
// libloading::Library calls dlclose automatically on Drop.
let cache = library::libcache();
let mut cache_write = cache.write();
cache_write.drop_lib(handle);
Ok(())
}
#[cfg(not(windows))]
#[pyfunction]
fn dlsym(
handle: usize,
name: crate::builtins::PyStrRef,
vm: &VirtualMachine,
) -> PyResult<usize> {
let symbol_name = std::ffi::CString::new(name.as_str())
.map_err(|_| vm.new_value_error("symbol name contains null byte"))?;
// Clear previous error
unsafe { libc::dlerror() };
let ptr = unsafe { libc::dlsym(handle as *mut libc::c_void, symbol_name.as_ptr()) };
// Check for error via dlerror first
let err = unsafe { libc::dlerror() };
if !err.is_null() {
let msg = unsafe { std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned() };
return Err(vm.new_os_error(msg));
}
// Treat NULL symbol address as error
// This handles cases like GNU IFUNCs that resolve to NULL
if ptr.is_null() {
return Err(vm.new_os_error(format!("symbol '{}' not found", name.as_str())));
}
Ok(ptr as usize)
}
#[pyfunction(name = "POINTER")]
fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult {
use crate::builtins::PyStr;
@@ -905,25 +956,24 @@ pub(crate) mod _ctypes {
#[pyfunction]
fn get_errno() -> i32 {
errno::errno().0
super::function::get_errno_value()
}
#[pyfunction]
fn set_errno(value: i32) {
errno::set_errno(errno::Errno(value));
fn set_errno(value: i32) -> i32 {
super::function::set_errno_value(value)
}
#[cfg(windows)]
#[pyfunction]
fn get_last_error() -> PyResult<u32> {
Ok(unsafe { windows_sys::Win32::Foundation::GetLastError() })
Ok(super::function::get_last_error_value())
}
#[cfg(windows)]
#[pyfunction]
fn set_last_error(value: u32) -> PyResult<()> {
unsafe { windows_sys::Win32::Foundation::SetLastError(value) };
Ok(())
fn set_last_error(value: u32) -> u32 {
super::function::set_last_error_value(value)
}
#[pyattr]
@@ -1084,9 +1134,9 @@ pub(crate) mod _ctypes {
ffi_args.push(Arg::new(val));
}
let cif = Cif::new(arg_types, Type::isize());
let cif = Cif::new(arg_types, Type::c_int());
let code_ptr = CodePtr::from_ptr(func_addr as *const _);
let result: isize = unsafe { cif.call(code_ptr, &ffi_args) };
let result: libc::c_int = unsafe { cif.call(code_ptr, &ffi_args) };
Ok(vm.ctx.new_int(result).into())
}
+38 -6
View File
@@ -1,9 +1,12 @@
use super::StgInfo;
use super::base::{CDATA_BUFFER_METHODS, PyCData};
use super::type_info;
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
atomic_func,
builtins::{PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef},
builtins::{
PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef, genericalias::PyGenericAlias,
},
class::StaticType,
function::{ArgBytesLike, FuncArgs, PySetterValue},
protocol::{BufferDescriptor, PyBuffer, PyNumberMethods, PySequenceMethods},
@@ -11,6 +14,19 @@ use crate::{
};
use num_traits::{Signed, ToPrimitive};
/// Get itemsize from a PEP 3118 format string
/// Extracts the type code (last char after endianness prefix) and returns its size
fn get_size_from_format(fmt: &str) -> usize {
// Format is like "<f", ">q", etc. - strip endianness prefix and get type code
let code = fmt
.trim_start_matches(['<', '>', '@', '=', '!', '&'])
.chars()
.next()
.map(|c| c.to_string());
code.map(|c| type_info(&c).map(|t| t.size).unwrap_or(1))
.unwrap_or(1)
}
/// Creates array type for (element_type, length)
/// Uses _array_type_cache to ensure identical calls return the same type object
pub(super) fn array_type_from_ctype(
@@ -444,6 +460,11 @@ impl AsSequence for PyCArray {
with(Constructor, AsSequence, AsBuffer)
)]
impl PyCArray {
#[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}
fn int_to_bytes(i: &malachite_bigint::BigInt, size: usize) -> Vec<u8> {
// Try unsigned first (handles values like 0xFFFFFFFF that overflow signed)
// then fall back to signed (handles negative values)
@@ -1056,19 +1077,30 @@ impl AsBuffer for PyCArray {
.expect("PyCArray type must have StgInfo");
let format = stg_info.format.clone();
let shape = stg_info.shape.clone();
let element_size = stg_info.element_size;
let desc = if let Some(fmt) = format
&& !shape.is_empty()
{
// itemsize is the size of the base element type (item_info->size)
// For empty arrays, we still need the element size, not 0
let total_elements: usize = shape.iter().product();
let has_zero_dim = shape.contains(&0);
let itemsize = if total_elements > 0 && buffer_len > 0 {
buffer_len / total_elements
} else {
// For empty arrays, get itemsize from format type code
get_size_from_format(&fmt)
};
// Build dim_desc from shape (C-contiguous: row-major order)
// stride[i] = product(shape[i+1:]) * itemsize
// For empty arrays (any dimension is 0), all strides are 0
let mut dim_desc = Vec::with_capacity(shape.len());
let mut stride = element_size as isize;
let mut stride = itemsize as isize;
// Calculate strides from innermost to outermost dimension
for &dim_size in shape.iter().rev() {
dim_desc.push((dim_size, stride, 0));
let current_stride = if has_zero_dim { 0 } else { stride };
dim_desc.push((dim_size, current_stride, 0));
stride *= dim_size as isize;
}
dim_desc.reverse();
@@ -1076,7 +1108,7 @@ impl AsBuffer for PyCArray {
BufferDescriptor {
len: buffer_len,
readonly: false,
itemsize: element_size,
itemsize,
format: std::borrow::Cow::Owned(fmt),
dim_desc,
}
+48 -26
View File
@@ -300,15 +300,27 @@ pub(super) fn get_field_format(
big_endian: bool,
vm: &VirtualMachine,
) -> String {
let endian_prefix = if big_endian { ">" } else { "<" };
// 1. Check StgInfo for format
if let Some(type_obj) = field_type.downcast_ref::<PyType>()
&& let Some(stg_info) = type_obj.stg_info_opt()
&& let Some(fmt) = &stg_info.format
{
// Handle endian prefix for simple types
if fmt.len() == 1 {
let endian_prefix = if big_endian { ">" } else { "<" };
return format!("{}{}", endian_prefix, fmt);
// For structures (T{...}), arrays ((n)...), and pointers (&...), return as-is
// These complex types have their own endianness markers inside
if fmt.starts_with('T')
|| fmt.starts_with('(')
|| fmt.starts_with('&')
|| fmt.starts_with("X{")
{
return fmt.clone();
}
// For simple types, replace existing endian prefix with the correct one
let base_fmt = fmt.trim_start_matches(['<', '>', '@', '=', '!']);
if !base_fmt.is_empty() {
return format!("{}{}", endian_prefix, base_fmt);
}
return fmt.clone();
}
@@ -318,8 +330,7 @@ pub(super) fn get_field_format(
&& let Some(type_str) = type_attr.downcast_ref::<PyStr>()
{
let s = type_str.as_str();
if s.len() == 1 {
let endian_prefix = if big_endian { ">" } else { "<" };
if !s.is_empty() {
return format!("{}{}", endian_prefix, s);
}
return s.to_string();
@@ -1168,29 +1179,30 @@ impl PyCData {
.ok_or_else(|| vm.new_value_error("Invalid library handle"))?
};
// Get symbol address using platform-specific API
let symbol_name = std::ffi::CString::new(name.as_str())
.map_err(|_| vm.new_value_error("Invalid symbol name"))?;
// Look up the library in the cache and use lib.get() for symbol lookup
let library_cache = super::library::libcache().read();
let library = library_cache
.get_lib(handle)
.ok_or_else(|| vm.new_value_error("Library not found"))?;
let inner_lib = library.lib.lock();
#[cfg(windows)]
let ptr: *const u8 = unsafe {
match windows_sys::Win32::System::LibraryLoader::GetProcAddress(
handle as windows_sys::Win32::Foundation::HMODULE,
symbol_name.as_ptr() as *const u8,
) {
Some(p) => p as *const u8,
None => std::ptr::null(),
let symbol_name_with_nul = format!("{}\0", name.as_str());
let ptr: *const u8 = if let Some(lib) = &*inner_lib {
unsafe {
lib.get::<*const u8>(symbol_name_with_nul.as_bytes())
.map(|sym| *sym)
.map_err(|_| {
vm.new_value_error(format!("symbol '{}' not found", name.as_str()))
})?
}
} else {
return Err(vm.new_value_error("Library closed"));
};
#[cfg(not(windows))]
let ptr: *const u8 =
unsafe { libc::dlsym(handle as *mut libc::c_void, symbol_name.as_ptr()) as *const u8 };
// dlsym can return NULL for symbols that resolve to NULL (e.g., GNU IFUNC)
// Treat NULL addresses as errors
if ptr.is_null() {
return Err(
vm.new_value_error(format!("symbol '{}' not found in library", name.as_str()))
);
return Err(vm.new_value_error(format!("symbol '{}' not found", name.as_str())));
}
// PyCData_AtAddress
@@ -1593,7 +1605,7 @@ impl PyCField {
/// PyCField_set
#[pyslot]
fn descr_set(
zelf: &crate::PyObject,
zelf: &PyObject,
obj: PyObjectRef,
value: PySetterValue<PyObjectRef>,
vm: &VirtualMachine,
@@ -1804,7 +1816,7 @@ pub enum FfiArgValue {
F64(f64),
Pointer(usize),
/// Pointer with owned data. The PyObjectRef keeps the pointed data alive.
OwnedPointer(usize, #[allow(dead_code)] crate::PyObjectRef),
OwnedPointer(usize, #[allow(dead_code)] PyObjectRef),
}
impl FfiArgValue {
@@ -2145,6 +2157,16 @@ pub(super) fn read_ptr_from_buffer(buffer: &[u8]) -> usize {
}
}
/// Check if a type is a "simple instance" (direct subclass of a simple type)
/// Returns TRUE for c_int, c_void_p, etc. (simple types with _type_ attribute)
/// Returns FALSE for Structure, Array, POINTER(T), etc.
pub(super) fn is_simple_instance(typ: &Py<PyType>) -> bool {
// _ctypes_simple_instance
// Check if the type's metaclass is PyCSimpleType
let metaclass = typ.class();
metaclass.fast_issubclass(super::simple::PyCSimpleType::static_type())
}
/// Set or initialize StgInfo on a type
pub(super) fn set_or_init_stginfo(type_ref: &PyType, stg_info: StgInfo) {
if type_ref.init_type_data(stg_info.clone()).is_err()
-306
View File
@@ -1,306 +0,0 @@
use crate::builtins::PyType;
use crate::function::PySetterValue;
use crate::types::{GetDescriptor, Representable};
use crate::{AsObject, Py, PyObject, PyObjectRef, PyResult, VirtualMachine};
use num_traits::ToPrimitive;
use super::structure::PyCStructure;
use super::union::PyCUnion;
#[pyclass(name = "PyCFieldType", base = PyType, module = "_ctypes")]
#[derive(Debug)]
pub struct PyCFieldType {
pub _base: PyType,
#[allow(dead_code)]
pub(super) inner: PyCField,
}
#[pyclass]
impl PyCFieldType {}
#[pyclass(name = "CField", module = "_ctypes")]
#[derive(Debug, PyPayload)]
pub struct PyCField {
pub(super) byte_offset: usize,
pub(super) byte_size: usize,
#[allow(unused)]
pub(super) index: usize,
/// The ctypes type for this field (can be any ctypes type including arrays)
pub(super) proto: PyObjectRef,
pub(super) anonymous: bool,
pub(super) bitfield_size: bool,
pub(super) bit_offset: u8,
pub(super) name: String,
}
impl PyCField {
pub fn new(
name: String,
proto: PyObjectRef,
byte_offset: usize,
byte_size: usize,
index: usize,
) -> Self {
Self {
name,
proto,
byte_offset,
byte_size,
index,
anonymous: false,
bitfield_size: false,
bit_offset: 0,
}
}
}
impl Representable for PyCField {
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
// Get type name from the proto object
let tp_name = if let Some(name_attr) = vm
.ctx
.interned_str("__name__")
.and_then(|s| zelf.proto.get_attr(s, vm).ok())
{
name_attr.str(vm)?.to_string()
} else {
zelf.proto.class().name().to_string()
};
if zelf.bitfield_size {
Ok(format!(
"<{} type={}, ofs={byte_offset}, bit_size={bitfield_size}, bit_offset={bit_offset}",
zelf.name,
tp_name,
byte_offset = zelf.byte_offset,
bitfield_size = zelf.bitfield_size,
bit_offset = zelf.bit_offset
))
} else {
Ok(format!(
"<{} type={tp_name}, ofs={}, size={}",
zelf.name, zelf.byte_offset, zelf.byte_size
))
}
}
}
impl GetDescriptor for PyCField {
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
_cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let zelf = zelf
.downcast::<PyCField>()
.map_err(|_| vm.new_type_error("expected CField".to_owned()))?;
// If obj is None, return the descriptor itself (class attribute access)
let obj = match obj {
Some(obj) if !vm.is_none(&obj) => obj,
_ => return Ok(zelf.into()),
};
// Instance attribute access - read value from the structure/union's buffer
if let Some(structure) = obj.downcast_ref::<PyCStructure>() {
let cdata = structure.cdata.read();
let offset = zelf.byte_offset;
let size = zelf.byte_size;
if offset + size <= cdata.buffer.len() {
let bytes = &cdata.buffer[offset..offset + size];
return PyCField::bytes_to_value(bytes, size, vm);
}
} else if let Some(union) = obj.downcast_ref::<PyCUnion>() {
let cdata = union.cdata.read();
let offset = zelf.byte_offset;
let size = zelf.byte_size;
if offset + size <= cdata.buffer.len() {
let bytes = &cdata.buffer[offset..offset + size];
return PyCField::bytes_to_value(bytes, size, vm);
}
}
// Fallback: return 0 for uninitialized or unsupported types
Ok(vm.ctx.new_int(0).into())
}
}
impl PyCField {
/// Convert bytes to a Python value based on size
fn bytes_to_value(bytes: &[u8], size: usize, vm: &VirtualMachine) -> PyResult {
match size {
1 => Ok(vm.ctx.new_int(bytes[0] as i8).into()),
2 => {
let val = i16::from_ne_bytes([bytes[0], bytes[1]]);
Ok(vm.ctx.new_int(val).into())
}
4 => {
let val = i32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
Ok(vm.ctx.new_int(val).into())
}
8 => {
let val = i64::from_ne_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
Ok(vm.ctx.new_int(val).into())
}
_ => Ok(vm.ctx.new_int(0).into()),
}
}
/// Convert a Python value to bytes
fn value_to_bytes(value: &PyObject, size: usize, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
if let Ok(int_val) = value.try_int(vm) {
let i = int_val.as_bigint();
match size {
1 => {
let val = i.to_i8().unwrap_or(0);
Ok(val.to_ne_bytes().to_vec())
}
2 => {
let val = i.to_i16().unwrap_or(0);
Ok(val.to_ne_bytes().to_vec())
}
4 => {
let val = i.to_i32().unwrap_or(0);
Ok(val.to_ne_bytes().to_vec())
}
8 => {
let val = i.to_i64().unwrap_or(0);
Ok(val.to_ne_bytes().to_vec())
}
_ => Ok(vec![0u8; size]),
}
} else {
Ok(vec![0u8; size])
}
}
}
#[pyclass(
flags(DISALLOW_INSTANTIATION, IMMUTABLETYPE),
with(Representable, GetDescriptor)
)]
impl PyCField {
#[pyslot]
fn descr_set(
zelf: &crate::PyObject,
obj: PyObjectRef,
value: PySetterValue<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
let zelf = zelf
.downcast_ref::<PyCField>()
.ok_or_else(|| vm.new_type_error("expected CField".to_owned()))?;
// Get the structure/union instance - use downcast_ref() to access the struct data
if let Some(structure) = obj.downcast_ref::<PyCStructure>() {
match value {
PySetterValue::Assign(value) => {
let offset = zelf.byte_offset;
let size = zelf.byte_size;
let bytes = PyCField::value_to_bytes(&value, size, vm)?;
let mut cdata = structure.cdata.write();
if offset + size <= cdata.buffer.len() {
cdata.buffer[offset..offset + size].copy_from_slice(&bytes);
}
Ok(())
}
PySetterValue::Delete => {
Err(vm.new_type_error("cannot delete structure field".to_owned()))
}
}
} else if let Some(union) = obj.downcast_ref::<PyCUnion>() {
match value {
PySetterValue::Assign(value) => {
let offset = zelf.byte_offset;
let size = zelf.byte_size;
let bytes = PyCField::value_to_bytes(&value, size, vm)?;
let mut cdata = union.cdata.write();
if offset + size <= cdata.buffer.len() {
cdata.buffer[offset..offset + size].copy_from_slice(&bytes);
}
Ok(())
}
PySetterValue::Delete => {
Err(vm.new_type_error("cannot delete union field".to_owned()))
}
}
} else {
Err(vm.new_type_error(format!(
"descriptor works only on Structure or Union instances, got {}",
obj.class().name()
)))
}
}
#[pymethod]
fn __set__(
zelf: PyObjectRef,
obj: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Assign(value), vm)
}
#[pymethod]
fn __delete__(zelf: PyObjectRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Delete, vm)
}
#[pygetset]
fn size(&self) -> usize {
self.byte_size
}
#[pygetset]
fn bit_size(&self) -> bool {
self.bitfield_size
}
#[pygetset]
fn is_bitfield(&self) -> bool {
self.bitfield_size
}
#[pygetset]
fn is_anonymous(&self) -> bool {
self.anonymous
}
#[pygetset]
fn name(&self) -> String {
self.name.clone()
}
#[pygetset(name = "type")]
fn type_(&self) -> PyObjectRef {
self.proto.clone()
}
#[pygetset]
fn offset(&self) -> usize {
self.byte_offset
}
#[pygetset]
fn byte_offset(&self) -> usize {
self.byte_offset
}
#[pygetset]
fn byte_size(&self) -> usize {
self.byte_size
}
#[pygetset]
fn bit_offset(&self) -> u8 {
self.bit_offset
}
}
File diff suppressed because it is too large. Load diff
+80 -28
View File
@@ -2,12 +2,14 @@ use crate::VirtualMachine;
use libloading::Library;
use rustpython_common::lock::{PyMutex, PyRwLock};
use std::collections::HashMap;
use std::ffi::{OsStr, c_void};
use std::ffi::OsStr;
use std::fmt;
use std::ptr::null;
pub(super) struct SharedLibrary {
pub(super) lib: PyMutex<Option<Library>>,
#[cfg(unix)]
use libloading::os::unix::Library as UnixLibrary;
pub struct SharedLibrary {
pub(crate) lib: PyMutex<Option<Library>>,
}
impl fmt::Debug for SharedLibrary {
@@ -17,18 +19,44 @@ impl fmt::Debug for SharedLibrary {
}
impl SharedLibrary {
fn new(name: impl AsRef<OsStr>) -> Result<SharedLibrary, libloading::Error> {
#[cfg(windows)]
pub fn new(name: impl AsRef<OsStr>) -> Result<SharedLibrary, libloading::Error> {
Ok(SharedLibrary {
lib: PyMutex::new(unsafe { Some(Library::new(name.as_ref())?) }),
})
}
fn get_pointer(&self) -> usize {
#[cfg(unix)]
pub fn new_with_mode(
name: impl AsRef<OsStr>,
mode: i32,
) -> Result<SharedLibrary, libloading::Error> {
Ok(SharedLibrary {
lib: PyMutex::new(Some(unsafe {
UnixLibrary::open(Some(name.as_ref()), mode)?.into()
})),
})
}
/// Create a SharedLibrary from a raw dlopen handle (for pythonapi / dlopen(NULL))
#[cfg(unix)]
pub fn from_raw_handle(handle: *mut libc::c_void) -> SharedLibrary {
SharedLibrary {
lib: PyMutex::new(Some(unsafe { UnixLibrary::from_raw(handle).into() })),
}
}
/// Get the underlying OS handle (HMODULE on Windows, dlopen handle on Unix)
pub fn get_pointer(&self) -> usize {
let lib_lock = self.lib.lock();
if let Some(l) = &*lib_lock {
l as *const Library as usize
// libloading::Library internally stores the OS handle directly
// On Windows: HMODULE (*mut c_void)
// On Unix: *mut c_void from dlopen
// We use transmute_copy to read the handle without consuming the Library
unsafe { std::mem::transmute_copy::<Library, usize>(l) }
} else {
null::<c_void>() as usize
0
}
}
@@ -36,16 +64,6 @@ impl SharedLibrary {
let lib_lock = self.lib.lock();
lib_lock.is_none()
}
fn close(&self) {
*self.lib.lock() = None;
}
}
impl Drop for SharedLibrary {
fn drop(&mut self) {
self.close();
}
}
pub(super) struct ExternalLibs {
@@ -63,6 +81,7 @@ impl ExternalLibs {
self.libraries.get(&key)
}
#[cfg(windows)]
pub fn get_or_insert_lib(
&mut self,
library_path: impl AsRef<OsStr>,
@@ -71,20 +90,53 @@ impl ExternalLibs {
let new_lib = SharedLibrary::new(library_path)?;
let key = new_lib.get_pointer();
match self.libraries.get(&key) {
Some(l) => {
if l.is_closed() {
self.libraries.insert(key, new_lib);
}
}
_ => {
self.libraries.insert(key, new_lib);
}
};
// Check if library already exists and is not closed
let should_use_cached = self.libraries.get(&key).is_some_and(|l| !l.is_closed());
if should_use_cached {
// new_lib will be dropped, calling FreeLibrary (decrements refcount)
// But library stays loaded because cached version maintains refcount
drop(new_lib);
return Ok((key, self.libraries.get(&key).expect("just checked")));
}
self.libraries.insert(key, new_lib);
Ok((key, self.libraries.get(&key).expect("just inserted")))
}
#[cfg(unix)]
pub fn get_or_insert_lib_with_mode(
&mut self,
library_path: impl AsRef<OsStr>,
mode: i32,
_vm: &VirtualMachine,
) -> Result<(usize, &SharedLibrary), libloading::Error> {
let new_lib = SharedLibrary::new_with_mode(library_path, mode)?;
let key = new_lib.get_pointer();
// Check if library already exists and is not closed
let should_use_cached = self.libraries.get(&key).is_some_and(|l| !l.is_closed());
if should_use_cached {
// new_lib will be dropped, calling dlclose (decrements refcount)
// But library stays loaded because cached version maintains refcount
drop(new_lib);
return Ok((key, self.libraries.get(&key).expect("just checked")));
}
self.libraries.insert(key, new_lib);
Ok((key, self.libraries.get(&key).expect("just inserted")))
}
/// Insert a raw dlopen handle into the cache (for pythonapi / dlopen(NULL))
#[cfg(unix)]
pub fn insert_raw_handle(&mut self, handle: *mut libc::c_void) -> usize {
let shared_lib = SharedLibrary::from_raw_handle(handle);
let key = handle as usize;
self.libraries.insert(key, shared_lib);
key
}
pub fn drop_lib(&mut self, key: usize) {
self.libraries.remove(&key);
}
+119 -28
View File
@@ -1,6 +1,7 @@
use super::base::CDATA_BUFFER_METHODS;
use super::{PyCArray, PyCData, PyCSimple, PyCStructure, StgInfo, StgInfoFlags};
use crate::protocol::PyNumberMethods;
use crate::types::{AsNumber, Constructor, Initializer};
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer};
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef},
@@ -8,6 +9,7 @@ use crate::{
function::{FuncArgs, OptionalArg},
};
use num_traits::ToPrimitive;
use std::borrow::Cow;
#[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")]
#[derive(Debug)]
@@ -42,11 +44,19 @@ impl Initializer for PyCPointerType {
stg_info.length = 1;
stg_info.flags |= StgInfoFlags::TYPEFLAG_ISPOINTER;
// Set format string: "&<element_format>"
if let Some(ref proto) = stg_info.proto {
let item_info = proto.stg_info_opt().expect("proto has StgInfo");
// Set format string: "&<element_format>" or "&(shape)<element_format>" for arrays
if let Some(ref proto) = stg_info.proto
&& let Some(item_info) = proto.stg_info_opt()
{
let current_format = item_info.format.as_deref().unwrap_or("B");
stg_info.format = Some(format!("&{}", current_format));
// Include shape for array types in the pointer format
let shape_str = if !item_info.shape.is_empty() {
let dims: Vec<String> = item_info.shape.iter().map(|d| d.to_string()).collect();
format!("({})", dims.join(","))
} else {
String::new()
};
stg_info.format = Some(format!("&{}{}", shape_str, current_format));
}
let _ = new_type.init_type_data(stg_info);
@@ -69,6 +79,15 @@ impl PyCPointerType {
return Ok(value);
}
// 1.5 CArgObject (from byref()) - check if underlying obj is instance of _type_
if let Some(carg) = value.downcast_ref::<super::_ctypes::CArgObject>()
&& let Ok(type_attr) = cls.as_object().get_attr("_type_", vm)
&& let Ok(type_ref) = type_attr.downcast::<PyType>()
&& carg.obj.is_instance(type_ref.as_object(), vm)?
{
return Ok(value);
}
// 2. If already an instance of the requested type, return it
if value.is_instance(cls.as_object(), vm)? {
return Ok(value);
@@ -149,10 +168,17 @@ impl PyCPointerType {
if let Some(mut stg_info) = zelf.get_type_data_mut::<StgInfo>() {
stg_info.proto = Some(typ_type.clone());
// Update format string: "&<element_format>"
// Update format string: "&<element_format>" or "&(shape)<element_format>" for arrays
let item_info = typ_type.stg_info_opt().expect("proto has StgInfo");
let current_format = item_info.format.as_deref().unwrap_or("B");
stg_info.format = Some(format!("&{}", current_format));
// Include shape for array types in the pointer format
let shape_str = if !item_info.shape.is_empty() {
let dims: Vec<String> = item_info.shape.iter().map(|d| d.to_string()).collect();
format!("({})", dims.join(","))
} else {
String::new()
};
stg_info.format = Some(format!("&{}{}", shape_str, current_format));
}
// 4. Set _type_ attribute on the pointer type
@@ -233,7 +259,10 @@ impl Initializer for PyCPointer {
}
}
#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor, Initializer))]
#[pyclass(
flags(BASETYPE, IMMUTABLETYPE),
with(Constructor, Initializer, AsBuffer)
)]
impl PyCPointer {
/// Get the pointer value stored in buffer as usize
pub fn get_ptr_value(&self) -> usize {
@@ -605,20 +634,47 @@ impl PyCPointer {
unsafe {
let ptr = addr as *const u8;
match type_code {
// Single-byte types don't need read_unaligned
Some("c") => Ok(vm.ctx.new_bytes(vec![*ptr]).into()),
Some("b") => Ok(vm.ctx.new_int(*(ptr as *const i8) as i32).into()),
Some("b") => Ok(vm.ctx.new_int(*ptr as i8 as i32).into()),
Some("B") => Ok(vm.ctx.new_int(*ptr as i32).into()),
Some("h") => Ok(vm.ctx.new_int(*(ptr as *const i16) as i32).into()),
Some("H") => Ok(vm.ctx.new_int(*(ptr as *const u16) as i32).into()),
Some("i") | Some("l") => Ok(vm.ctx.new_int(*(ptr as *const i32)).into()),
Some("I") | Some("L") => Ok(vm.ctx.new_int(*(ptr as *const u32)).into()),
Some("q") => Ok(vm.ctx.new_int(*(ptr as *const i64)).into()),
Some("Q") => Ok(vm.ctx.new_int(*(ptr as *const u64)).into()),
Some("f") => Ok(vm.ctx.new_float(*(ptr as *const f32) as f64).into()),
Some("d") | Some("g") => Ok(vm.ctx.new_float(*(ptr as *const f64)).into()),
Some("P") | Some("z") | Some("Z") => {
Ok(vm.ctx.new_int(*(ptr as *const usize)).into())
}
// Multi-byte types need read_unaligned for safety on strict-alignment architectures
Some("h") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const i16) as i32)
.into()),
Some("H") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const u16) as i32)
.into()),
Some("i") | Some("l") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const i32))
.into()),
Some("I") | Some("L") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const u32))
.into()),
Some("q") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const i64))
.into()),
Some("Q") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const u64))
.into()),
Some("f") => Ok(vm
.ctx
.new_float(std::ptr::read_unaligned(ptr as *const f32) as f64)
.into()),
Some("d") | Some("g") => Ok(vm
.ctx
.new_float(std::ptr::read_unaligned(ptr as *const f64))
.into()),
Some("P") | Some("z") | Some("Z") => Ok(vm
.ctx
.new_int(std::ptr::read_unaligned(ptr as *const usize))
.into()),
_ => {
// Default: read as bytes
let bytes = std::slice::from_raw_parts(ptr, size).to_vec();
@@ -652,27 +708,37 @@ impl PyCPointer {
"bytes/string or integer address expected".to_owned(),
));
};
*(ptr as *mut usize) = ptr_val;
std::ptr::write_unaligned(ptr as *mut usize, ptr_val);
return Ok(());
}
_ => {}
}
// Try to get value as integer
// Use write_unaligned for safety on strict-alignment architectures
if let Ok(int_val) = value.try_int(vm) {
let i = int_val.as_bigint();
match size {
1 => {
*ptr = i.to_u8().unwrap_or(0);
*ptr = i.to_u8().expect("int too large");
}
2 => {
*(ptr as *mut i16) = i.to_i16().unwrap_or(0);
std::ptr::write_unaligned(
ptr as *mut i16,
i.to_i16().expect("int too large"),
);
}
4 => {
*(ptr as *mut i32) = i.to_i32().unwrap_or(0);
std::ptr::write_unaligned(
ptr as *mut i32,
i.to_i32().expect("int too large"),
);
}
8 => {
*(ptr as *mut i64) = i.to_i64().unwrap_or(0);
std::ptr::write_unaligned(
ptr as *mut i64,
i.to_i64().expect("int too large"),
);
}
_ => {
let bytes = i.to_signed_bytes_le();
@@ -688,10 +754,10 @@ impl PyCPointer {
let f = float_val.to_f64();
match size {
4 => {
*(ptr as *mut f32) = f as f32;
std::ptr::write_unaligned(ptr as *mut f32, f as f32);
}
8 => {
*(ptr as *mut f64) = f;
std::ptr::write_unaligned(ptr as *mut f64, f);
}
_ => {}
}
@@ -712,3 +778,28 @@ impl PyCPointer {
}
}
}
impl AsBuffer for PyCPointer {
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
let stg_info = zelf
.class()
.stg_info_opt()
.expect("PyCPointer type must have StgInfo");
let format = stg_info
.format
.clone()
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed("&B"));
let itemsize = stg_info.size;
// Pointer types are scalars with ndim=0, shape=()
let desc = BufferDescriptor {
len: itemsize,
readonly: false,
itemsize,
format,
dim_desc: vec![],
};
let buf = PyBuffer::new(zelf.to_owned().into(), desc, &CDATA_BUFFER_METHODS);
Ok(buf)
}
}
+127 -60
View File
@@ -14,12 +14,47 @@ use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer, Representable};
use crate::{AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine};
use num_traits::ToPrimitive;
use std::borrow::Cow;
use std::fmt::Debug;
/// Valid type codes for ctypes simple types
// spell-checker: disable-next-line
pub(super) const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfuzZqQPXOv?g";
/// Convert ctypes type code to PEP 3118 format code.
/// Some ctypes codes need to be mapped to standard-size codes based on platform.
/// _ctypes_alloc_format_string_for_type
fn ctypes_code_to_pep3118(code: char) -> char {
match code {
// c_int: map based on sizeof(int)
'i' if std::mem::size_of::<std::ffi::c_int>() == 2 => 'h',
'i' if std::mem::size_of::<std::ffi::c_int>() == 4 => 'i',
'i' if std::mem::size_of::<std::ffi::c_int>() == 8 => 'q',
'I' if std::mem::size_of::<std::ffi::c_int>() == 2 => 'H',
'I' if std::mem::size_of::<std::ffi::c_int>() == 4 => 'I',
'I' if std::mem::size_of::<std::ffi::c_int>() == 8 => 'Q',
// c_long: map based on sizeof(long)
'l' if std::mem::size_of::<std::ffi::c_long>() == 4 => 'l',
'l' if std::mem::size_of::<std::ffi::c_long>() == 8 => 'q',
'L' if std::mem::size_of::<std::ffi::c_long>() == 4 => 'L',
'L' if std::mem::size_of::<std::ffi::c_long>() == 8 => 'Q',
// c_bool: map based on sizeof(bool) - typically 1 byte on all platforms
'?' if std::mem::size_of::<bool>() == 1 => '?',
'?' if std::mem::size_of::<bool>() == 2 => 'H',
'?' if std::mem::size_of::<bool>() == 4 => 'L',
'?' if std::mem::size_of::<bool>() == 8 => 'Q',
// Default: use the same code
_ => code,
}
}
/// _ctypes_alloc_format_string_for_type
fn alloc_format_string_for_type(code: char, big_endian: bool) -> String {
let prefix = if big_endian { ">" } else { "<" };
let pep_code = ctypes_code_to_pep3118(code);
format!("{}{}", prefix, pep_code)
}
/// Create a new simple type instance from a class
fn new_simple_type(
cls: Either<&PyObject, &Py<PyType>>,
@@ -165,6 +200,20 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul
}
// O_set: py_object accepts any Python object
"O" => Ok(value.to_owned()),
// X_set: BSTR - same as Z (c_wchar_p), accepts None, int, or str
"X" => {
if value.is(&vm.ctx.none)
|| value.downcast_ref_if_exact::<PyInt>(vm).is_some()
|| value.downcast_ref_if_exact::<PyStr>(vm).is_some()
{
Ok(value.to_owned())
} else {
Err(vm.new_type_error(format!(
"unicode string or integer address expected instead of {} instance",
value.class().name()
)))
}
}
_ => {
// "P"
if value.downcast_ref_if_exact::<PyInt>(vm).is_some()
@@ -313,7 +362,7 @@ impl PyCSimpleType {
.to_pyobject(vm));
}
// 2. Array/Pointer with c_wchar element type
if is_cwchar_array_or_pointer(&value, vm) {
if is_cwchar_array_or_pointer(&value, vm)? {
return Ok(value);
}
// 3. CArgObject (byref(c_wchar(...)))
@@ -521,13 +570,10 @@ impl Initializer for PyCSimpleType {
let mut stg_info = StgInfo::new(size, align);
// Set format for PEP 3118 buffer protocol
// Format is endian prefix + type code (e.g., "<i" for little-endian int)
let endian_prefix = if cfg!(target_endian = "little") {
"<"
} else {
">"
};
stg_info.format = Some(format!("{}{}", endian_prefix, type_str));
stg_info.format = Some(alloc_format_string_for_type(
type_str.chars().next().unwrap_or('?'),
cfg!(target_endian = "big"),
));
stg_info.paramfunc = super::base::ParamFunc::Simple;
// Set TYPEFLAG_ISPOINTER for pointer types: z (c_char_p), Z (c_wchar_p),
@@ -619,13 +665,14 @@ fn create_swapped_types(
swapped_type.set_attr("_swappedbytes_", vm.ctx.none(), vm)?;
// Update swapped type's StgInfo format to use opposite endian prefix
// Native uses '<' on little-endian, '>' on big-endian
// Swapped uses the opposite
if let Ok(swapped_type_ref) = swapped_type.clone().downcast::<PyType>()
&& let Some(mut sw_stg) = swapped_type_ref.get_type_data_mut::<StgInfo>()
{
let swapped_prefix = if is_little_endian { ">" } else { "<" };
sw_stg.format = Some(format!("{}{}", swapped_prefix, type_str));
// Swapped: little-endian system uses big-endian prefix and vice versa
sw_stg.format = Some(alloc_format_string_for_type(
type_str.chars().next().unwrap_or('?'),
is_little_endian,
));
}
// Set attributes based on system byte order
@@ -734,63 +781,56 @@ fn value_to_bytes_endian(
}
"b" => {
// c_byte - signed char (1 byte)
// PyLong_AsLongMask pattern: wrapping for overflow values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i8;
let v = int_val.as_bigint().to_i128().expect("int too large") as i8;
return vec![v as u8];
}
vec![0]
}
"B" => {
// c_ubyte - unsigned char (1 byte)
// PyLong_AsUnsignedLongMask: wrapping for negative values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().map(|n| n as u8).unwrap_or(0);
let v = int_val.as_bigint().to_i128().expect("int too large") as u8;
return vec![v];
}
vec![0]
}
"h" => {
// c_short (2 bytes)
// PyLong_AsLongMask pattern: wrapping for overflow values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i16;
let v = int_val.as_bigint().to_i128().expect("int too large") as i16;
return to_bytes!(v);
}
vec![0; 2]
}
"H" => {
// c_ushort (2 bytes)
// PyLong_AsUnsignedLongMask: wrapping for negative values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().map(|n| n as u16).unwrap_or(0);
let v = int_val.as_bigint().to_i128().expect("int too large") as u16;
return to_bytes!(v);
}
vec![0; 2]
}
"i" => {
// c_int (4 bytes)
// PyLong_AsLongMask pattern: wrapping for overflow values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i32;
let v = int_val.as_bigint().to_i128().expect("int too large") as i32;
return to_bytes!(v);
}
vec![0; 4]
}
"I" => {
// c_uint (4 bytes)
// PyLong_AsUnsignedLongMask: wrapping for negative values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().map(|n| n as u32).unwrap_or(0);
let v = int_val.as_bigint().to_i128().expect("int too large") as u32;
return to_bytes!(v);
}
vec![0; 4]
}
"l" => {
// c_long (platform dependent)
// PyLong_AsLongMask pattern: wrapping for overflow values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().unwrap_or(0) as libc::c_long;
let v = int_val.as_bigint().to_i128().expect("int too large") as libc::c_long;
return to_bytes!(v);
}
const SIZE: usize = std::mem::size_of::<libc::c_long>();
@@ -798,13 +838,8 @@ fn value_to_bytes_endian(
}
"L" => {
// c_ulong (platform dependent)
// PyLong_AsUnsignedLongMask: wrapping for negative values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val
.as_bigint()
.to_i128()
.map(|n| n as libc::c_ulong)
.unwrap_or(0);
let v = int_val.as_bigint().to_i128().expect("int too large") as libc::c_ulong;
return to_bytes!(v);
}
const SIZE: usize = std::mem::size_of::<libc::c_ulong>();
@@ -812,18 +847,16 @@ fn value_to_bytes_endian(
}
"q" => {
// c_longlong (8 bytes)
// PyLong_AsLongMask pattern: wrapping for overflow values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i64;
let v = int_val.as_bigint().to_i128().expect("int too large") as i64;
return to_bytes!(v);
}
vec![0; 8]
}
"Q" => {
// c_ulonglong (8 bytes)
// PyLong_AsUnsignedLongLongMask: wrapping for negative values
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_i128().map(|n| n as u64).unwrap_or(0);
let v = int_val.as_bigint().to_i128().expect("int too large") as u64;
return to_bytes!(v);
}
vec![0; 8]
@@ -899,7 +932,10 @@ fn value_to_bytes_endian(
"P" => {
// c_void_p - pointer type (platform pointer size)
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_usize().unwrap_or(0);
let v = int_val
.as_bigint()
.to_usize()
.expect("int too large for pointer");
return to_bytes!(v);
}
vec![0; std::mem::size_of::<usize>()]
@@ -908,7 +944,10 @@ fn value_to_bytes_endian(
// c_char_p - pointer to char (stores pointer value from int)
// PyBytes case is handled in slot_new/set_value with make_z_buffer()
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_usize().unwrap_or(0);
let v = int_val
.as_bigint()
.to_usize()
.expect("int too large for pointer");
return to_bytes!(v);
}
vec![0; std::mem::size_of::<usize>()]
@@ -917,7 +956,10 @@ fn value_to_bytes_endian(
// c_wchar_p - pointer to wchar_t (stores pointer value from int)
// PyStr case is handled in slot_new/set_value with make_wchar_buffer()
if let Ok(int_val) = value.try_index(vm) {
let v = int_val.as_bigint().to_usize().unwrap_or(0);
let v = int_val
.as_bigint()
.to_usize()
.expect("int too large for pointer");
return to_bytes!(v);
}
vec![0; std::mem::size_of::<usize>()]
@@ -939,7 +981,7 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
if let Some(arr) = value.downcast_ref::<PyCArray>()
&& let Some(info) = arr.class().stg_info_opt()
&& let Some(ref elem_type) = info.element_type
&& let Some(elem_code) = elem_type.class().type_code(vm)
&& let Some(elem_code) = elem_type.type_code(vm)
{
return elem_code == "c";
}
@@ -947,7 +989,7 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
if let Some(ptr) = value.downcast_ref::<PyCPointer>()
&& let Some(info) = ptr.class().stg_info_opt()
&& let Some(ref proto) = info.proto
&& let Some(proto_code) = proto.class().type_code(vm)
&& let Some(proto_code) = proto.type_code(vm)
{
return proto_code == "c";
}
@@ -955,25 +997,25 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
}
/// Check if value is a c_wchar array or pointer(c_wchar)
fn is_cwchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
fn is_cwchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
// Check Array with c_wchar element type
if let Some(arr) = value.downcast_ref::<PyCArray>() {
let info = arr.class().stg_info_opt().expect("array has StgInfo");
let info = arr.class().stg_info(vm)?;
let elem_type = info.element_type.as_ref().expect("array has element_type");
if let Some(elem_code) = elem_type.class().type_code(vm) {
return elem_code == "u";
if let Some(elem_code) = elem_type.type_code(vm) {
return Ok(elem_code == "u");
}
}
// Check Pointer to c_wchar
if let Some(ptr) = value.downcast_ref::<PyCPointer>() {
let info = ptr.class().stg_info_opt().expect("pointer has StgInfo");
let info = ptr.class().stg_info(vm)?;
if let Some(ref proto) = info.proto
&& let Some(proto_code) = proto.class().type_code(vm)
&& let Some(proto_code) = proto.type_code(vm)
{
return proto_code == "u";
return Ok(proto_code == "u");
}
}
false
Ok(false)
}
impl Constructor for PyCSimple {
@@ -1121,15 +1163,27 @@ impl PyCSimple {
return Ok(vm.ctx.none());
}
// Read null-terminated wide string at the address
// Windows: wchar_t = u16 (UTF-16) -> use Wtf8Buf::from_wide for surrogate pairs
// Unix: wchar_t = i32 (UTF-32) -> convert via char::from_u32
unsafe {
let w_ptr = ptr as *const libc::wchar_t;
let len = libc::wcslen(w_ptr);
let wchars = std::slice::from_raw_parts(w_ptr, len);
let s: String = wchars
.iter()
.filter_map(|&c| char::from_u32(c as u32))
.collect();
return Ok(vm.ctx.new_str(s).into());
#[cfg(windows)]
{
use rustpython_common::wtf8::Wtf8Buf;
let wide: Vec<u16> = wchars.to_vec();
let wtf8 = Wtf8Buf::from_wide(&wide);
return Ok(vm.ctx.new_str(wtf8).into());
}
#[cfg(not(windows))]
{
let s: String = wchars
.iter()
.filter_map(|&c| char::from_u32(c as u32))
.collect();
return Ok(vm.ctx.new_str(s).into());
}
}
}
@@ -1349,12 +1403,25 @@ impl PyCSimple {
impl AsBuffer for PyCSimple {
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
let buffer_len = zelf.0.buffer.read().len();
let buf = PyBuffer::new(
zelf.to_owned().into(),
BufferDescriptor::simple(buffer_len, false), // readonly=false for ctypes
&CDATA_BUFFER_METHODS,
);
let stg_info = zelf
.class()
.stg_info_opt()
.expect("PyCSimple type must have StgInfo");
let format = stg_info
.format
.clone()
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed("B"));
let itemsize = stg_info.size;
// Simple types are scalars with ndim=0, shape=()
let desc = BufferDescriptor {
len: itemsize,
readonly: false,
itemsize,
format,
dim_desc: vec![],
};
let buf = PyBuffer::new(zelf.to_owned().into(), desc, &CDATA_BUFFER_METHODS);
Ok(buf)
}
}
-6
View File
@@ -601,12 +601,6 @@ impl PyCStructure {
fn _b0_(&self) -> Option<PyObjectRef> {
self.0.base.read().clone()
}
#[pygetset]
fn _fields_(&self, vm: &VirtualMachine) -> PyObjectRef {
// Return the _fields_ from the class, not instance
vm.ctx.none()
}
}
impl AsBuffer for PyCStructure {
+13 -7
View File
@@ -1,6 +1,8 @@
use crate::{Py, PyResult, VirtualMachine, builtins::PyModule, convert::ToPyObject};
pub(crate) use sys::{__module_def, DOC, MAXSIZE, MULTIARCH, UnraisableHookArgsData};
pub(crate) use sys::{
__module_def, DOC, MAXSIZE, RUST_MULTIARCH, UnraisableHookArgsData, multiarch,
};
#[pymodule]
mod sys {
@@ -37,10 +39,14 @@ mod sys {
System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW},
};
// not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu)
// but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly,
// https://github.com/python/cpython/blob/3.8/configure.ac#L725
pub(crate) const MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
// Rust target triple (e.g., "x86_64-unknown-linux-gnu")
pub(crate) const RUST_MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
/// Convert Rust target triple to CPython-style multiarch
/// e.g., "x86_64-unknown-linux-gnu" -> "x86_64-linux-gnu"
pub(crate) fn multiarch() -> String {
RUST_MULTIARCH.replace("-unknown", "")
}
#[pyattr(name = "_rustpython_debugbuild")]
const RUSTPYTHON_DEBUGBUILD: bool = cfg!(debug_assertions);
@@ -189,7 +195,7 @@ mod sys {
py_namespace!(vm, {
"name" => ctx.new_str(NAME),
"cache_tag" => ctx.new_str(cache_tag),
"_multiarch" => ctx.new_str(MULTIARCH.to_owned()),
"_multiarch" => ctx.new_str(multiarch()),
"version" => version_info(vm),
"hexversion" => ctx.new_int(version::VERSION_HEX),
})
@@ -1249,6 +1255,6 @@ pub(crate) fn sysconfigdata_name() -> String {
"_sysconfigdata_{}_{}_{}",
sys::ABIFLAGS,
sys::PLATFORM,
sys::MULTIARCH
sys::multiarch()
)
}
+7 -4
View File
@@ -4,20 +4,23 @@ pub(crate) use _sysconfigdata::make_module;
#[pymodule]
pub(crate) mod _sysconfigdata {
use crate::{VirtualMachine, builtins::PyDictRef, convert::ToPyObject, stdlib::sys::MULTIARCH};
use crate::stdlib::sys::{RUST_MULTIARCH, multiarch};
use crate::{VirtualMachine, builtins::PyDictRef, convert::ToPyObject};
#[pyattr]
fn build_time_vars(vm: &VirtualMachine) -> PyDictRef {
let vars = vm.ctx.new_dict();
let multiarch = multiarch();
macro_rules! sysvars {
($($key:literal => $value:expr),*$(,)?) => {{
$(vars.set_item($key, $value.to_pyobject(vm), vm).unwrap();)*
}};
}
sysvars! {
// fake shared module extension
"EXT_SUFFIX" => format!(".rustpython-{MULTIARCH}"),
"MULTIARCH" => MULTIARCH,
// Extension module suffix in CPython-compatible format
"EXT_SUFFIX" => format!(".rustpython313-{multiarch}.so"),
"MULTIARCH" => multiarch.clone(),
"RUST_MULTIARCH" => RUST_MULTIARCH,
// enough for tests to stop expecting urandom() to fail after restricting file resources
"HAVE_GETRANDOM" => 1,
// Compiler configuration for native extension builds
+18 -1
View File
@@ -15,12 +15,29 @@ pub const VERSION_HEX: usize =
(MAJOR << 24) | (MINOR << 16) | (MICRO << 8) | (RELEASELEVEL_N << 4) | SERIAL;
pub fn get_version() -> String {
// Windows: include MSC v. for compatibility with ctypes.util.find_library
// MSC v.1929 = VS 2019, version 14+ makes find_msvcrt() return None
#[cfg(windows)]
let msc_info = {
let arch = if cfg!(target_pointer_width = "64") {
"64 bit (AMD64)"
} else {
"32 bit (Intel)"
};
// Include both RustPython identifier and MSC v. for compatibility
format!(" MSC v.1929 {arch}",)
};
#[cfg(not(windows))]
let msc_info = String::new();
format!(
"{:.80} ({:.80}) \n[RustPython {} with {:.80}]", // \n is PyPy convention
"{:.80} ({:.80}) \n[RustPython {} with {:.80}{}]", // \n is PyPy convention
get_version_number(),
get_build_info(),
env!("CARGO_PKG_VERSION"),
COMPILER,
msc_info,
)
}
+4 -1
View File
@@ -361,7 +361,9 @@ def compare():
if platform.python_implementation() == "CPython":
if not_implementeds:
sys.exit("ERROR: CPython should have all the methods")
sys.exit(
f"ERROR: CPython should have all the methods but missing: {not_implementeds}"
)
mod_names = [
name.decode()
@@ -455,6 +457,7 @@ result = subprocess.run(
)
# The last line should be json output, the rest of the lines can contain noise
# because importing certain modules can print stuff to stdout/stderr
print(result.stderr, file=sys.stderr)
result = json.loads(result.stdout.splitlines()[-1])
if args.json: