1

I'm on windows, using anaconda with python 3 and setuptools.

I'm having problems with loading a DLL inside my package. I tried following the advice in 'Packaging resources with setuptools/distribute' as well as 'Python copy a DLL to site-packages on Windows' but I seem to be missing something.

So, my package looks like:

setup.py
package
|--- __init__.py
|--- main.py
|--- subpackage
     |--__init__.py
     |--foo.py
     |--bar.DLL

Inside foo.py I do:

import ctypes

my_dll = ctypes.cdll.LoadLibrary('bar.dll')

This works, when I run my script in the console. However, as soon as I make everything into a package and install it (e.g. via setuptools and pip), I seem to be unable to load the dll.

Inside setup.py I set package_data={'':['*.dll', '*.h', '*.lib']}. After installtion, I can see all the files being correctly placed in the install location. Once I try to import my package, I get the error:

File "path\to\subpackage\foo.py", line 3, in <module>
    my_dll = ctypes.cdll.LoadLibrary('bar.dll')
  [...]
OSError: [WinError 126] Das angegebene Modul wurde nicht gefunden
(Could not find the given module)

So, I'm pretty sure, I need to change the loading of my dll file in the first place at runtime but I don't know how.

I'm looking for a solution, that still allows me to run the single file foo.py in editor, but allows me to use the same file inside a package after installation.

Update Edited for clarity.

Update 16.12.2019 I found one more thing:

I tried the below answer from Sergey which is

import os
this_dir = os.path.abspath(os.path.dirname(__file__))

my_dll = ctypes.cdll.LoadLibrary(os.path.join(this_dir, 'bar.dll'))

This works only, if my current workdir is equal to ...\package\subpackage, so I assume that LoadLibrary does not even try to search the given path and only takes the filename instead.

Dschoni
  • 3,714
  • 6
  • 45
  • 80

1 Answers1

1

Your DLL is not in PATH, so cannot be found.

Use importlib.util inside of foo.py to retrieve its location:

import os
import importlib.util

spec = importlib.util.find_spec('subpackage', 'package')

my_dll = ctypes.cdll.LoadLibrary(os.path.join(spec.submodule_search_locations, 'bar.dll'))

I didn't test this code for your particular setup, but you should get an overall idea

More on find_spec is in documentation


Even simpler approach:

import os
this_dir = os.path.abspath(os.path.dirname(__file__))

my_dll = ctypes.cdll.LoadLibrary(os.path.join(this_dir, 'bar.dll'))

Sergey Nudnov
  • 1,327
  • 11
  • 20
  • This leads me to `spec` being `None`, when I run it from inside the package. – Dschoni Dec 13 '19 at 14:59
  • For your second answer: If I run that in a console, `__file__` is not defined. – Dschoni Dec 13 '19 at 15:33
  • @Dschoni, yes `__file__` is not defined when run in a console, so you should use a different method in that case. There are quite a lot of suggestions there at StackOverflow. But the overall idea is to load DLL with the full path specified. – Sergey Nudnov Dec 13 '19 at 17:42
  • @Dschoni, and for the first part of answer - you may need to play with `find_spec()` parameters, because you will use it from inside of a module – Sergey Nudnov Dec 13 '19 at 17:45
  • Actually, I would like to have a solution, that works in console, stand-alone and from inside the installed package... So far I found none. – Dschoni Dec 15 '19 at 11:52
  • @Dschoni, could you please explain your use cases, particularly in console and standalone with more details. – Sergey Nudnov Dec 15 '19 at 14:45
  • I followed your second approach. It works now from inside the console if execute it on a python file, but it still fails as soon as I load it from the package. From inside the package the path is `C:\Users\Me\Miniconda2\envs\my_env\lib\site-packages\package\subpackage\bar.dll` but I still get the same error ('Module not found'). If I c+p the path to explorer, the path is correct – Dschoni Dec 16 '19 at 11:39
  • @Dschoni, can you confirm that your DLL doesn't have any depended DLLs in the same folder? If it does, they should be loaded manually, specifying their full path, before loading this one. Or you could add library folder to the `PATH` environment variable – Sergey Nudnov Dec 17 '19 at 03:04
  • Yes, I can confirm that. I added the folder to my `PATH` environment variable, which solves the problem locally, but makes distribution of my package a lot harder. I'll investigate more with a minimal example. – Dschoni Dec 18 '19 at 10:43
  • Your comments helped me to find my problem and now I solved it, your approach actually does work. I just needed to find the dependencies and load them explictly (with your code) before I load the given dll. I thought, there were no deps, but when double-checking I found some. So, big thanks to your patience and answers. – Dschoni Dec 18 '19 at 11:14