mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
84 files changed
+3559
-1736
No files matched your search
Vendored
+51
-46
@@ -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)
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -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":
|
||||
|
||||
Vendored
-16
@@ -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)
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
from ctypes.test import load_tests
|
||||
import unittest
|
||||
|
||||
unittest.main()
|
||||
Vendored
-21
@@ -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
@@ -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()
|
||||
Vendored
+15
-3
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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'
|
||||
|
||||
Vendored
-10
@@ -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()
|
||||
Vendored
+10
@@ -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)
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
from test.test_ctypes import load_tests
|
||||
import unittest
|
||||
|
||||
unittest.main()
|
||||
Vendored
+24
@@ -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
@@ -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()
|
||||
+8
-4
@@ -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()
|
||||
+37
-23
@@ -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()
|
||||
+11
-14
@@ -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
@@ -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))
|
||||
+45
-45
@@ -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()
|
||||
+10
-9
@@ -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()
|
||||
Vendored
+26
@@ -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()
|
||||
Vendored
+180
@@ -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()
|
||||
+6
-3
@@ -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()
|
||||
+100
-30
@@ -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()
|
||||
+19
-11
@@ -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()
|
||||
+15
-14
@@ -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()
|
||||
+11
-8
@@ -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()
|
||||
+89
-51
@@ -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()
|
||||
+43
-13
@@ -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()
|
||||
+23
-23
@@ -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()
|
||||
+20
-14
@@ -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'"
|
||||
+50
-23
@@ -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()
|
||||
+4
-3
@@ -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
@@ -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()
|
||||
+19
-15
@@ -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):
|
||||
+35
-6
@@ -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()
|
||||
+195
-51
@@ -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()
|
||||
Vendored
+7
-1
@@ -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__)
|
||||
Vendored
+35
@@ -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()
|
||||
+3
-1
@@ -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
@@ -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()
|
||||
Vendored
+1
@@ -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')
|
||||
|
||||
Vendored
-3
@@ -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)
|
||||
|
||||
Vendored
-1
@@ -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')
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user