From f9e65fd6cfbf64dd88962ebc444efd52dc52dbfc Mon Sep 17 00:00:00 2001 From: Robert Hoelzl Date: Mon, 21 May 2018 15:07:59 +0200 Subject: [PATCH 1/8] On windows ctypes.test.test_loading.test_load was always skipped Due to issue23606 (find msvct does not work any more) test_load did not work any more, as it used MSVCRT for testing DLL loading. Now it uses _ctypes_test library, which is explicitly generated for testing purposes. --- Lib/ctypes/test/test_loading.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index f3b65b9d6e7e2e..fdd0b726612884 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -24,10 +24,16 @@ 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": + import _ctypes_test + test_lib = _ctypes_test.__file__ + else: + self.skipTest('could not find libc') + CDLL(test_lib) + CDLL(os.path.basename(test_lib)) self.assertRaises(OSError, CDLL, self.unknowndll) def test_load_version(self): From 73fd01cf089450b1f120b4a25fd0f6362455f7a5 Mon Sep 17 00:00:00 2001 From: Robert Hoelzl Date: Mon, 21 May 2018 15:58:41 +0200 Subject: [PATCH 2/8] bpo-33591: Fix CDLL to accept PathLike objects CDLL accepts now not only paths of type 'str' but also of type PathLike (like Path()). --- Lib/ctypes/__init__.py | 2 +- Lib/ctypes/test/test_loading.py | 4 ++++ Misc/ACKS | 1 + .../next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 61467739886482..a0994441395974 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -332,7 +332,7 @@ class CDLL(object): def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False): - self._name = name + self._name = _os.fspath(name) if name is not None else None flags = self._func_flags_ if use_errno: flags |= _FUNCFLAG_USE_ERRNO diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index fdd0b726612884..288d0c3f9fe53b 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -34,6 +34,10 @@ def test_load(self): self.skipTest('could not find libc') CDLL(test_lib) CDLL(os.path.basename(test_lib)) + class CTypesTestPathLikeCls: + def __fspath__(self): + return test_lib + CDLL(CTypesTestPathLikeCls()) self.assertRaises(OSError, CDLL, self.unknowndll) def test_load_version(self): diff --git a/Misc/ACKS b/Misc/ACKS index 42f1abc8da199d..7a7a9937072e86 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -665,6 +665,7 @@ David Hobley Tim Hochberg Benjamin Hodgson Joerg-Cyril Hoehle +Robert Hoelzl Gregor Hoffleit Chris Hoffman Stefan Hoffmeister diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst new file mode 100644 index 00000000000000..845dffa68c1264 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst @@ -0,0 +1,2 @@ +os.CDLL (And os.WinDLL, os.OleDLL, ...) accepts now PathLike objects like +pathlib.Path() as library path (up to now only 'str' objects were accepted). \ No newline at end of file From cf03812c78aac59ced40ce4ee7676747d492d33d Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 3 Feb 2023 21:34:07 +0400 Subject: [PATCH 3/8] Address the review --- Doc/library/ctypes.rst | 16 ++++++++++++++++ .../2018-05-21-17-18-00.bpo-33591.Fhg84L.rst | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 4de5c820f2c6ac..6df3d72b7c9883 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1380,6 +1380,10 @@ way is to instantiate one of the following classes: DLLs and determine which one is not found using Windows debugging and tracing tools. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. seealso:: `Microsoft DUMPBIN tool `_ @@ -1398,6 +1402,10 @@ way is to instantiate one of the following classes: .. versionchanged:: 3.3 :exc:`WindowsError` used to be raised. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) @@ -1405,6 +1413,10 @@ way is to instantiate one of the following classes: functions in these libraries use the ``stdcall`` calling convention, and are assumed to return :c:expr:`int` by default. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + The Python :term:`global interpreter lock` is released before calling any function exported by these libraries, and reacquired afterwards. @@ -1418,6 +1430,10 @@ function exported by these libraries, and reacquired afterwards. Thus, this is only useful to call Python C api functions directly. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + All these classes can be instantiated by calling them with at least one argument, the pathname of the shared library. If you have an existing handle to an already loaded shared library, it can be passed as the ``handle`` named diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst index 845dffa68c1264..3a7c6d45297ba4 100644 --- a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst +++ b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst @@ -1,2 +1,3 @@ -os.CDLL (And os.WinDLL, os.OleDLL, ...) accepts now PathLike objects like -pathlib.Path() as library path (up to now only 'str' objects were accepted). \ No newline at end of file +:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, +and :class:`ctypes.PyDLL` now accept :term:`path-like objects +` as their ``name`` argument. Patch by Robert Hoelzl. From bd48fb86fd0def510b88d5a615d58ef5bba69c24 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 3 Feb 2023 22:14:47 +0400 Subject: [PATCH 4/8] Port the NEWS entry to GitHub numbering --- ...1.Fhg84L.rst => 2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2018-05-21-17-18-00.bpo-33591.Fhg84L.rst => 2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2018-05-21-17-18-00.bpo-33591.Fhg84L.rst rename to Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst From cccd41de027b9d793e7f9c38396f3e0936371aa3 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 3 Feb 2023 22:33:27 +0400 Subject: [PATCH 5/8] Fix the failing tests (by not using the raw `name` argument anymore) --- Lib/ctypes/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index ef23567eaaa57f..bd4db2c711c5bd 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -356,7 +356,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, 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: + if self._name and self._name.endswith(")") and ".a(" in self._name: mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW ) if _os.name == "nt": if winmode is not None: @@ -364,7 +364,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 '/' in self._name or '\\' in self._name: self._name = nt._getfullpathname(self._name) mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR From ea1eae55a6132958e8b8a0db05e3a562ccf05eeb Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sat, 4 Feb 2023 09:57:08 +0400 Subject: [PATCH 6/8] Reassign `_os.fspath(name)` back to `name` --- Lib/ctypes/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index bd4db2c711c5bd..a6444e81ee17d3 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -344,7 +344,9 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): - self._name = _os.fspath(name) if name is not None else None + if name: + name = _os.fspath(name) + self._name = name flags = self._func_flags_ if use_errno: flags |= _FUNCFLAG_USE_ERRNO @@ -356,7 +358,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, archive(member) syntax for dlopen(), and the mode is adjusted. Otherwise, name is presented to dlopen() as a file argument. """ - if self._name and self._name.endswith(")") and ".a(" in self._name: + 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: @@ -364,8 +366,8 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, else: import nt mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS - if '/' in self._name or '\\' in self._name: - self._name = nt._getfullpathname(self._name) + if '/' in name or '\\' in name: + name = nt._getfullpathname(name) mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR class _FuncPtr(_CFuncPtr): @@ -374,7 +376,7 @@ class _FuncPtr(_CFuncPtr): self._FuncPtr = _FuncPtr if handle is None: - self._handle = _dlopen(self._name, mode) + self._handle = _dlopen(name, mode) else: self._handle = handle From d3fdfd4adde93e4f25b523c17fa4baf12a8042e5 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sat, 4 Feb 2023 10:00:08 +0400 Subject: [PATCH 7/8] Move `self._name` to the end --- Lib/ctypes/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index a6444e81ee17d3..1be9f3387e2437 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -346,7 +346,6 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, winmode=None): if name: name = _os.fspath(name) - self._name = name flags = self._func_flags_ if use_errno: flags |= _FUNCFLAG_USE_ERRNO @@ -380,6 +379,8 @@ class _FuncPtr(_CFuncPtr): else: self._handle = handle + self._name = name + def __repr__(self): return "<%s '%s', handle %x at %#x>" % \ (self.__class__.__name__, self._name, From 373be1da1cb09d5464f44e3f30a07032ea147404 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sun, 5 Feb 2023 14:39:17 +0400 Subject: [PATCH 8/8] Move `self._name = name` to the top --- Lib/ctypes/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 1be9f3387e2437..95353bab26cc71 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -346,6 +346,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, winmode=None): if name: name = _os.fspath(name) + self._name = name flags = self._func_flags_ if use_errno: flags |= _FUNCFLAG_USE_ERRNO @@ -366,7 +367,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, import nt mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS if '/' in name or '\\' in name: - name = nt._getfullpathname(name) + self._name = nt._getfullpathname(self._name) mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR class _FuncPtr(_CFuncPtr): @@ -375,12 +376,10 @@ class _FuncPtr(_CFuncPtr): self._FuncPtr = _FuncPtr if handle is None: - self._handle = _dlopen(name, mode) + self._handle = _dlopen(self._name, mode) else: self._handle = handle - self._name = name - def __repr__(self): return "<%s '%s', handle %x at %#x>" % \ (self.__class__.__name__, self._name,