diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 04ec0270148..7223fc49977 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -470,6 +470,8 @@ def _load_library(self, name, mode, handle, winmode): if name and name.endswith(")") and ".a(" in name: mode |= _os.RTLD_MEMBER | _os.RTLD_NOW self._name = name + if handle is not None: + return handle return _dlopen(name, mode) def __repr__(self): diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 378f12167c6..3b21658433b 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -85,15 +85,10 @@ def find_library(name): wintypes.DWORD, ) - _psapi = ctypes.WinDLL('psapi', use_last_error=True) - _enum_process_modules = _psapi["EnumProcessModules"] - _enum_process_modules.restype = wintypes.BOOL - _enum_process_modules.argtypes = ( - wintypes.HANDLE, - ctypes.POINTER(wintypes.HMODULE), - wintypes.DWORD, - wintypes.LPDWORD, - ) + # gh-145307: We defer loading psapi.dll until _get_module_handles is called. + # Loading additional DLLs at startup for functionality that may never be + # used is wasteful. + _enum_process_modules = None def _get_module_filename(module: wintypes.HMODULE): name = (wintypes.WCHAR * 32767)() # UNICODE_STRING_MAX_CHARS @@ -101,8 +96,19 @@ def _get_module_filename(module: wintypes.HMODULE): return name.value return None - def _get_module_handles(): + global _enum_process_modules + if _enum_process_modules is None: + _psapi = ctypes.WinDLL('psapi', use_last_error=True) + _enum_process_modules = _psapi["EnumProcessModules"] + _enum_process_modules.restype = wintypes.BOOL + _enum_process_modules.argtypes = ( + wintypes.HANDLE, + ctypes.POINTER(wintypes.HMODULE), + wintypes.DWORD, + wintypes.LPDWORD, + ) + process = _get_current_process() space_needed = wintypes.DWORD() n = 1024 diff --git a/Lib/test/test_ctypes/test_byteswap.py b/Lib/test/test_ctypes/test_byteswap.py index ea5951603f9..6a1bae14773 100644 --- a/Lib/test/test_ctypes/test_byteswap.py +++ b/Lib/test/test_ctypes/test_byteswap.py @@ -166,6 +166,48 @@ def test_endian_double(self): self.assertEqual(s.value, math.pi) self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s)) + @unittest.skipUnless(hasattr(ctypes, 'c_float_complex'), "No complex types") + def test_endian_float_complex(self): + c_float_complex = ctypes.c_float_complex + if sys.byteorder == "little": + self.assertIs(c_float_complex.__ctype_le__, c_float_complex) + self.assertIs(c_float_complex.__ctype_be__.__ctype_le__, + c_float_complex) + else: + self.assertIs(c_float_complex.__ctype_be__, c_float_complex) + self.assertIs(c_float_complex.__ctype_le__.__ctype_be__, + c_float_complex) + s = c_float_complex(math.pi+1j) + self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s)) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + s = c_float_complex.__ctype_le__(math.pi+1j) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s)) + + @unittest.skipUnless(hasattr(ctypes, 'c_double_complex'), "No complex types") + def test_endian_double_complex(self): + c_double_complex = ctypes.c_double_complex + if sys.byteorder == "little": + self.assertIs(c_double_complex.__ctype_le__, c_double_complex) + self.assertIs(c_double_complex.__ctype_be__.__ctype_le__, + c_double_complex) + else: + self.assertIs(c_double_complex.__ctype_be__, c_double_complex) + self.assertIs(c_double_complex.__ctype_le__.__ctype_be__, + c_double_complex) + s = c_double_complex(math.pi+1j) + self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s)) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + s = c_double_complex.__ctype_le__(math.pi+1j) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s)) + def test_endian_other(self): self.assertIs(c_byte.__ctype_le__, c_byte) self.assertIs(c_byte.__ctype_be__, c_byte) diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py index 0e7c65127f6..53f077be026 100644 --- a/Lib/test/test_ctypes/test_dllist.py +++ b/Lib/test/test_ctypes/test_dllist.py @@ -26,7 +26,6 @@ ) class ListSharedLibraries(unittest.TestCase): - # TODO: RUSTPYTHON @unittest.skipIf(not APPLE, "TODO: RUSTPYTHON") def test_lists_system(self): dlls = ctypes.util.dllist() @@ -36,8 +35,7 @@ def test_lists_system(self): any(lib in dll for dll in dlls for lib in KNOWN_LIBRARIES), f"loaded={dlls}" ) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_lists_updates(self): dlls = ctypes.util.dllist() diff --git a/Lib/test/test_ctypes/test_loading.py b/Lib/test/test_ctypes/test_loading.py index 3b8332fbb30..343f6a07c0a 100644 --- a/Lib/test/test_ctypes/test_loading.py +++ b/Lib/test/test_ctypes/test_loading.py @@ -106,6 +106,14 @@ def test_load_without_name_and_with_handle(self): lib = ctypes.WinDLL(name=None, handle=handle) self.assertIs(handle, lib._handle) + @unittest.skipIf(os.name == "nt", 'POSIX-specific test') + @unittest.skipIf(libc_name is None, 'could not find libc') + def test_load_without_name_and_with_handle_posix(self): + lib1 = CDLL(libc_name) + handle = lib1._handle + lib2 = CDLL(name=None, handle=handle) + self.assertIs(lib2._handle, handle) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') def test_1703286_A(self): # On winXP 64-bit, advapi32 loads at an address that does diff --git a/Lib/test/test_ctypes/test_python_api.py b/Lib/test/test_ctypes/test_python_api.py index a1ee8a0de1e..28abf2ac031 100644 --- a/Lib/test/test_ctypes/test_python_api.py +++ b/Lib/test/test_ctypes/test_python_api.py @@ -7,8 +7,7 @@ class PythonAPITestCase(unittest.TestCase): - # TODO: RUSTPYTHON - requires pythonapi (Python C API) - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON; - requires pythonapi (Python C API) def test_PyBytes_FromStringAndSize(self): PyBytes_FromStringAndSize = pythonapi.PyBytes_FromStringAndSize @@ -59,8 +58,7 @@ def test_PyObj_FromPtr(self): del pyobj self.assertEqual(sys.getrefcount(s), ref) - # TODO: RUSTPYTHON - requires pythonapi (Python C API) - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON; - requires pythonapi (Python C API) def test_PyOS_snprintf(self): PyOS_snprintf = pythonapi.PyOS_snprintf PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p diff --git a/Lib/test/test_ctypes/test_values.py b/Lib/test/test_ctypes/test_values.py index 18554e193be..27db481894f 100644 --- a/Lib/test/test_ctypes/test_values.py +++ b/Lib/test/test_ctypes/test_values.py @@ -39,8 +39,7 @@ def test_undefined(self): class PythonValuesTestCase(unittest.TestCase): """This test only works when python itself is a dll/shared library""" - # TODO: RUSTPYTHON - requires pythonapi (Python C API) - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON; - requires pythonapi (Python C API) 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 @@ -48,8 +47,7 @@ def test_optimizeflag(self): opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value self.assertEqual(opt, sys.flags.optimize) - # TODO: RUSTPYTHON - requires pythonapi (Python C API) - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON; - requires pythonapi (Python C API) @thread_unsafe('overrides frozen modules') def test_frozentable(self): # Python exports a PyImport_FrozenModules symbol. This is a diff --git a/Lib/test/test_ctypes/test_win32.py b/Lib/test/test_ctypes/test_win32.py index bb2fc0ca222..0e028e3a37c 100644 --- a/Lib/test/test_ctypes/test_win32.py +++ b/Lib/test/test_ctypes/test_win32.py @@ -13,8 +13,7 @@ @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') class FunctionCallTestCase(unittest.TestCase): - # TODO: RUSTPYTHON: SEH not implemented, crashes with STATUS_ACCESS_VIOLATION - @unittest.skip("TODO: RUSTPYTHON") + @unittest.skip("TODO: RUSTPYTHON; SEH not implemented, crashes with STATUS_ACCESS_VIOLATION") @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")