Python Leopard DLL Hell


B

Brian Cole

Hello All,

I'm running into a strange problem on Leopard with how Python loads
shared libraries. I'll give you a background of what we are trying to
accomplish before describing the problem. I am not certain whether
this is an OS X problem, or a Python problem, though it appears with
the combination of the two.

We have swig wrapped C++ code. We used to require our users to set
LD_LIBRARY_PATH to the directory containing the dynamic libraries
because the swig generated .so needs to dynamically link in another
..so. The dependency tree looks like the following:

foo.py <--- performs a "from libs import _foo"
libs/ <--- LD_LIBRARY_PATH required to point here
| _foo.so
| bar.so <-- _foo.so depends on me

The dependency of bar.so can be taken care of by using $ORIGIN with
-rpath (http://linuxreviews.org/man/ld.so/). On Mac OS X this is done
by post-processing the shared library with install_name_tool
(http://lapcatsoftware.com/blog/2007/08/11/embedding-frameworks-in-loadable-bundles/).

Another design goal was to allow for the same directory structure to
be NFS mounted in a heterogeneous environment. Then the magic of
Python can choose the proper .so to load based upon introspection of
the machine architecture and the Python version being used. So the
directory structure becomes:

foo.py <--- performs a "from libs import
GetModule; _foo = GetModule('_foo')"
libs/
| __init__.py <--- contains code for GetModule
| arch-specific-directory/
| _foo.so
| bar.so <--
_foo.so depends on me

The GetModule function defined in libs/__init__.py looks like the following:
DLLS = os.path.join(os.path.basename(__file__),
"arch-specific-directory") <-- determined at runtime based on
platform, compiler, and python version

def GetModule(name):
args = imp.find_module(name, [DLLS])
return imp.load_module(name, *args)

This works great on Linux. LD_LIBRARY_PATH can even be set to a
directory that contains a different (incompatible) _foo.so and python
will force the correct .so to be loaded. However, there is a bug on OS
X Leopard (maybe Tiger too, I haven't tested it).

On OS X, when DYLD_LIBRARY_PATH (the OS X equivalent of
LD_LIBRARY_PATH) is set to a directory that contains an identically
named .so file imp.load_module pulls that .so file in. Even though the
..so has been specified by an absolute filename. Running a simple test
with the -v option of python shows the following:


[email protected]~/debug/wrappers/python$ ls -1 $DYLD_LIBRARY_PATH
_foo.so
bar.so
[email protected]~/debug/wrappers/python$ python -vc "import
openeye.oeshape; from time import sleep; sleep(10)"
<python importing modules>
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
# foo.pyc matches foo.py
import foo # precompiled from foo.pyc
import libs # directory libs
# libs/__init__.pyc matches libs/__init__.py
import libs # precompiled from libs/__init__.pyc
dlopen("/Users/coleb/debug/wrappers/python/openeye/libs/osx-10.5-g++4.0-x86+x64-python2.5/_foo.so",
2); <--- This is the correct .so!
import _foo # dynamically loaded from
/Users/coleb/debug/wrappers/python/openeye/libs/osx-10.5-g++4.0-x86+x64-python2.5/_foo.so
<python clean up>

That appears to be working correctly at first glance. The argument to
dlopen is the correct shared library. Unfortunately, either python or
OS X is lying to me here. If I inspect the python process with OS X's
Activity Monitor and look at the "Open Files and Ports" tab, it shows
that the _foo.so shared library is actually the one located inside
$DYLD_LIBRARY_PATH.

So this problem may not be python's, but I place it here as a first
shot (maybe I'm using the imp module incorrectly).

Thanks,
Brian
 
Ad

Advertisements


Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top