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?
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.
精彩评论