Создать исполняемый файл из модуля Python с помощью Pyinstaller или Cython
Я хочу преобразовать в исполняемый файл этот проект Python 2.7, который имеет структуру модуля:
(.venv) ip-192-168-22-127:indictrans loretoparisi$ tree -L 1
.
├── __init__.py
├── __init__.pyc
├── __init__.spec
├── _decode
├── _utils
├── base.py
├── build
├── mappings
├── models
├── script_transliterate.py
├── tests
├── transliterator.py
└── trunk
я использую pyinstaller
для этого на первом этапе я просто делаю:
pyinstall --onefile __init__.py
и я получаю исполняемый файл:
192 INFO: PyInstaller: 3.3.1
192 INFO: Python: 2.7.10
201 INFO: Platform: Darwin-17.7.0-x86_64-i386-64bit
202 INFO: wrote /Users/loretoparisi/Documents/Projects/AI/indic-trans/indictrans/__init__.spec
208 INFO: UPX is not available.
209 INFO: Extending PYTHONPATH with paths
['/Users/loretoparisi/Documents/Projects/AI/indic-trans',
'/Users/loretoparisi/Documents/Projects/AI/indic-trans/indictrans']
210 INFO: checking Analysis
218 INFO: checking PYZ
223 INFO: checking PKG
224 INFO: Bootloader /Users/loretoparisi/Documents/Projects/AI/indic-trans/.venv/lib/python2.7/site-packages/PyInstaller/bootloader/Darwin-64bit/run
224 INFO: checking EXE
225 INFO: Rebuilding out00-EXE.toc because __init__ missing
225 INFO: Building EXE from out00-EXE.toc
225 INFO: Appending archive to EXE /Users/loretoparisi/Documents/Projects/AI/indic-trans/indictrans/dist/__init__
230 INFO: Fixing EXE for code signing /Users/loretoparisi/Documents/Projects/AI/indic-trans/indictrans/dist/__init__
234 INFO: Building EXE from out00-EXE.toc completed successfully.
Но когда я запускаю его, я получаю ошибку импорта
Traceback (most recent call last):
File "indictrans/__init__.py", line 9, in <module>
ValueError: Attempted relative import in non-package
[30629] Failed to execute script __init__
Эта библиотека построена с использованием Cython через установку cythonize, поэтому другой вариант будет заключаться в создании исполняемого встроенного модуля с использованием --embed
Вариант Cython.
мой setup.py
является следующим:
#!/usr/bin/env python
import os
from setuptools import setup
from setuptools.extension import Extension
from Cython.Build import cythonize
import numpy
os.environ['PBR_VERSION'] = '1.2.3'
os.environ['SKIP_WRITE_GIT_CHANGELOG'] = '1'
os.environ['SKIP_GENERATE_AUTHORS'] = '1'
extensions = [
Extension(
"indictrans._decode.beamsearch",
[
"indictrans/_decode/beamsearch.pyx"
],
include_dirs=[numpy.get_include()]
),
Extension(
"indictrans._decode.viterbi",
[
"indictrans/_decode/viterbi.pyx"
],
include_dirs=[numpy.get_include()]
),
Extension(
"indictrans._utils.ctranxn",
[
"indictrans/_utils/ctranxn.pyx"
],
include_dirs=[numpy.get_include()]
),
Extension(
"indictrans._utils.sparseadd",
[
"indictrans/_utils/sparseadd.pyx"
],
include_dirs=[numpy.get_include()]
)
]
setup(
setup_requires=['pbr'],
pbr=True,
ext_modules=cythonize(extensions)
)
В то время как легко собрать один файл Python с --embed option
см. здесь для получения дополнительной информации об этом, я не знаю, как использовать --embed
вариант в setup.py
для того, чтобы избавиться от всех зависимостей в проекте.
0 ответов
Я собираюсь обратиться к корневому каталогу пакета с setup.py
файл в качестве каталога пакета, которыйindic-trans
в твоем случае. Я имею в виду исходный каталог первого уровня как каталог модуля, то естьindic-trans/indictrans
в твоем случае.
Если я правильно понимаю вашу настройку, у вас возникла проблема из-за попытки создать .exe
в сценарии в каталоге модуля, а не в каталоге пакета. Это делает текущий каталог внутренним каталогом модуля, и относительные ссылки работать не будут.
В аналогичной ситуации я решил эту проблему, создав run.py
скрипт в каталоге основного пакета (в той же папке, что иsetup.py
), который импортирует пакет, а затем запускает любой сценарий, который вы хотите запустить в каталоге модуля.
Вы не разместили __init__.py
файл (кстати, обычно не одобряется наличие реального кода в __init.py__
...), поэтому я предполагаю, что ниже вы хотите запустить main()
функция в base.py
сценарий. В этом случае сделайтеrun.py
что-то типа:
# This will import everything from the module __init__.py based on it's "all" setup
# You likely want to limit it to just what is needed or just the script you are targeting
# Or you can use the "import indictrans" format and let __init__.py handle it
from indictrans import *
def main():
indictrans.base.main()
if __name__ == '__main__':
main()
Теперь ты можешь просто бежать run.py
из командной строки или используйте его в качестве цели отладки для запуска вашего пакета.
Обратите внимание, что с помощью PyInstaller вы можете настроить таргетинг run.py
(или run.spec
) и измените имя на другое с помощью --name
аргумент.
pyinstaller --onefile --name indictrans run.spec
который создаст indictrans.exe
в dist
каталог.
Запуск вашего пакета как модуля
Обратите внимание, что в каталоге модуля вы также можете создать__main__.py
файл, который в основном является копией run.py
и делает то же самое, но будет запускаться как модуль исполняемым файлом Python, если он установлен локально.
т.е. python -m indictrans
запустит ваш пакет, используя __main__.py
в качестве точки входа.