gpt4 book ai didi

python - setuptools 包上的 Pyinstaller

转载 作者:太空狗 更新时间:2023-10-29 21:45:07 25 4
gpt4 key购买 nike

我正在尝试在使用 Click 在 Python 中构建的 CLI 应用程序上运行 PyInstaller图书馆。我在使用 PyInstaller 构建项目时遇到问题。 PyInstaller 在他们的 GitHub wiki 中有一个文档,标题为 Recipe Setuptools Entry Point ,它提供了有关如何将 PyInstaller 与 setuptools 包一起使用的信息,我正在将其用于该项目。但是,当我运行 pyinstaller --onefile main.spec 时,它似乎找不到基本模块。

我的问题是:问题仅仅是我的文件夹结构的问题吗? Recipe Setuptools Entry Point假定某种文件结构?

相关信息

Pyinstaller 输出

184 INFO: PyInstaller: 3.3.1
184 INFO: Python: 3.6.4
189 INFO: Platform: Darwin-16.7.0-x86_64-i386-64bit
193 INFO: UPX is available.
Traceback (most recent call last):
File "/usr/local/bin/pyinstaller", line 11, in <module>
sys.exit(run())
File "/usr/local/lib/python3.6/site-packages/PyInstaller/__main__.py", line 94, in run
run_build(pyi_config, spec_file, **vars(args))
File "/usr/local/lib/python3.6/site-packages/PyInstaller/__main__.py", line 46, in run_build
PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
File "/usr/local/lib/python3.6/site-packages/PyInstaller/building/build_main.py", line 791, in main
build(specfile, kw.get('distpath'), kw.get('workpath'), kw.get('clean_build'))
File "/usr/local/lib/python3.6/site-packages/PyInstaller/building/build_main.py", line 737, in build
exec(text, spec_namespace)
File "<string>", line 40, in <module>
File "<string>", line 26, in Entrypoint
File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 582, in get_entry_info
return get_distribution(dist).get_entry_info(group, name)
File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 564, in get_distribution
dist = get_provider(dist)
File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 436, in get_provider
return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 984, in require
needed = self.resolve(parse_requirements(requirements))
File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 870, in resolve
raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'myapp' distribution was not found and is required by the application

main.pymain.spec 文件,它是我的 CLI 应用程序的入口点:

block_cipher = None

def Entrypoint(dist, group, name,
scripts=None, pathex=None, hiddenimports=None,
hookspath=None, excludes=None, runtime_hooks=None):
import pkg_resources

# get toplevel packages of distribution from metadata
def get_toplevel(dist):
distribution = pkg_resources.get_distribution(dist)
if distribution.has_metadata('top_level.txt'):
return list(distribution.get_metadata('top_level.txt').split())
else:
return []

hiddenimports = hiddenimports or []
packages = []
for distribution in hiddenimports:
packages += get_toplevel(distribution)

scripts = scripts or []
pathex = pathex or []
# get the entry point
ep = pkg_resources.get_entry_info(dist, group, name)
# insert path of the egg at the verify front of the search path
pathex = [ep.dist.location] + pathex
# script name must not be a valid module name to avoid name clashes on import
script_path = os.path.join(workpath, name + '-script.py')
print ("creating script for entry point", dist, group, name)
with open(script_path, 'w') as fh:
print("import", ep.module_name, file=fh)
print("%s.%s()" % (ep.module_name, '.'.join(ep.attrs)), file=fh)
for package in packages:
print ("import", package, file=fh)

return Analysis([script_path] + scripts, pathex, hiddenimports, hookspath, excludes, runtime_hooks)

a = Entrypoint('myapp', 'console_scripts', 'myapp')

pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='main',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='main')

在我的虚拟环境中运行 pip3 install --editable . 时生成的 myapp 脚本的内容:

#!/some/path/to/myapp-cli/venv/bin/python3.6
# EASY-INSTALL-ENTRY-SCRIPT: 'myapp','console_scripts','myapp'
__requires__ = 'myapp'
import re
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('myapp', 'console_scripts', 'myapp')()
)

最后,我的存储库结构:

myapp-cli/
├── README.md
├── myapp
│ ├── __init__.py
│ ├── main.py
│ ├── main.spec
│ ├── resources
│ │ ├── __init__.py
│ │ └── functions.py
│ ├── subcommands
│ │ ├── __init__.py
│ │ ├── config
│ │ │ ├── __init__.py
│ │ │ └── cli.py
│ │ ├── create
│ │ │ ├── __init__.py
│ │ │ └── cli.py
│ │ ├── destroy
│ │ │ ├── __init__.py
│ │ │ └── cli.py
│ │ └── switch
│ │ ├── __init__.py
│ │ └── cli.py
│ └── variables.py
├── requirements.txt
└── setup.py

还有我的setup.py 文件:

from setuptools import find_packages
from setuptools import setup
import os

base_dir = os.path.dirname(__file__)

setup(
entry_points = '''
[console_scripts]
myapp=myapp.main:entry_point
''',
install_requires = [
'packageone==1.0',
'packagetwo==2.0',
],
name = "myapp",
packages=find_packages(),
setup_requires="setuptools",
version = "0.1",
)

最佳答案

首先:我结合使用了 Stephen 的答案和我自己的一些挖掘来找到答案。最后,Stephen 的第一部分成功了:手动添加/导出 PYTHONPATH 变量。您实际上可以在 Entrypoint 函数中使用 pathex 指定它,如下所示:

a = Entrypoint('myapp-cli',
'console_scripts',
'myapp',
pathex=['/some/path/to/myapp-cli/myapp', '/some/path/to/myapp-cli']
)

毕竟我最终并不需要 myapp.main

第二:我仍然遇到 PyInstaller not 生成单个二进制文件的问题。对我来说,这成功了:

  • 最新 版本的 PyInstaller 添加到您的 requirements.txtsetup.py 中的 install_requires : https://github.com/pyinstaller/pyinstaller/archive/develop.zip .
  • 此外,您可以使用 pyi-makespec 中的 --onefile 选项制作您的 .spec 文件,如下所示:pyi -makespec --onefile myapp.py。这将生成一个 .spec 文件,确保您的所有包都被编译成二进制文件。

最后,下面的 spec 文件成功了,我能够制作一个完全可用的二进制文件:

# -*- mode: python -*-

block_cipher = None

def Entrypoint(dist, group, name,
scripts=None, pathex=None, hiddenimports=None,
hookspath=None, excludes=None, runtime_hooks=None):
import pkg_resources

# get toplevel packages of distribution from metadata
def get_toplevel(dist):
distribution = pkg_resources.get_distribution(dist)
if distribution.has_metadata('top_level.txt'):
return list(distribution.get_metadata('top_level.txt').split())
else:
return []

hiddenimports = hiddenimports or []
packages = []
for distribution in hiddenimports:
packages += get_toplevel(distribution)

scripts = scripts or []
pathex = pathex or []
# get the entry point
ep = pkg_resources.get_entry_info(dist, group, name)
# insert path of the egg at the verify front of the search path
pathex = [ep.dist.location] + pathex
# script name must not be a valid module name to avoid name clashes on import
script_path = os.path.join(workpath, name + '-script.py')
print ("creating script for entry point", dist, group, name)
with open(script_path, 'w') as fh:
print("import", ep.module_name, file=fh)
print("%s.%s()" % (ep.module_name, '.'.join(ep.attrs)), file=fh)
for package in packages:
print ("import", package, file=fh)

return Analysis([script_path] + scripts, pathex, hiddenimports, hookspath, excludes, runtime_hooks)

a = Entrypoint('myapp-cli',
'console_scripts',
'myapp',
pathex=['/some/path/to/myapp-cli/myapp', '/some/path/to/myapp-cli']
)

pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='myapp',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )

我认为最终使用类似 Cobra for Golang 的东西会更容易工作,因为 Golang 开箱即用地编译单文件二进制文件。但是,如果您更喜欢 Python,这应该可以解决问题。

关于python - setuptools 包上的 Pyinstaller,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48884766/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com