1

I'd like to specify a path for ctypes.util.find_library() to search. How can I do this?

I'd like to do it from within Python.

I'm using macOS.

If I wanted to do it from outside Python, I can specify the location using LD_LIBRARY_PATH. However I have read that I cannot modify this environment variable from within Python as it is cached on Python's startup. Modifying the value and then restarting Python seems like a very unusable idea; for example, what would happen if the library was imported part way through execution?

Why would I like to do this? Because I would like to add a MacOS wheel to a Python library that works under Windows. Currently they're packaging the DLLs into the same directory as the Python source files and adding that path to Windows' PATH environment, which ctypes.util.find_library() searches--a technique that I can't seem to replicate under Mac.

I have tried to understand delocate. It seems to state that the Python library doesn't depend on any shared objects. I suspect this is because the dylibs are loaded dynamically using ctypes.util.find_library() rather than being compiled code within Python.

Any suggestions would be gratefully received!

Matthew Walker
  • 2,527
  • 3
  • 24
  • 30

1 Answers1

1

Although there are environment variables (LD_LIBRARY_PATH and DYLD_LIBRARY_PATH) that impact the search path for shared libraries, they are read and fixed by Python when Python starts up. Thus that option was eliminated.

Instead, we took the approach hinted at in the Python documentation:

If wrapping a shared library with ctypes, it may be better to determine the shared library name at development time, and hardcode that into the wrapper module instead of using find_library() to locate the library at runtime.

We hard-coded the names of the libraries that were included for each operating system, plus we included a generic name for operating systems where the libraries were not included:

names = {
    "Windows": "libogg.dll",
    "Darwin": "libogg.0.dylib",
    "external": "ogg"
}

The library loading code checks if it can find the specified library for the current operating system. If it isn't successful, a search is made for the 'external' library.

The library loading code can be found inside the PyOgg project; see Library.load().

Further to this, inspired by the delocate project, we were required to edit the paths found inside certain dylibs. For example, we edited the opusfile binary to point to the ogg binary found in our package:

install_name_tool -change /usr/local/opt/libogg/lib/libogg.0.dylib @loader_path/libogg.0.dylib libopusfile.0.dylib

For the details on this process please see the README file associated with the macOS binaries inside the PyOgg project.

Further discussion of the approach can be found in the issue raised on PyOgg's GitHub page (#32).

Matthew Walker
  • 2,527
  • 3
  • 24
  • 30