我正在组装一个使用 Cygwin 可执行文件的 Python 包,但无需安装 Cygwin。这意味着我的包中有一个可执行.exe文件 () 和一个库文件 ( .dll)。我这样做是为了让只使用 Windows 和 Python 的人可以在 Windows 上使用该工具。我是 Python 包的新手,因此非常感谢您的帮助。
.exe
.dll
如何使我的包指向包内的可执行文件?例如,这将被用来代替 引用的可执行文件PATH。答案可能就在那里,但我在搜索中找到的只是一堆关于如何从 Python 脚本创建可执行文件的信息,这不是我想要的。
PATH
该工具是sclite,一种对语音识别输出进行评分的工具。有关如何在 Windows 上使用该工具的信息,请参阅下面的如何安装 SCLITE(用于可复制性)部分。
sclite
这是一个关于我如何设置包裹的小“玩具”示例。
$ tree package_holder_dir/ package_holder_dir/ ├── bbd_package │ ├── __init__.py │ ├── non_py_exe_dll │ │ ├── cygwin1.dll │ │ └── sclite.exe │ ├── score │ │ ├── __init__.py │ │ └── run_sclite.py │ └── score_transcript.py ├── MANIFEST.in ├── README.txt └── setup.py 3 directories, 9 files
我的第一个猜测是它sclite.exe应该放在MANIFEST.in文件中。
sclite.exe
MANIFEST.in
# @file MANIFEST.in include ./README.txt include ./bbd_package/non_py_exe_dll/sclite.exe include ./bbd_package/non_py_exe_dll/cygwin1.dll
我也尝试把它们放进去setup.py
setup.py
我的setup.py如下
#!/usr/bin/env/python3 # -*- coding: utf-8 -*- # @file setup.py import setuptools from distutils.core import setup with open ("README.txt", "r") as fh: long_description = fh.read() setup( name='bbd_package', url='user@host.com', author='bballdave025', author_email='not.likely@idontwantspam.com', packages=setuptools.find_packages(), #data_files=[('lib', ['./non_py_exe_dll/cygwin1.dll']), # './non_py_exe_dll/sclite.exe'], version='0.0.1', description="Example for SO", long_description=long_description, include_package_data=True ) ##endof: setup
请注意,我使用这个源(已存档data_files)作为 的位置DLL。但是,当我在其他地方安装发行版时,我找不到这些文件。这就是为什么在这里将它们注释掉的原因。
data_files
DLL
使用似乎可行,但随后我必须使用相对路径来访问可执行文件。如果尝试在另一个目录中MANIFEST.in访问,则此方法无效。import bbd_package
import bbd_package
让我尝试用我的两个 Python 文件来说明:
score_transcript.py只需调用run_sclite.py。
score_transcript.py
run_sclite.py
#!/usr/env/bin python3 # -*- coding: utf-8 -*- # @file run_sclite.py import os, sys, subprocess def run_sclite(hyp, ref): subprocess.call(['../non_py_exe_dll/sclite.exe', '-h', hyp, '-r', ref, '-i', 'rm', \ '-o', 'all snt'])
我可以将它安装在我的系统上:
C:\toy_executable_example\package_holder_dir>pip install .
然后如果我碰巧在目录中run_sclite.py
C:\toy_executable_example\package_holder_dir\bbd_package\score>python Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import bbd_package.score.run_sclite >>> bbd_package.score.run_sclite.run_sclite('a.hyp', 'a.ref') sclite Version: 2.10, SCTK Version: 1.3 ...output that shows it works... >>>
然而,从任何其他目录来看,都没有结果。
C:\Users\me>python Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import bbd_package.score.run_sclite >>> bbd_package.score.run_sclite.run_sclite('a.hyp', 'a.ref') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\dblack\AppData\Local\Programs\Python\Python36\lib\site-packages\bbd_package\score\run_sclite.py", line 9, in run_sclite '-o', 'all snt']) File "C:\Users\dblack\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 267, in call with Popen(*popenargs, **kwargs) as p: File "C:\Users\dblack\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 709, in __init__ restore_signals, start_new_session) File "C:\Users\dblack\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 997, in _execute_child startupinfo) FileNotFoundError: [WinError 2] The system cannot find the file specified >>>
我怎样才能告诉 Python 在我的包中寻找可执行文件?
此信息是systeminfo从 Windows 命令提示符运行中获取的。
systeminfo
OS Name: Microsoft Windows 10 Enterprise OS Version: 10.0.15063 N/A Build 15063 OS Manufacturer: Microsoft Corporation OS Configuration: Member Workstation OS Build Type: Multiprocessor Free
我在这里附上了一个安装说明的链接(使用 Cygwin),请查看我的评论。以下是没有解释的命令
$ cd $ git clone https://github.com/kaldi-asr/kaldi.git $ cd kaldi/tools $ extras/check_dependencies.sh $ make -j $(nproc --all) $ cp -R sctk-2.4.10 ~/ $ cd $ rm -rf kaldi $ cd sctk-2.4.10/ $ cp $HOME/.bashrc "${HOME}/.bashrc.$(date +%Y%m%d-%H%M%S).bak" $ echo -e "\n\n## Allow access to sclite, rfilter, etc" >> $HOME/.bashrc $ echo 'export PATH='"$(pwd)/bin"':$PATH' >> $HOME/.bashrc $ source ~/.bashrc
我还附上了一个链接(已存档EXE),其中包含“更快”的说明以及有关和文件的信息DLL。这一切都要感谢@benreaves
EXE
Easier (but even uglier) workaround, which I gave to an intern who did some Word Error Rate calculations for me: On my laptop with Cygwin installed, I create sclite.exe using my py cp src/*/*.c . ; make all workaround from my previous message I create a zip file containing sclite.exe and cygwin1.dll from my laptop’s c:/cygwin/bin/ folder
Easier (but even uglier) workaround, which I gave to an intern who did some Word Error Rate calculations for me:
On my laptop with Cygwin installed, I create sclite.exe using my
py cp src/*/*.c . ; make all
workaround from my previous message
I create a zip file containing sclite.exe and cygwin1.dll from my laptop’s c:/cygwin/bin/ folder
cygwin1.dll
c:/cygwin/bin/
我没有设置PATH,因为我希望它是可分发的。
This worked just fine on her laptop running Windows 7, and she didn’t need Cygwin or any other NIST software on her laptop. -Ben
This worked just fine on her laptop running Windows 7, and she didn’t need Cygwin or any other NIST software on her laptop.
-Ben
我终于让它工作了。我拼凑起来的源代码如下。基本答案是我使用变量__file__来查找调用打包模块的目录,然后使用相对路径来获取我的可执行文件。我对这个解决方案并不满意(这里{ archived } 是一些它不起作用的情况),但它现在完成了工作。
__file__
文件MANIFEST.in保持不变:
# @file MANIFEST.in # @author bballdave025 include ./README.txt include ./bbd_package/non_py_exe_dll/sclite.exe include ./bbd_package/non_py_exe_dll/cygwin1.dll
但是,我需要将以下内容添加到exe运行代码中。这是run_sclite.py
exe
from pathlib import Path #... path_to_here = os.path.dirname(os.path.abspath(__file__)) path_to_pkg_root = Path(path_to_here).resolve().parents[1] path_to_exe = os.path.join(path_to_pkg_root, 'non_py_exe_dll') #...
请注意,Python 版本 >= 3.4 是必需的pathlib
pathlib
以下是整个 ( run_sclite.py) 文件:
#!/usr/env/bin python3 # -*- coding: utf-8 -*- # @file run_sclite.py # @author bballdave025 import os, sys, subprocess from pathlib import Path def run_sclite(hyp, ref): path_to_here = os.path.dirname(os.path.abspath(__file__)) path_to_pkg_root = Path(path_to_here).resolve().parents[1] path_to_exe = os.path.join(path_to_pkg_root, 'non_py_exe_dll') subprocess.call([os.path.join(path_to_exe, 'sclite.exe'), '-h', hyp, '-r', ref, \ '-i', 'rm', '-o', 'all snt']) ##endof: run_sclite(hyp, ref)