From cf31798efa413c1435049db0565867698e5398a1 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 21 Jul 2025 00:52:05 +0900 Subject: [PATCH 1/5] Allow empty name if handle is non-null in CDLL --- Lib/ctypes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index d6d07a13f756e2..16c6e7ce1943ff 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -443,7 +443,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, else: import nt mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS - if '/' in name or '\\' in name: + if name is not None and ('/' in name or '\\' in name): self._name = nt._getfullpathname(self._name) mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR From fad677f2678304e369b5329c3660f5a881e06b20 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 21 Jul 2025 01:08:26 +0900 Subject: [PATCH 2/5] Refactor CDLL __init__ function --- Lib/ctypes/__init__.py | 84 ++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 16c6e7ce1943ff..4378bf65436ac0 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -108,7 +108,8 @@ class CFunctionType(_CFuncPtr): return CFunctionType if _os.name == "nt": - from _ctypes import LoadLibrary as _dlopen + # from _ctypes import LoadLibrary as _dlopen + from _ctypes import LoadLibrary as _LoadLibrary from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL _win_functype_cache = {} @@ -137,6 +138,7 @@ class WinFunctionType(_CFuncPtr): WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") elif _os.name == "posix": + # from _ctypes import dlopen as _dlopen from _ctypes import dlopen as _dlopen from _ctypes import sizeof, byref, addressof, alignment, resize @@ -410,52 +412,62 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, 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) + if handle is None: + self._handle = self._load_library(name, mode, winmode) + else: + self._handle = handle + + if _os.name == "nt": + def _load_library(self, name, mode, winmode): + if winmode is not None: + mode = winmode + else: + import nt as _nt + mode = _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) + mode |= _nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR + self._name = name + return _LoadLibrary(self._name, mode) + + else: + def _load_library(self, name, mode, winmode): + 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 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 name is not None and ('/' 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 + self._name = name + return _dlopen(name, mode) def __repr__(self): return "<%s '%s', handle %x at %#x>" % \ From ab5aaa5f96f89522f6fcf12ba25de961b3f147f1 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 21 Jul 2025 01:12:57 +0900 Subject: [PATCH 3/5] Add test --- Lib/test/test_ctypes/test_loading.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_ctypes/test_loading.py b/Lib/test/test_ctypes/test_loading.py index 13ed813ad98c31..3b8332fbb30928 100644 --- a/Lib/test/test_ctypes/test_loading.py +++ b/Lib/test/test_ctypes/test_loading.py @@ -100,6 +100,12 @@ def test_load_ordinal_functions(self): 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): # On winXP 64-bit, advapi32 loads at an address that does From 68062445f7706548b8447379b9f1637ce07ac937 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 21 Jul 2025 01:28:27 +0900 Subject: [PATCH 4/5] Remove dead code --- Lib/ctypes/__init__.py | 2 -- .../next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 4378bf65436ac0..fd402eff3785a9 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -108,7 +108,6 @@ class CFunctionType(_CFuncPtr): 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 @@ -138,7 +137,6 @@ class WinFunctionType(_CFuncPtr): WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") elif _os.name == "posix": - # from _ctypes import dlopen as _dlopen from _ctypes import dlopen as _dlopen from _ctypes import sizeof, byref, addressof, alignment, resize diff --git a/Misc/NEWS.d/next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst b/Misc/NEWS.d/next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst new file mode 100644 index 00000000000000..0c1a16cdb29f43 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst @@ -0,0 +1,2 @@ +Allows creating a :class:`ctypes.CDLL` without name when passing a handle as +an argument. From 06df0965d9c646d0d36d47349f3ef9f36e890789 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 21 Jul 2025 01:32:34 +0900 Subject: [PATCH 5/5] Ensure self._name is set --- Lib/ctypes/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index fd402eff3785a9..b06e19fdf967c4 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -422,13 +422,10 @@ class _FuncPtr(_CFuncPtr): if name: name = _os.fspath(name) - if handle is None: - self._handle = self._load_library(name, mode, winmode) - else: - self._handle = handle + self._handle = self._load_library(name, mode, handle, winmode) if _os.name == "nt": - def _load_library(self, name, mode, winmode): + def _load_library(self, name, mode, handle, winmode): if winmode is not None: mode = winmode else: @@ -443,10 +440,12 @@ def _load_library(self, name, mode, winmode): name = _nt._getfullpathname(name) mode |= _nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR self._name = name + if handle is not None: + return handle return _LoadLibrary(self._name, mode) else: - def _load_library(self, name, mode, winmode): + def _load_library(self, name, mode, handle, winmode): 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy