Crea un ejecutable de python usando setuptools

Tengo una pequeña aplicación de Python que me gustaría convertir en un ejecutable descargable / instalable para sistemas similares a UNIX. Tengo la impresión de que setuptools sería la mejor manera de hacer que esto suceda, pero de alguna manera esto no parece ser una tarea común.

Mi estructura de directorio se ve así:

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

El archivo que contiene if __name__ == "__main__": es myappname.py. Este archivo tiene una línea en la parte superior, import src.mainclassfile .

Cuando se descargue, me gustaría que un usuario pueda hacer algo como :

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

Y luego será un ejecutable instalado que pueden invocar desde cualquier lugar en la línea de comandos con:

 $ myappname arg1 arg2 

Las partes importantes de mi setup.py son como:

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

Estado actual

Mediante la ejecución:

 $ sudo python setup.py install 

Y luego en un nuevo shell:

 $ myapp.py 

Estoy recibiendo un error No module named

El problema aquí es que el diseño de su paquete está roto.

Sucede que funciona en el lugar, al menos en 2.x. ¿Por qué? No está accediendo al paquete como myappname nombre de myappname pero el mismo directorio que el directorio de ese paquete también es el directorio de secuencias de comandos de nivel superior, por lo que termina obteniendo cualquiera de sus hermanos a través de una importación relativa de estilo antiguo.

Por supuesto, una vez que instales las cosas, terminarás con el paquete myappname instalado en tus paquetes de sitio, y luego una copia de myappname.py instalada en algún lugar de tu PATH, por lo que la importación relativa no puede funcionar.

La forma correcta de hacer esto es colocar sus scripts de nivel superior fuera del paquete (o, idealmente, en un directorio bin ).

Además, su módulo y su script no deberían tener el mismo nombre. (Hay formas en que puede hacer que eso funcione, pero … simplemente no lo intente).

Así por ejemplo:

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

Por supuesto, hasta ahora, todo lo que hace es romper el modo en el lugar de la misma manera que se rompe cuando se instala. Pero al menos eso hace que las cosas sean más fáciles de depurar, ¿verdad?

De todos modos, tu myscriptname.py entonces tiene que usar una importación absoluta:

 import myappname.src.mainclassfile 

Y su setup.py tiene que encontrar el script en el lugar correcto:

 scripts=['myscriptname.py'], 

Finalmente, si necesita que se pueda acceder a algún código de myscriptname.py dentro del módulo, así como en el script, lo correcto es refactorizarlo en dos archivos, pero si eso es demasiado difícil por alguna razón, siempre puede escribir un script envoltorio.

Para obtener más detalles, consulte Organizar la estructura de su archivo y directorio y las secciones relacionadas en la Guía de empaquetado del autostopista.

También vea PEP 328 para obtener detalles sobre las importaciones absolutas frente a las relativas (pero tenga en cuenta que cuando se refiere a “hasta Python 2.5” realmente significa “hasta 2.7”, y “que comienza en 2.6” significa “que comienza en 3.0”.

Para ver algunos ejemplos de paquetes que incluyen scripts que se instalan de esta manera a través de setup.py (y, generalmente, easy_install y pip ), vea ipython , bpython , modulegraph , py2app y, por supuesto, easy_install y pip ellos mismos.