Search code examples
pythonsetuptoolssetup.pypy2app

Relative imports and py2app?


Here's how my files are laid out:

| setup.py
+ myapp
  | __init__.py
  | myapp.py
  | version.py

(Hope that's clear... not too complicated, I don't think.)

Here's what myapp.py contains:

from fingui   import Label
from .version import __version__

Label('I am version: ' + __version__)

If I'm in the directory where I can see setup.py, the following works fine:

python -m myapp.myapp

When I try packaging it up as an app using this:

python setup.py py2app

And then running it, I get this error message on the line where I import the version:

ValueError: Attempted relative import in non-package

Here's the contents of setup.py:

exec(open('myapp/version.py').read())

from setuptools import setup

setup(app                = 'myapp/myapp.py',
      setup_requirements = ['py2app'],
      name               = 'MyApp',
      version            = __version__)

If I look into the app bundle that py2app has generated, I see myapp.py is placed in the bundle, but version.py and __init__.py are nowhere to be found.

What is the proper way to structure my files? How can I get py2app and/or setup to recognize which files are necessary and where to put them?

Also, while we're on the subject, how do I get it to include fingui? That's a library that I've installed using pip... I think py2app might be mistaking it for a standard library module or something, so not including it in my app bundle?


Solution

  • I was able to fix this with the following:

    | main.py   <- New file I added - details below.
    | setup.py  <- I changed a bit in here.
    + myapp     <- Same exact contents as before - I changed nothing.
    

    I created a new file, main.py which lived outside of the myapp package. In setup.py, I told it that main.py was the app, and that myapp was a package.

    Contents of main.py:

    from myapp import myapp
    

    That's it.

    New contents of setup.py, with comments on the changed lines.

    exec(open('myapp/version.py').read())
    
    from setuptools import setup
    
    setup(app                = 'main.py',  ### This now points at main.py instead of myapp/myapp.py
          options            = {'py2app': {'packages': ['myapp']}}, ### This line is all new.
          setup_requirements = ['py2app'],
          name               = 'MyApp',
          version            = __version__)
    

    I still have the problem that it doesn't seem to be copying myapp or fingui into the actual MyApp.app bundle... I think it's relying on MyApp.app/Contents/Resources/site.pyc... which will obviously break if I try to distribute my app.