瀏覽代碼

Merge branch 'prototype' into 'master'

Frist Prototype

See merge request grasp-tools/python-dlinfo!1
Moritz Wanzenböck 5 年之前
父節點
當前提交
d9f1398aeb
共有 10 個文件被更改,包括 177 次插入2 次删除
  1. 4 0
      .coveragerc
  2. 46 0
      .gitlab-ci.yml
  3. 4 0
      .pylintrc
  4. 12 0
      CHANGELOG.md
  5. 1 0
      Pipfile
  6. 7 2
      Pipfile.lock
  7. 21 0
      README.md
  8. 44 0
      dlinfo/__init__.py
  9. 16 0
      setup.py
  10. 22 0
      tests/dlinfo_test.py

+ 4 - 0
.coveragerc

@@ -0,0 +1,4 @@
+# https://pytest-cov.readthedocs.io/en/stable/config.html
+
+[report]
+fail_under = 100

+ 46 - 0
.gitlab-ci.yml

@@ -0,0 +1,46 @@
+image: docker-registry-default.apps.openshift.grasp-cloud.com/grasp-cloud/ubuntu-base:18.04
+
+stages:
+- test
+- pack
+- deploy
+
+variables:
+  LC_ALL: C.UTF-8
+  LANG: C.UTF-8
+
+before_script:
+- apt-get update
+- apt-get install --yes
+  python3
+- pip3 install pipenv
+- pipenv sync --dev
+
+linting:
+  stage: test
+  script:
+  - pipenv run pylint dlinfo tests/*.py
+
+tests:
+  stage: test
+  script:
+  - pipenv run pytest --doctest-modules --cov dlinfo --cov-report term-missing
+
+pack:
+  stage: pack
+  script:
+  - python3 setup.py sdist
+  - apt-get install --yes python3-wheel
+  - python3 setup.py bdist_wheel
+  artifacts:
+    paths:
+    - dist/
+
+python-push:
+  stage: deploy
+  only: [tags]
+  script:
+  - apt-get install --yes twine
+  - twine upload dist/*
+
+# https://docs.gitlab.com/ee/ci/yaml/

+ 4 - 0
.pylintrc

@@ -0,0 +1,4 @@
+[MESSAGES CONTROL]
+
+disable=missing-docstring,
+        too-few-public-methods

+ 12 - 0
CHANGELOG.md

@@ -0,0 +1,12 @@
+# Changelog
+
+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.0.0] 2019-09-03
+
+### Added
+- Class `DLInfo`
+  - Property `path` returns absolute path to dynamically loaded library

+ 1 - 0
Pipfile

@@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
 verify_ssl = true
 
 [packages]
+dlinfo = {editable = true,path = "."}
 
 [dev-packages]
 pytest = "*"

+ 7 - 2
Pipfile.lock

@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "de06ab2209a13d7ab0d2bb3154d85646ae8637cfabc691c155fd70d26a1a7c7b"
+            "sha256": "a5087605919651f195acc814f62bdae9b244667de324914ade71c248dc1047a1"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -15,7 +15,12 @@
             }
         ]
     },
-    "default": {},
+    "default": {
+        "dlinfo": {
+            "editable": true,
+            "path": "."
+        }
+    },
     "develop": {
         "astroid": {
             "hashes": [

+ 21 - 0
README.md

@@ -0,0 +1,21 @@
+# python-dlinfo
+
+Python wrapper for libc's dlinfo
+
+# install
+
+```sh
+pip install dlinfo
+# or
+pipenv install dlinfo
+```
+
+# usage
+
+```python
+>>> from dlinfo import DLInfo
+>>> lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
+>>> dlinfo = DLInfo(lib)
+>>> dlinfo.path
+'/lib/x86_64-linux-gnu/libc.so.6'
+```

+ 44 - 0
dlinfo/__init__.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())

+ 16 - 0
setup.py

@@ -0,0 +1,16 @@
+import setuptools
+
+setuptools.setup(
+    name='dlinfo',
+    use_scm_version=True,
+    packages=setuptools.find_packages(),
+    setup_requires=[
+        'setuptools_scm',
+    ],
+    tests_require=[
+        'pytest',
+        'pytest-cov',
+        # https://github.com/PyCQA/pylint/issues/2694
+        'pylint>=2.3.0',
+    ],
+)

+ 22 - 0
tests/dlinfo_test.py

@@ -0,0 +1,22 @@
+import ctypes
+import os
+
+import pytest
+
+from dlinfo import DLInfo
+
+
+@pytest.mark.parametrize('lib_name', [
+    'c',
+    'dl',
+    'grasp_python',
+])
+def test_dlinfo_path(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(lib)
+    assert os.path.exists(dlinfo.path)
+    assert os.path.isabs(dlinfo.path)
+    assert lib_filename == os.path.basename(dlinfo.path)