Browse Source

added support for mac os x via macholib.dylib.dyld_find()

https://code.grasp-open.com/grasp-internal/grasp-issue-board/issues/296
Fabian Peter Hammerle 4 years ago
parent
commit
52e58073f3
8 changed files with 116 additions and 45 deletions
  1. 3 0
      .gitignore
  2. 4 2
      CHANGELOG.md
  3. 4 42
      dlinfo/__init__.py
  4. 44 0
      dlinfo/_glibc.py
  5. 21 0
      dlinfo/_macosx.py
  6. 4 0
      tests/.pylintrc
  7. 35 0
      tests/dlinfo_macosx_mock_test.py
  8. 1 1
      tests/dlinfo_test.py

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.coverage
+dist/
+tags

+ 4 - 2
CHANGELOG.md

@@ -5,15 +5,17 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
-## [1.1.0] 2019-09-03
+## Unreleased
+### Added
+- Support for Mac OS X
 
+## [1.1.0] 2019-09-03
 ### Added
 - Prepare push to pypi.org
   - Converted readme from markdown to ReSructuredText format
   - Added module metadata (author, maintainer, url)
 
 ## [1.0.0] 2019-09-03
-
 ### Added
 - Class `DLInfo`
   - Property `path` returns absolute path to dynamically loaded library

+ 4 - 42
dlinfo/__init__.py

@@ -1,44 +1,6 @@
-import ctypes
-import ctypes.util
 import sys
 
-# dlfcn.h
-_RTLD_DI_LINKMAP = 2
-
-
-class _LinkMap(ctypes.Structure):
-    # link.h
-    _fields_ = [
-        ('l_addr', ctypes.c_void_p),
-        ('l_name', ctypes.c_char_p),
-        ('l_ld', ctypes.c_void_p),
-        ('l_next', ctypes.c_void_p),
-        ('l_previous', ctypes.c_void_p),
-    ]
-
-
-_LIBDL = ctypes.cdll.LoadLibrary(ctypes.util.find_library('dl'))
-_DLINFO = _LIBDL.dlinfo
-_DLINFO.argtypes = ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p
-_DLINFO.restype = ctypes.c_int
-
-
-class DLInfo:
-
-    """
-    >>> lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
-    >>> dlinfo = DLInfo(lib)
-    >>> dlinfo.path
-    '/lib/x86_64-linux-gnu/libc.so.6'
-    """
-
-    def __init__(self, cdll: ctypes.CDLL):
-        _linkmap = ctypes.c_void_p()
-        # pylint: disable=protected-access
-        if _DLINFO(cdll._handle, _RTLD_DI_LINKMAP, ctypes.byref(_linkmap)) != 0:  # pragma: no cover
-            raise Exception('dlinfo on {} failed'.format(cdll._name))
-        self._linkmap = ctypes.cast(_linkmap, ctypes.POINTER(_LinkMap))
-
-    @property
-    def path(self) -> str:
-        return self._linkmap.contents.l_name.decode(sys.getdefaultencoding())
+if sys.platform == 'darwin':
+    from ._macosx import DLInfo
+else:
+    from ._glibc import DLInfo

+ 44 - 0
dlinfo/_glibc.py

@@ -0,0 +1,44 @@
+import ctypes
+import ctypes.util
+import sys
+
+# dlfcn.h
+_RTLD_DI_LINKMAP = 2
+
+
+class _LinkMap(ctypes.Structure):
+    # link.h
+    _fields_ = [
+        ('l_addr', ctypes.c_void_p),
+        ('l_name', ctypes.c_char_p),
+        ('l_ld', ctypes.c_void_p),
+        ('l_next', ctypes.c_void_p),
+        ('l_previous', ctypes.c_void_p),
+    ]
+
+
+_LIBDL = ctypes.cdll.LoadLibrary(ctypes.util.find_library('dl'))
+_DLINFO = _LIBDL.dlinfo
+_DLINFO.argtypes = ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p
+_DLINFO.restype = ctypes.c_int
+
+
+class DLInfo:
+
+    """
+    >>> lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
+    >>> dlinfo = DLInfo(lib)
+    >>> dlinfo.path
+    '/lib/x86_64-linux-gnu/libc.so.6'
+    """
+
+    def __init__(self, cdll: ctypes.CDLL):
+        _linkmap = ctypes.c_void_p()
+        # pylint: disable=protected-access
+        if _DLINFO(cdll._handle, _RTLD_DI_LINKMAP, ctypes.byref(_linkmap)) != 0:  # pragma: no cover
+            raise Exception('dlinfo on {} failed'.format(cdll._name))
+        self._linkmap = ctypes.cast(_linkmap, ctypes.POINTER(_LinkMap))
+
+    @property
+    def path(self) -> str:
+        return self._linkmap.contents.l_name.decode(sys.getdefaultencoding())

+ 21 - 0
dlinfo/_macosx.py

@@ -0,0 +1,21 @@
+import ctypes
+# pylint: disable=import-error,no-name-in-module
+from ctypes.macholib.dylib import dyld_find
+
+
+class DLInfo:
+
+    """
+    >>> lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
+    >>> dlinfo = DLInfo(lib)
+    >>> dlinfo.path
+    '/usr/lib/libc.dylib'
+    """
+
+    def __init__(self, cdll: ctypes.CDLL):
+        self._cdll = cdll
+
+    @property
+    def path(self) -> str:
+        # pylint: disable=protected-access
+        return dyld_find(self._cdll._name)

+ 4 - 0
tests/.pylintrc

@@ -0,0 +1,4 @@
+[MESSAGES CONTROL]
+
+disable=missing-docstring,
+        redefined-outer-name

+ 35 - 0
tests/dlinfo_macosx_mock_test.py

@@ -0,0 +1,35 @@
+import ctypes
+import ctypes.util
+import os
+import sys
+import unittest.mock
+
+import pytest
+
+
+def dyld_find_mock(name):
+    # https://github.com/python/cpython/blob/master/Lib/ctypes/macholib/dyld.py#L116
+    return os.path.join(os.sep, 'lib', name)
+
+
+@pytest.fixture
+def dlinfo_module_mac():
+    with unittest.mock.patch('sys.platform', 'darwin'):
+        sys.modules['ctypes.macholib.dylib'] = unittest.mock.Mock()
+        sys.modules['ctypes.macholib.dylib'].dyld_find = dyld_find_mock
+        return __import__('dlinfo')
+
+
+@pytest.mark.parametrize('lib_name', [
+    'c',
+    'dl',
+    'python_grasp',
+])
+def test_dlinfo_path(dlinfo_module_mac, lib_name):
+    lib_filename = ctypes.util.find_library(lib_name)
+    if not lib_filename:
+        pytest.xfail('lib{} not found'.format(lib_name))
+    lib = ctypes.cdll.LoadLibrary(lib_filename)
+    dlinfo = dlinfo_module_mac.DLInfo(lib)
+    assert lib_filename == os.path.basename(dlinfo.path)
+    assert os.path.join(os.sep, 'lib') == os.path.dirname(dlinfo.path)

+ 1 - 1
tests/dlinfo_test.py

@@ -9,7 +9,7 @@ from dlinfo import DLInfo
 @pytest.mark.parametrize('lib_name', [
     'c',
     'dl',
-    'grasp_python',
+    'python_grasp',
 ])
 def test_dlinfo_path(lib_name):
     lib_filename = ctypes.util.find_library(lib_name)