Create a python executable using setuptools

scottmrogowski picture scottmrogowski · May 24, 2013 · Viewed 11.3k times · Source

I have a small python application that I would like to make into a downloadable / installable executable for UNIX-like systems. I am under the impression that setuptools would be the best way to make this happen but somehow this doesn't seem to be a common task.

My directory structure looks like this:

myappname/
|-- setup.py
|-- myappname/
|   |-- __init__.py
|   |-- myappname.py
|   |-- src/
|      |-- __init__.py
|      |-- mainclassfile.py
|      |-- morepython/
|         |-- __init__.py
|         |-- extrapython1.py
|         |-- extrapython2.py

The file which contains if __name__ == "__main__": is myappname.py. This file has a line at the top, import src.mainclassfile.

When this is downloaded, I would like for a user to be able to do something like:

$ python setup.py build
$ python setup.py install

And then it will be an installed executable which they can invoke from anywhere on the command line with:

$ myappname arg1 arg2

The important parts of my setup.py are like:

from setuptools import setup, find_packages
setup(
  name='code2flow',
  scripts=['myappname/myappname.py'],
  package_dir={'myappname': 'myappname'},
  packages=find_packages(),
  )

Current state

By running:

$ sudo python setup.py install

And then in a new shell:

$ myapp.py

I am getting a No module named error

Answer

abarnert picture abarnert · May 25, 2013

The problem here is that your package layout is broken.

It happens to work in-place, at least in 2.x. Why? You're not accessing the package as myappname—but the same directory that is that package's directory is also the top-level script directory, so you end up getting any of its siblings via old-style relative import.

Once you install things, of course, you'll end up with the myappname package installed in your site-packages, and then a copy of myappname.py installed somewhere on your PATH, so relative import can't possibly work.

The right way to do this is to put your top-level scripts outside the package (or, ideally, into a bin directory).

Also, your module and your script shouldn't have the same name. (There are ways you can make that work, but… just don't try it.)

So, for example:

myappname/
|-- setup.py
|-- myscriptname.py
|-- myappname/
|   |-- __init__.py
|   |-- src/
|      |-- __init__.py
|      |-- mainclassfile.py

Of course so far, all this makes it do is break in in-place mode the exact same way it breaks when installed. But at least that makes things easier to debug, right?

Anyway, your myscriptname.py then has to use an absolute import:

import myappname.src.mainclassfile

And your setup.py has to find the script in the right place:

scripts=['myscriptname.py'],

Finally, if you need some code from myscriptname.py to be accessible inside the module, as well as in the script, the right thing to do is to refactor it into two files—but if that's too difficult for some reason, you can always write a wrapper script.

See Arranging your file and directory structure and related sections in the Hitchhiker's Guide to Packaging for more details.

Also see PEP 328 for details on absolute vs. relative imports (but keep in mind that when it refers to "up to Python 2.5" it really means "up to 2.7", and "starting in 2.6" means "starting in 3.0".

For a few examples of packages that include scripts that get installed this way via setup.py (and, usually, easy_install and pip), see ipython, bpython, modulegraph, py2app, and of course easy_install and pip themselves.