Кроссплатформенное распространение приложений на Python
-
18-09-2019 - |
Вопрос
Я хочу распространять свое приложение на OSX (используя py2app) и в виде пакета Debian.
Структура моего приложения выглядит следующим образом:
app/
debian/
<lots of debian related stuff>
scripts/
app
app/
__init__.py
app.py
mod1/
__init__.py
a.py
mod2/
__init__.py
b.py
Мой setup.py выглядит примерно так:
from setuptools import setup
import os
import os.path
osname = os.uname()[0]
if osname == 'Darwin':
APP = ['app/app.py']
DATA_FILES = []
OPTIONS = {'argv_emulation': True}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
elif osname == 'Linux':
setup(
name = "app",
version = "0.0.1",
description = "foo bar",
packages = ["app", "app.mod1", "app.mod2"],
scripts = ["scripts/app"],
data_files = [
("/usr/bin", ["scripts/app"]),
]
)
Затем, в b.py (это на OSX):
from app.mod2.b import *
Я получаю:
ImportError: No module named mod2.b
Таким образом, в принципе, mod2 не может получить доступ к mod1.В Linux проблем нет, потому что модуль python 'app' установлен глобально в /usr/shared /pyshared.Но в OSX приложение, очевидно, будет автономным .app, созданным py2app.Интересно, если я подошел к этому совершенно неправильно, существуют ли какие-либо рекомендации при распространении приложений Python на OSX?
Редактировать:Я также попробовал подобный взлом в b.py:
from ..mod2.b import *
ValueError: Attempted relative import beyond toplevel package
Редактировать 2:Кажется, это связано с этим Как выполнить относительный импорт в Python?
Решение
Я не уверен, является ли это "наилучшей практикой" или нет (я не распространял много программного обеспечения на python надлежащим образом), но я бы просто убедился, что пакет приложений верхнего уровня был в sys.path
.Что-то вроде помещения следующего на верхний уровень __init__.py
:
try:
import myapp
except ImportError:
import sys
from os.path import abspath, dirname, split
parent_dir = split(dirname(abspath(__file__)))[0]
sys.path.append(parent_dir)
Я думаю, что это должно быть правильно с точки зрения кроссплатформенности.
Редактировать:Как указывает kaizer.se , это может не сработать в __init__.py
файл, в зависимости от того, как выполняется код, который вы вызываете.Это сработало бы только в том случае, если этот файл будет оценен.Главное - убедиться, что пакет верхнего уровня находится в sys.path
из некоторого кода, который на самом деле запущен.
Часто, так что я выполняю отдельные файлы непосредственно внутри пакета (для тестирования с помощью if __name__ eq '__main__'
идиома), я сделаю что-то вроде размещения заявления:
import _setup
В верхней части отдельного файла, о котором идет речь, а затем создайте файл _setup.py
который выполняет сжатие пути по мере необходимости.Итак, что-то вроде:
package/
__init__.py
_setup.py
mod1/
__init__.py
_setup.py
somemodule.py
Если вы import _setup
От somemodule.py
, этот установочный файл может гарантировать, что пакет верхнего уровня находится в sys.path
перед остальной частью кода в somemodule.py
оценивается.