Skip to content

Commit

Permalink
Find the linked libpython using dladdr
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf committed Aug 29, 2018
1 parent 6e91cf9 commit e1b5ef8
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 2 deletions.
45 changes: 45 additions & 0 deletions julia/find_libpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,48 @@
SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") or ".so"


class Dl_info(ctypes.Structure):
_fields_ = [
("dli_fname", ctypes.c_char_p),
("dli_fbase", ctypes.c_void_p),
("dli_sname", ctypes.c_char_p),
("dli_saddr", ctypes.c_void_p),
]


def linked_libpython():
"""
Find the linked libpython using dladdr (in *nix).
Calling this in Windows always return `None` at the moment.
Returns
-------
path : str or None
A path to linked libpython. Return `None` if statically linked.
"""
if platform.system() == "Windows":
return None
return _linked_libpython_unix()


def _linked_libpython_unix():
libdl = ctypes.CDLL(ctypes.util.find_library("dl"))
libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)]
libdl.dladdr.restype = ctypes.c_int

dlinfo = Dl_info()
retcode = libdl.dladdr(
ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p),
ctypes.pointer(dlinfo))
if retcode == 0: # means error
return None
path = os.path.realpath(dlinfo.dli_fname.decode())
if path == os.path.realpath(sys.executable):
return None
return path


def library_name(name, suffix=SHLIB_SUFFIX,
is_windows=platform.system() == "Windows"):
"""
Expand Down Expand Up @@ -54,6 +96,9 @@ def libpython_candidates(suffix=SHLIB_SUFFIX):
Candidate path to libpython. The path may not be a fullpath
and may not exist.
"""

yield linked_libpython()

is_windows = platform.system() == "Windows"

# List candidates for libpython basenames
Expand Down
21 changes: 19 additions & 2 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,28 @@
Unit tests which can be done without loading `libjulia`.
"""

from julia.find_libpython import finding_libpython
import platform

import pytest

from julia.find_libpython import finding_libpython, linked_libpython
from julia.core import determine_if_statically_linked

try:
unicode
except NameError:
unicode = str # for Python 3


def test_finding_libpython_yield_type():
paths = list(finding_libpython())
assert set(map(type, paths)) <= {str}
assert set(map(type, paths)) <= {str, unicode}
# In a statically linked Python executable, no paths may be found. So
# let's just check returned type of finding_libpython.


@pytest.mark.xfail(platform.system() == "Windows",
reason="linked_libpython is not implemented for Windows")
def test_linked_libpython():
if determine_if_statically_linked():
assert linked_libpython() is not None

0 comments on commit e1b5ef8

Please sign in to comment.