Python应用跨平台分发

发布于 2024-08-11 03:01:44 字数 1642 浏览 11 评论 0原文

我想在 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 上,该应用程序显然是一个由 py​​2app 构建的独立的 .app 东西。我想知道我的做法是否完全错误,在 OSX 上分发 Python 应用程序时是否有最佳实践?

编辑:我也在 b.py 中尝试过这样的黑客:

from ..mod2.b import *

ValueError: Attempted relative import beyond toplevel package

Edit2:似乎与此相关 如何在Python中进行相对导入?

I want to distribute my app on OSX (using py2app) and as a Debian package.

The structure of my app is like:

app/
     debian/
            <lots of debian related stuff>
     scripts/
             app
     app/
         __init__.py
         app.py
         mod1/
              __init__.py
              a.py
         mod2/
              __init__.py
              b.py

My setup.py looks something like:

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"]),
       ]
    )

Then, in b.py (this is on OSX):

from app.mod2.b import *

I get:

ImportError: No module named mod2.b

So basically, mod2 can't acccess mod1. On Linux there's no problem, because the python module 'app' is installed globally in /usr/shared/pyshared. But on OSX the app will obviously be a self-contained .app thing built by py2app. I wonder if I approached this totally wrong, are there any best practices when distributing Python apps on OSX?

Edit: I also tried a hack like this in b.py:

from ..mod2.b import *

ValueError: Attempted relative import beyond toplevel package

Edit2: Seems to be related to this How to do relative imports in Python?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

路还长,别太狂 2024-08-18 03:01:44

我不确定这是否是“最佳实践”(我没有将太多 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

如果您从 somemodule.py 导入 _setup,则该安装文件可以确保顶级包位于 sys.path 中在评估 somemodule.py 中的其余代码之前。

I'm not sure if this is the 'best practice' or not (I've not put much python software into proper distribution), but I would just make sure that the top-level app package was in sys.path. Something like putting the following into the top-level __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)

I think that should do the right thing in a cross platform way.

EDIT: As kaizer.se points out this might not work in the __init__.py file, depending on how the code you're invoking is getting executed. It would only work if that file is evaluated. The key is to make sure that the top-level package is in sys.path from some the code that actually is running.

Often times, so that I an execute individual files inside of a package directly (for testing with the if __name__ eq '__main__' idiom), I'll do something like place a statement:

import _setup

At the top of the individual file in question, and then create a file _setup.py which does the path munging as necessary. So, something like:

package/
    __init__.py
    _setup.py
    mod1/
        __init__.py
        _setup.py
        somemodule.py

If you import _setup from somemodule.py, that setup file can ensure that the top level package is in sys.path before the rest of the code in somemodule.py is evaluated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文