1

I am trying to upgrade a library to Python 3.10. Up to now, I have been using 3.7.6.

In my library, I am using DLLs. In the package __init__.py, I add the DLL path to the PATH variable. Since I want to have the code compatible with 32 and 64 bit systems, I check the bitness at runtime and add the appropriate DLL folder to the PATH:

import os
import sys

# Add libs to PATH environment variable such that DLLs can be loaded.
# Load either x64 or x86 depending on whether we are running a 32/64
# bit system.
package_directory = os.path.dirname(os.path.abspath(__file__))
if sys.maxsize > 2**32:
    path_dir = os.path.join(package_directory, 'libs', 'x64')
else:
    path_dir = os.path.join(package_directory, 'libs', 'x86')

os.environ['PATH'] += os.pathsep + path_dir

The corrresponding folder structure is

package_root
|- libs
   |- x64
      |- libbristolpolled.dll
      ...
   |- x86
      |- libbristolpolled.dll
      ...

Then in a sub-package, I am using:

from ctypes import CDLL

dll = "libbristolpolled.dll"
_lib_bristlp = CDLL(dll)

This has worked fine in 3.7.6 but fails in 3.10.3 with the following error message:

  File "C:\...\lib\site-packages\optoMD\sensor\optical\bristol\dll_api.py", line 37, in <module>
    _lib_bristlp = CDLL(dll)  # The correct DLL
  File "C:\Program Files\Python310\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
FileNotFoundError: Could not find module 'libbristolpolled.dll' (or one of its dependencies). Try using the full path with constructor syntax.

If I instead use the absolute path, as the error message suggests, it works:

from ctypes import CDLL

dll = r"C:\...\libs\x64\libbristolpolled.dll"
_lib_bristlp = CDLL(dll)

What is the reason the old method doesn't work anymore, or how can I make it work again? I'd prefer not to have absolute paths in my code and I liked the fact that I handle the DLL path once in the init file and afterwards I just need to import the DLL using the file name.

erik
  • 619
  • 1
  • 11
  • 20
  • Most likely, a dupe of https://stackoverflow.com/questions/59330863/cant-import-dll-module-in-python/59333711#59333711. If that's the case, please mark it as such – CristiFati Mar 29 '22 at 22:27
  • It's true. That question is very similar and your solution applies to me too. I don't know how to mark it as a related issue, though. – erik Apr 01 '22 at 12:02

1 Answers1

1

This particular features (by adding the path to find a .dll to PATH environment variable) has been removed in Python 3.8. As per the notice, the new function os.add_dll_directory is provided; quoting the documentation for the reason for the change:

New in version 3.8: Previous versions of CPython would resolve DLLs using the default behavior for the current process. This led to inconsistencies, such as only sometimes searching PATH or the current working directory, and OS functions such as AddDllDirectory having no effect.

The more portable solution going forward is to attempt to use that function, alternatively just generate the absolute path as you have current done using os.path.dirname and os.path.join on the dll variable - i.e. to maintain compatibility with prior Python versions; also this assumes those .dll files do not have external dependencies, if there are, this thread has a significantly more detail regarding the issues at hand (even with absolute paths - though given that it did appear to work for your program and library, this should not an issue for your use case).

metatoaster
  • 17,419
  • 5
  • 55
  • 66
  • Thank you for the great explanation. I used ```os.add_dll_directory``` instead of editing the PATH variable and everything was running fine. Note that this doesn't seem to be backward compatible with Python 3.7. I used a try-except block and my previous solution as a fall back option. – erik Apr 01 '22 at 11:59