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 <jun6...@gmail.com> (大前 潤)
* 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