Hi,
I noticed Python bindings is unable to load with Python 3.8.x on Windows, while
trying
to run check-swig-py.
[[[
C:\usr\src\subversion\trunk-py3> python.exe -c "from svn import core;
print(core.SVN_VERSION)"
Traceback (most recent call last):
File "C:\usr\src\subversion\trunk-py3\Release\python\libsvn\core.py", line
14, in swig_import_helper
return importlib.import_module(mname)
File "C:\usr\apps\python38\lib\importlib\__init__.py", line 127, in
import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 657, in _load_unlocked
File "<frozen importlib._bootstrap>", line 556, in module_from_spec
File "<frozen importlib._bootstrap_external>", line 1101, in create_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
ImportError: DLL load failed while importing _core: The specified module could
not be found.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\usr\src\subversion\trunk-py3\Release\python\svn\core.py", line 26, in
<module>
from libsvn.core import *
File "C:\usr\src\subversion\trunk-py3\Release\python\libsvn\core.py", line 17, in
<module>
_core = swig_import_helper()
File "C:\usr\src\subversion\trunk-py3\Release\python\libsvn\core.py", line
16, in swig_import_helper
return importlib.import_module('_core')
File "C:\usr\apps\python38\lib\importlib\__init__.py", line 127, in
import_module
return _bootstrap._gcd_import(name[level:], package, level)
ModuleNotFoundError: No module named '_core'
]]]
Investigating the issue, I found related changes in What's New In Python 3.8
[1].
According to the document, DLL dependencies for *.pyd file doesn't search the
directories
in PATH environment since Python 3.8 on Windows. We should call
os.add_dll_directory() to
add search paths to resolve the dependencies.
I created patch to resolve the issue using moduleimport option of %module
directive.
After attached patch, *.pyd file is successfully loaded and tests for Python
bindings pass.
[[[
* subversion/bindings/swig/include/svn_global.swg
(SVN_PYTHON_MODULEIMPORT): Add custom code to add search paths to resolve DLL
dependencies when importing libsvn/*.pyd file.
* subversion/bindings/swig/core.i
* subversion/bindings/swig/svn_client.i
* subversion/bindings/swig/svn_delta.i
* subversion/bindings/swig/svn_diff.i
* subversion/bindings/swig/svn_fs.i
* subversion/bindings/swig/svn_ra.i
* subversion/bindings/swig/svn_repos.i
* subversion/bindings/swig/svn_wc.i
Add moduleimport option with SVN_PYTHON_MODULEIMPORT to %module directive for
Python.
]]]
[1] https://docs.python.org/3/whatsnew/3.8.html#bpo-36085-whatsnew
--
Jun Omae <[email protected]> (大前 潤)
* subversion/bindings/swig/include/svn_global.swg
(SVN_PYTHON_MODULEIMPORT): Add custom code to add search paths to resolve DLL
dependencies when importing libsvn/*.pyd file.
* subversion/bindings/swig/core.i
* subversion/bindings/swig/svn_client.i
* subversion/bindings/swig/svn_delta.i
* subversion/bindings/swig/svn_diff.i
* subversion/bindings/swig/svn_fs.i
* subversion/bindings/swig/svn_ra.i
* subversion/bindings/swig/svn_repos.i
* subversion/bindings/swig/svn_wc.i
Add moduleimport option with SVN_PYTHON_MODULEIMPORT to %module directive for
Python.
Index: subversion/bindings/swig/core.i
===================================================================
--- subversion/bindings/swig/core.i (revision 1877480)
+++ subversion/bindings/swig/core.i (working copy)
@@ -23,8 +23,10 @@
* of the more specific module files.
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") core
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) core
#elif defined(SWIGPERL)
%module "SVN::_Core"
#elif defined(SWIGRUBY)
@@ -31,8 +33,6 @@
%module "svn::ext::core"
#endif
-%include svn_global.swg
-
%{
#include <apr.h>
#include <apr_general.h>
Index: subversion/bindings/swig/include/svn_global.swg
===================================================================
--- subversion/bindings/swig/include/svn_global.swg (revision 1877480)
+++ subversion/bindings/swig/include/svn_global.swg (working copy)
@@ -242,3 +242,40 @@
/* Now, include the main Subversion typemap library. */
%include svn_types.swg
%include proxy.swg
+
+
+#ifdef SWIGPYTHON
+/* Since Python 3.8+ on Windows, DLL dependencies when loading *.pyd file
+ * searches only the system paths, the directory containing the *.pyd file and
+ * the directories added with os.add_dll_directory().
+ * See also https://bugs.python.org/issue36085.
+ */
+%define SVN_PYTHON_MODULEIMPORT
+"
+def _dll_paths():
+ import os
+ if hasattr(os, 'add_dll_directory'): # Python 3.8+ on Windows
+ cookies = []
+ for path in os.environ.get('PATH', '').split(';'):
+ if path and os.path.isabs(path):
+ try:
+ cookie = os.add_dll_directory(path)
+ except:
+ continue
+ else:
+ cookies.append(cookie)
+ return cookies
+ else:
+ return ()
+
+_dll_paths = _dll_paths()
+try:
+ from . import $module
+finally:
+ _dll_path = None
+ for _dll_path in _dll_paths:
+ _dll_path.close()
+ del _dll_paths, _dll_path
+"
+%enddef
+#endif
Index: subversion/bindings/swig/svn_client.i
===================================================================
--- subversion/bindings/swig/svn_client.i (revision 1877480)
+++ subversion/bindings/swig/svn_client.i (working copy)
@@ -21,8 +21,10 @@
* svn_client.i: SWIG interface file for svn_client.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") client
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) client
#elif defined(SWIGPERL)
%module "SVN::_Client"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::client"
#endif
-%include svn_global.swg
%import core.i
%import svn_delta.i
%import svn_wc.i
Index: subversion/bindings/swig/svn_delta.i
===================================================================
--- subversion/bindings/swig/svn_delta.i (revision 1877480)
+++ subversion/bindings/swig/svn_delta.i (working copy)
@@ -21,8 +21,10 @@
* svn_delta.i: SWIG interface file for svn_delta.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") delta
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) delta
#elif defined(SWIGPERL)
%module "SVN::_Delta"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::delta"
#endif
-%include svn_global.swg
%import core.i
#ifdef SWIGRUBY
Index: subversion/bindings/swig/svn_diff.i
===================================================================
--- subversion/bindings/swig/svn_diff.i (revision 1877480)
+++ subversion/bindings/swig/svn_diff.i (working copy)
@@ -21,8 +21,10 @@
* svn_diff.i: SWIG interface file for svn_diff.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") diff
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) diff
#elif defined(SWIGPERL)
%module "SVN::_Diff"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::diff"
#endif
-%include svn_global.swg
%import core.i
/* -----------------------------------------------------------------------
Index: subversion/bindings/swig/svn_fs.i
===================================================================
--- subversion/bindings/swig/svn_fs.i (revision 1877480)
+++ subversion/bindings/swig/svn_fs.i (working copy)
@@ -21,8 +21,10 @@
* svn_fs.i: SWIG interface file for svn_fs.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") fs
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) fs
#elif defined(SWIGPERL)
%module "SVN::_Fs"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::fs"
#endif
-%include svn_global.swg
%import core.i
%import svn_delta.i
Index: subversion/bindings/swig/svn_ra.i
===================================================================
--- subversion/bindings/swig/svn_ra.i (revision 1877480)
+++ subversion/bindings/swig/svn_ra.i (working copy)
@@ -21,8 +21,10 @@
* svn_ra.i: SWIG interface file for svn_ra.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") ra
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) ra
#elif defined(SWIGPERL)
%module "SVN::_Ra"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::ra"
#endif
-%include svn_global.swg
%import core.i
%import svn_delta.i
Index: subversion/bindings/swig/svn_repos.i
===================================================================
--- subversion/bindings/swig/svn_repos.i (revision 1877480)
+++ subversion/bindings/swig/svn_repos.i (working copy)
@@ -21,8 +21,10 @@
* svn_repos.i: SWIG interface file for svn_repos.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") repos
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) repos
#elif defined(SWIGPERL)
%module "SVN::_Repos"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::repos"
#endif
-%include svn_global.swg
%import core.i
%import svn_delta.i
%import svn_fs.i
Index: subversion/bindings/swig/svn_wc.i
===================================================================
--- subversion/bindings/swig/svn_wc.i (revision 1877480)
+++ subversion/bindings/swig/svn_wc.i (working copy)
@@ -21,8 +21,10 @@
* svn_wc.i: SWIG interface file for svn_wc.h
*/
+%include svn_global.swg
+
#if defined(SWIGPYTHON)
-%module(package="libsvn") wc
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) wc
#elif defined(SWIGPERL)
%module "SVN::_Wc"
#elif defined(SWIGRUBY)
@@ -29,7 +31,6 @@
%module "svn::ext::wc"
#endif
-%include svn_global.swg
%import core.i
%import svn_delta.i
%import svn_ra.i