Skip to content

Commit 435ccf4

Browse files
committed
Find the linked libpython using dladdr
1 parent 6e91cf9 commit 435ccf4

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

julia/find_libpython.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,48 @@
1818
SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") or ".so"
1919

2020

21+
def linked_libpython():
22+
"""
23+
Find the linked libpython using dladdr (in *nix).
24+
25+
Calling this in Windows always return `None` at the moment.
26+
27+
Returns
28+
-------
29+
path : str or None
30+
A path to linked libpython. Return `None` if statically linked.
31+
"""
32+
if platform.system() == "Windows":
33+
return None
34+
return _linked_libpython_unix()
35+
36+
37+
class Dl_info(ctypes.Structure):
38+
_fields_ = [
39+
("dli_fname", ctypes.c_char_p),
40+
("dli_fbase", ctypes.c_void_p),
41+
("dli_sname", ctypes.c_char_p),
42+
("dli_saddr", ctypes.c_void_p),
43+
]
44+
45+
46+
def _linked_libpython_unix():
47+
libdl = ctypes.CDLL(ctypes.util.find_library("dl"))
48+
libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)]
49+
libdl.dladdr.restype = ctypes.c_int
50+
51+
dlinfo = Dl_info()
52+
retcode = libdl.dladdr(
53+
ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p),
54+
ctypes.pointer(dlinfo))
55+
if retcode == 0: # means error
56+
return None
57+
path = os.path.realpath(dlinfo.dli_fname.decode())
58+
if path == os.path.realpath(sys.executable):
59+
return None
60+
return path
61+
62+
2163
def library_name(name, suffix=SHLIB_SUFFIX,
2264
is_windows=platform.system() == "Windows"):
2365
"""
@@ -54,6 +96,9 @@ def libpython_candidates(suffix=SHLIB_SUFFIX):
5496
Candidate path to libpython. The path may not be a fullpath
5597
and may not exist.
5698
"""
99+
100+
yield linked_libpython()
101+
57102
is_windows = platform.system() == "Windows"
58103

59104
# List candidates for libpython basenames

test/test_utils.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,28 @@
22
Unit tests which can be done without loading `libjulia`.
33
"""
44

5-
from julia.find_libpython import finding_libpython
5+
import platform
6+
7+
import pytest
8+
9+
from julia.find_libpython import finding_libpython, linked_libpython
10+
from julia.core import determine_if_statically_linked
11+
12+
try:
13+
unicode
14+
except NameError:
15+
unicode = str # for Python 3
616

717

818
def test_finding_libpython_yield_type():
919
paths = list(finding_libpython())
10-
assert set(map(type, paths)) <= {str}
20+
assert set(map(type, paths)) <= {str, unicode}
1121
# In a statically linked Python executable, no paths may be found. So
1222
# let's just check returned type of finding_libpython.
23+
24+
25+
@pytest.mark.xfail(platform.system() == "Windows",
26+
reason="linked_libpython is not implemented for Windows")
27+
def test_linked_libpython():
28+
if determine_if_statically_linked():
29+
assert linked_libpython() is not None

0 commit comments

Comments
 (0)