En términos generales, ¿cómo están estructurados los proyectos (Python)?

Estoy un poco perdido cuando se trata de estructurar mi (s) proyecto (s). Trato de estructurar las cosas de manera que tengan sentido, pero siempre terminan reestructurando todo al menos dos veces por día. Por supuesto, mis proyectos no son muy grandes, pero me encantaría no tener que reestructurar todo y simplemente conformarme con algo por una vez.

Describiré mi progtwig actual para tratar de darle sentido a las cosas. Es un progtwig gráfico con una base de datos para calcular el precio de las velas. No todo está escrito todavía, pero el usuario podrá seleccionar una categoría y modelo de navegación desde dos menús desplegables. Dependiendo de la combinación categoría-modelo, el progtwig presentará casillas de verificación y casillas de selección. Estas casillas de verificación y casillas de selección, cuando se modifican, obtienen información de una base de datos y presentan el precio de tener esa checkbox marcada o un número determinado (por ejemplo, área en metros cuadrados) en la casilla de selección.

En su forma actual, el proyecto se ve así:

COPYING README.md SailQt.pyw (Should program be called from here ...) sailqt/ __init__.py (This holds a __version__ string) SailQt.pyw (... or here?) gui/ __init__.py MainWindow.py (This needs access to a __version__ string) MainWindow_rc.py OptionsWidget.py ui_MainWindow.py ui_OptionsWidget.py resources/ __init__.py database.db generate_gui.py MainWindow.ui MainWindow.qrc OptionsWidget.ui icons/ logo.png 

Para aclarar aún más. resources contiene todos los archivos .ui creados en Qt Designer. Son archivos XML que describen la GUI. Se pueden convertir a scripts de Python con una herramienta de terminal, que he incrustado en generate_gui.py . Lo mismo ocurre con los archivos .qrc . generate_gui.py coloca los archivos generate_gui.py automáticamente en la carpeta gui con el prefijo ui_ o el sufijo _rc . database.db está actualmente vacío, pero eventualmente se utilizará para mantener los precios y todo.

MainWindow.py y OptionsWidget.py son archivos de Python que contienen objetos del mismo nombre, menos el sufijo .py . MainWindow mantiene OptionsWidget en su superficie de visualización. Ambos objetos utilizan sus correspondientes archivos ui y rc .

SailQt.pyw es el archivo que SailQt.pyw una instancia de MainWindow , le dice que se muestre y luego le dice a (Py) Qt que entre en su bucle y se haga cargo desde allí. Básicamente, se parece mucho a un archivo .exe de muchas aplicaciones gráficas, ya que es un archivo pequeño que hace funcionar el progtwig.

Mi conjetura inicial fue colocar SailQt.pyw dentro de la carpeta sailqt . Pero luego, MainWindow.py repentinamente necesitó acceso a una cadena __version__ . La única forma en que podía descubrir cómo lograrlo era mover SailQt.pyw a la carpeta raíz de mi proyecto y dejar que MainWindow.py importara sailqt.__version__ . Pero teniendo en cuenta que fue la enésima vez que tuve que reorganizar las cosas y rehacer las líneas en la mayoría de los archivos para explicar esa pequeña confusión, decidí preguntar aquí.

Mis preguntas son bastante claras:

  • ¿Cómo se estructuran, en general, los proyectos de Python? Este enlace pydoc fue útil, pero me parece más un módulo que algo que realmente ejecuta un usuario.
  • ¿Conseguí el derecho de estructuración anterior?
  • Puntos de bonificación por responder a esto, ya que es un poco fuera de tema. ¿Cómo es que puedo import os y luego hacer cosas como os.system("sudo rm -rf /") , pero no puedo hacer cosas como la import sailqt y luego hacer sailqt.gui.generate_gui.generate() ?

Tratemos primero con tu última pregunta, porque es la más importante en lo que respecta a la estructuración de proyectos de Python. Una vez que haya resuelto cómo hacer que las importaciones funcionen correctamente dentro de su proyecto, el rest será mucho más fácil de manejar.

La clave a entender es que el directorio de la secuencia de comandos actualmente en ejecución se agrega automáticamente al inicio de sys.path . Por lo tanto, si coloca su script main.py (lo que está llamando actualmente SailQt.pyw ) fuera de su paquete en un directorio contenedor de nivel superior, garantizará que las importaciones de paquetes siempre funcionarán, sin importar desde dónde se ejecute el script. .

Así que una estructura inicial mínima podría verse así:

 project/ main.py package/ __init__.py app.py mainwindow.py 

Ahora, debido a que main.py debe estar fuera del directorio de paquetes de python de nivel superior, debe contener solo una cantidad mínima de código (lo suficiente para iniciar el progtwig). Dada la estructura anterior, eso no significaría mucho más que esto:

 if __name__ == '__main__': import sys from package import app sys.exit(app.run()) 

El módulo de la app contendría la mayoría del código real necesario para inicializar el progtwig y configurar la interfaz gráfica de usuario, que se importaría así:

 from package.mainwindow import MainWindow 

y esta misma forma de statement de importación completa puede utilizarse desde cualquier lugar con el paquete. Así, por ejemplo, con esta estructura un poco más complicada:

 project/ main.py package/ __init__.py app.py mainwindow.py utils.py dialogs/ search.py 

El módulo de search podría importar una función del módulo de utils como esta:

  from package.utils import myfunc 

Sobre el problema específico de acceder a la cadena __version__ : para un progtwig PyQt, puede poner lo siguiente en la parte superior del módulo de la app :

  QtGui.QApplication.setApplicationName('progname') QtGui.QApplication.setApplicationVersion('0.1') 

y luego acceda al nombre / versión más tarde así:

  name = QtGui.qApp.applicationName() version = QtGui.qApp.applicationVersion() 

Los otros problemas con su estructura actual tienen que ver principalmente con mantener la separación entre los archivos de código y los archivos de recursos.

En primer lugar: el árbol de paquetes solo debe contener archivos de código (es decir, módulos de Python). Los archivos de recursos pertenecen al directorio del proyecto (es decir, fuera del paquete). En segundo lugar: los archivos que contienen código generado a partir de recursos (p. Ej., Por Pyuic o pyrcc) probablemente deberían ir en un subpaquete separado (esto también hace que sea más fácil para su herramienta de control de versiones excluirlos). Esto daría lugar a una estructura general del proyecto como esta:

 project/ db/ database.db designer/ mainwindow.ui icons/ logo.png LICENSE Makefile resources.qrc main.py package/ __init__.py app.py mainwindow.py ui/ __init__.py mainwindow_ui.py resources_rc.py 

Aquí, Makefile (o su equivalente) es responsable de generar los archivos ui / rc, comstackr los módulos de Python, instalar / desinstalar el progtwig, etc. Los recursos necesarios para el progtwig en tiempo de ejecución (como el archivo de base de datos) deberán ser instalado en una ubicación estándar que su progtwig sepa cómo encontrar (por ejemplo, algo como /usr/share/progname/database.db en Linux). En el momento de la instalación, el Makefile también necesitará generar un script de bash ejecutable (o equivalente) que sepa dónde está su progtwig y cómo iniciarlo. Es decir, algo como:

 #!/bin/sh exec 'python' '/usr/share/progname/main.py' "$@" 

que obviamente debería instalarse como /usr/bin/progname (o lo que sea).

Esto puede parecer mucho para tratar al principio, pero, por supuesto, el principal beneficio de encontrar una estructura de proyecto que funcione bien es que puede reutilizarla para todos los proyectos futuros (y comenzar a desarrollar sus propias plantillas y herramientas). para la puesta en marcha y gestión de dichos proyectos).