Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >包含和分发带有Python C扩展的第三方库

包含和分发带有Python C扩展的第三方库
EN

Stack Overflow用户
提问于 2020-09-08 20:42:52
回答 1查看 1.9K关注 0票数 6

我正在构建一个使用“第三方”库的C Python扩展--在本例中,我使用单独的构建过程和工具链构建了一个库。调用这个库libplumbus.dylib

目录结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grumbo/
  include/
    plumbus.h
  lib/
    libplumbus.so
  grumbo.c
  setup.py

我的setup.py看起来近似于:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from setuptools import Extension, setup

native_module = Extension(
    'grumbo',
    define_macros = [('MAJOR_VERSION', '1'),
                     ('MINOR_VERSION', '0')],
    sources       = ['grumbo.c'],
    include_dirs  = ['include'],
    libraries     = ['plumbus'],
    library_dirs  = ['lib'])


setup(
    name = 'grumbo',
    version = '1.0',
    ext_modules = [native_module] )

由于lib铅是一个外部库,所以当我运行import grumbo时,我得到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: dlopen(/path/to/grumbo/grumbo.cpython-37m-darwin.so, 2): Library not loaded: lib/libplumbus.dylib
  Referenced from: /path/to/grumbo/grumbo.cpython-37m-darwin.so
  Reason: image not found

libplumbus ----什么是最简单的方法来设置这些东西,以便包含在发行版中,并在导入grumbo时正确加载?(请注意,这应该适用于虚拟服务器)。

我尝试过将lib/libplumbus.dylib添加到package_data中,但这不起作用,即使我将-Wl,-rpath,@loader_path/grumbo/lib添加到扩展的extra_link_args中。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-10 13:17:08

这篇文章的目标是创建一个setup.py,它将创建一个源发行版。这意味着在跑步之后

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python setup.py sdist

生成的dist/grumbo-1.0.tar.gz可通过以下方式用于安装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pip install grumbo-1.0.tar.gz

我们将开始为Linux/MacOS创建一个setup.py,但随后对其进行调整,使其在Windows上也能正常工作。

第一步是将附加数据(包括/库)输入到发行版中。我不确定是否真的不可能为一个模块添加数据,但是setuptools提供了为包添加数据的功能,所以让我们从您的模块中创建一个包(这可能是个好主意)。

grumbo的新结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
src/
  grumbo/
     __init__.py  # empty
     grumbo.c
     include/
       plumbus.h
     lib/
       libplumbus.so
setup.py

改变了setup.py

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from setuptools import setup, Extension, find_packages

native_module = Extension(
                name='grumbo.grumbo',
                sources = ["src/grumbo/grumbo.c"],
              )
kwargs = {
      'name' : 'grumbo',
      'version' : '1.0',
      'ext_modules' :  [native_module],
      'packages':find_packages(where='src'),
      'package_dir':{"": "src"},
}

setup(**kwargs)

它还不起什么作用,但至少我们的包可以通过setuptools找到。生成失败,因为缺少包含。

现在,让我们通过include-folder将所需的包含添加到发行版中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
kwargs = {
      ...,
      'package_data' : { 'grumbo': ['include/*.h']},
}
...

这样,包含-文件就会被复制到源发行版中。但是,由于它将构建在我们还不知道的“某个地方”,因此将include_dirs = ['include']添加到Extension定义中并不能减少它。

找到正确的包含路径必须有更好的方法(并且不那么脆弱),但这正是我想出的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
import os
import sys
import sysconfig
def path_to_build_folder():
    """Returns the name of a distutils build directory"""
    f = "{dirname}.{platform}-{version[0]}.{version[1]}"
    dir_name = f.format(dirname='lib',
                    platform=sysconfig.get_platform(),
                    version=sys.version_info)
    return os.path.join('build', dir_name, 'grumbo')

native_module = Extension(
                ...,
                include_dirs  = [os.path.join(path_to_build_folder(),'include')],
)
...

现在,扩展已经构建,但还不能加载,因为它没有与共享对象libplumbus.so链接,因此一些符号没有得到解决。

与头文件类似,我们可以将库添加到发行版中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
kwargs = {
          ...,
          'package_data' : { 'grumbo': ['include/*.h', 'lib/*.so']},
}
...

并为链接器添加右lib路径:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
native_module = Extension(
                ...
                libraries     = ['plumbus'],
                library_dirs  = [os.path.join(path_to_build_folder(), 'lib')],
              )
...

现在,我们就快到了:

扩展是构建到site-packages/grumbo/

  • the扩展中的,它依赖于ldd

  • libplumbus.solibplumbus.so,可以看到,在ldd

  • libplumbus.so的帮助下,它被放入了site-packages/grumbo/lib

但是,我们仍然不能导入扩展,因为import grumbo.grumbo导致

ImportError: libplumbus.so:无法打开共享对象文件:没有这样的文件或目录

因为加载程序找不到所需的共享对象,它驻留在相对于我们的扩展的文件夹.\lib中。我们可以使用rpath“帮助”加载程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
native_module = Extension(
                ...
                extra_link_args = ["-Wl,-rpath=$ORIGIN/lib/."],
              )
...

现在我们结束了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> import grumbo.grumbo
# works!

此外,建造和安装一个轮子应该可以:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python setup.py bdist_wheel

然后:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pip install grumbo-1.0-xxxx.whl

第一英里的石头实现了。现在我们对它进行了扩展,因此它也可以运行其他平台。

用于Linux和Macos的相同的源代码发行版:

为了能够在Linux和MacOS上安装相同的源代码发行版,必须提供两个版本的共享库(用于Linux和MacOS)。一个选项是在共享对象的名称中添加一个后缀:例如,有libplumbus.linux.solibplumbis.macos.so。根据平台的不同,可以在setup.py中选择正确的共享对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
import platform
def pick_library():
    my_system = platform.system()
    if my_system == 'Linux':
        return "plumbus.linux"
    if my_system == 'Darwin':
        return "plumbus.macos"
    if my_system == 'Windows':
        return "plumbus"
    raise ValueError("Unknown platform: " + my_system)

native_module = Extension(
                ...
                libraries     = [pick_library()],
                ...
              )

对Windows的调整:

在Windows上,动态库是dlls而不是共享对象,因此需要考虑一些差异:

当生成C-扩展时,它需要lib-subfolder.

  • when -file,我们需要在运行时加载C-扩展,它需要plumbus.dll-file.

  • Windows没有rpath的概念,因此我们需要将dll放在扩展旁边,这样就可以找到它(请参阅此SO-post以获得更多详细信息)。

这意味着文件夹结构应该如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
src/
  grumbo/
     __init__.py
     grumbo.c
     plumbus.dll           # needed for Windows
     include/
       plumbus.h
     lib/
       libplumbus.linux.so # needed on Linux
       libplumbus.macos.so # needed on Macos
       plumbus.lib         # needed on Windows
setup.py

setup.py中也有一些变化。首先,扩展package_data以便获取dlllib

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
kwargs = {
      ...
      'package_data' : { 'grumbo': ['include/*.h', 'lib/*.so',
                                    'lib/*.lib', '*.dll',      # for windows
                                   ]},
}
...

其次,rpath只能在Linux/MacOS上使用,因此:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def get_extra_link_args():
    if platform.system() == 'Windows':
        return []
    else:
        return ["-Wl,-rpath=$ORIGIN/lib/."]
    

native_module = Extension(
                ...
                extra_link_args = get_extra_link_args(),
              )

就这样!

完整的安装文件(您可能想要添加宏定义或类似的内容,我已经跳过了):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from setuptools import setup, Extension, find_packages

import os
import sys
import sysconfig
def path_to_build_folder():
    """Returns the name of a distutils build directory"""
    f = "{dirname}.{platform}-{version[0]}.{version[1]}"
    dir_name = f.format(dirname='lib',
                    platform=sysconfig.get_platform(),
                    version=sys.version_info)
    return os.path.join('build', dir_name, 'grumbo')


import platform
def pick_library():
    my_system = platform.system()
    if my_system == 'Linux':
        return "plumbus.linux"
    if my_system == 'Darwin':
        return "plumbus.macos"
    if my_system == 'Windows':
        return "plumbus"
    raise ValueError("Unknown platform: " + my_system)


def get_extra_link_args():
    if platform.system() == 'Windows':
        return []
    else:
        return ["-Wl,-rpath=$ORIGIN/lib/."]
    

native_module = Extension(
                name='grumbo.grumbo',
                sources = ["src/grumbo/grumbo.c"],
                include_dirs  = [os.path.join(path_to_build_folder(),'include')],
                libraries     = [pick_library()],
                library_dirs  = [os.path.join(path_to_build_folder(), 'lib')],
                extra_link_args = get_extra_link_args(),
              )
kwargs = {
      'name' : 'grumbo',
      'version' : '1.0',
      'ext_modules' :  [native_module],
      'packages':find_packages(where='src'),
      'package_dir':{"": "src"},
      'package_data' : { 'grumbo': ['include/*.h', 'lib/*.so',
                                    'lib/*.lib', '*.dll',      # for windows
                                   ]},
}

setup(**kwargs)
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63804883

复制
相关文章
从mybatis sql模板中获取参数信息
最近在尝试从mybatis sql模板中获取参数信息,期间学习了mybatis内部的一些结构,接下来笔者就向大家分享mybatis相关知识和具体代码实现。
zhangheng
2021/01/12
7.8K0
从mybatis sql模板中获取参数信息
【工具类】jwt 从request头信息中获取jwt信息
XcOauth2Util 类 package com.xuecheng.framework.utils; import lombok.Data; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * Created by mrt on 2018/5/25. */ public class XcOauth2Util {
周杰伦本人
2022/10/25
1.3K0
Springsecurity从当前请求对象中获取用户信息
SpringMVC中Controller的请求参数都是当前请求HttpServletRequest带来的,Authentication Principal也是HttpServletRequest带来的,在Controller层我们拿到的是HttpServletRequest的实例是Servlet3SecurityContextHolderAwareRequestWrapper
周杰伦本人
2022/10/25
2.9K0
Spring Security - 02 从 SecurityContextHolder 中获取用户信息
新建 HelloController 控制器类,我们可以通过 SecurityContextHolder 获取用户信息(第 17 ~ 19 行):
用户6619361
2022/04/20
2K0
定时从列表中爬今日通知信息,打包成windows服务
每天8点爬取今日发布的新闻和通知公告,将爬取后的信息保存到Excel文件中,将程序发布成windows服务,开机即可自动启动。
happlyfox
2018/10/31
6410
简单的语音分类任务入门(需要些深度学习基础)
上次公众号刚刚讲过使用 python 播放音频与录音的方法,接下来我将介绍一下简单的语音分类处理流程。简单主要是指,第一:数据量比较小,主要是考虑到数据量大,花费的时间太长。作为演示,我只选取了六个单词作为分类目标,大约 350M 的音频。实际上,整个数据集包含 30 个单词的分类目标,大约 2GB 的音频。第二 :使用的神经网络比较简单,主要是因为分类目标只有 6 个。如果读者有兴趣的话,可以使用更加复杂的神经网络,这样就可以处理更加复杂的分类任务。第三:为了计算机能够更快地处理数据,我并没有选择直接把原始数据‘’喂“给神经网络,而是借助于提取 mfcc 系数的方法,只保留音频的关键信息,减小了运算量,却没有牺牲太大的准确性。
用户2870857
2019/12/22
5K1
听音识情绪 | 程序员手把手教你搭建神经网络,更快get女朋友情绪,求生欲max!⛵
《礼记·乐记》中说:“凡音之起,由人心生也。人心之动,物使之然也。感于物而动,故形于声。声相应,故生变。”
ShowMeAI
2022/08/09
6900
听音识情绪 | 程序员手把手教你搭建神经网络,更快get女朋友情绪,求生欲max!⛵
浅谈MFCC
MFCC(Mel-frequency cepstral coefficients):梅尔频率倒谱系数。梅尔频率是基于人耳听觉特性提出来的, 它与Hz频率成非线性对应关系。梅尔频率倒谱系数(MFCC)则是利用它们之间的这种关系,计算得到的Hz频谱特征。主要用于语音数据特征提取和降低运算维度。例如:对于一帧有512维(采样点)数据,经过MFCC后可以提取出最重要的40维(一般而言)数据同时也达到了将维的目的。
全栈程序员站长
2022/07/21
1.7K0
浅谈MFCC
从SpringMVC获取用户信息谈起
上周末拜读了一位牛人的公众号文章<[Token认证,如何快速方便获取用户信息](https://mp.weixin.qq.com/s/Qi82d5xmlYwiuaGRSn54uw)>,语言风趣,引人入胜,为了表示涛涛敬仰之情,已经转载到自己的公众号了。
A稻田守望者
2019/09/30
1.5K0
从SpringMVC获取用户信息谈起
Flink中Watermark定时生成源码分析
watermark的生成策略有两种:一种是周期性生成,另外一种是根据特定标记生成。在实际使用中大多数情况下会选择周期性生成方式也就是AssignerWithPeriodicWatermarks方式,使用方式如下:
Flink实战剖析
2022/04/18
6590
Flink中Watermark定时生成源码分析
用python获取天气数据,并作定时播报
思路 1.调用和风天气的API,获取天气数据 2.用百度语音API,将天气数据合成语音 3.用树莓派每天早上定时播报天气(定时任务crontab + Python脚本 + mpg123播放器) Pyt
机器学习AI算法工程
2018/03/14
2.6K0
用python获取天气数据,并作定时播报
如何使用AndroidQF快速从Android设备中获取安全取证信息
AndroidQF,全称为Android快速取证(Android Quick Forensics)工具,这是一款便携式工具,可以帮助广大研究人员快速从目标Android设备中获取相关的信息安全取证数据。该工具基于Snoopdroid项目实现其功能,利用的是官方ADB源码,并且使用了Go语言进行重构。
FB客服
2021/12/27
7.1K0
如何使用AndroidQF快速从Android设备中获取安全取证信息
安卓(Android)生成证书和信息获取
这套方法生成证书没有什么问题,但是在获取证书信息的时候,jdk1.8版本没有什么问题,但是jdk11和jdk17均无法输出MD5秘钥。为此找了一个比较好用的工具来进行识别。(该工具也可以进行生成各种秘钥,非常强大)
华创信息技术
2022/05/28
7810
从Go的二进制文件中获取其依赖的模块信息
我们用 Go 构建的二进制文件中默认包含了很多有用的信息。例如,可以获取构建用的 Go 版本:
Jintao Zhang
2021/02/26
2.6K0
人工智能下的音频还能这样玩!!!!
Librosa是一个用于音频、音乐分析、处理的python工具包,一些常见的时频处理、特征提取、绘制声音图形等功能应有尽有,功能十分强大。本文主要介绍librosa的安装与使用方法。
Python研究者
2021/08/21
1.5K0
Python MFCC算法
MFCC(梅尔倒谱系数)的算法思路 读取波形文件 汉明窗 分帧 傅里叶变换 回归离散数据 取得特征数据 Python示例代码 import numpy, numpy.fft def mel(f): return 2595. * numpy.log10(1. + f / 700.) def melinv(m): return 700. * (numpy.power(10., m / 2595.) - 1.) class MFCC(objec
Pulsar-V
2018/04/18
1.6K0
从Python调用堆栈获取行号等信息
程序中的日志打印,或者消息上传,比如kafka消息等等。经常上传的消息中需要上传堆栈信息中的文件名、行号、上层调用者等具体用于定位的消息。Python提供了以下两种方法:
职场亮哥
2020/10/10
2.6K0
python获取linux中top信息
import os,time,sys import paramiko,pexpect
py3study
2020/01/10
3.7K0
Flutter中的获取设备信息以及获取地理位置
在使用之前,我们一定要好好阅读文档,关于Android以及iOS平台的相关配置,我在这里不做过多介绍,大家自己去看文档。
拉维
2019/09/10
12.3K0
点击加载更多

相似问题

用Librosa计算MFCC

11

为什么librosa librosa.feature.mfcc()会吐出一个2D数组?

23

从MySQL获取特定时间添加的信息

34

从频谱图时间/频率序列数组中获取MFCC

140

Librosa未能绘制生成的mfcc

16
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文