Si un archivo de Python dentro de un paquete genera ImportError: attempted relative import with no known parent package al ejecutarlo directamente, inícielo como un módulo desde el directorio superior al paquete. En macOS, vaya a la raíz del proyecto en Terminal y luego ejecute python3 -m package.module. Esto le proporciona a Python el contexto de paquete que una ruta de archivo directa no ofrece.
¿Por qué falla una importación relativa cuando ejecuto un archivo directamente?
Una importación relativa como from .helpers import format_result depende del nombre del paquete del módulo. Python utiliza puntos iniciales para identificar el paquete actual y sus padres. Cuando ejecuta un archivo con python3 path/to/file.py, Python ejecuta ese archivo como el módulo de nivel superior __main__; no le proporciona los metadatos de paquete necesarios para resolver el punto. Por lo tanto, la importación puede fallar a pesar de que helpers.py esté junto al archivo. La referencia de importación de Python describe las importaciones relativas y el tratamiento especial de __main__.
Por ejemplo, suponga que su proyecto está organizado de la siguiente manera:
project/
└── myapp/
├── __init__.py
├── cli.py
└── helpers.py
Si cli.py contiene from .helpers import format_result, ejecutar python3 myapp/cli.py desde project es la invocación incorrecta. El problema radica en la forma en que se le pidió a Python que ejecutara el archivo, no necesariamente en que la importación relativa o la ubicación del archivo sean erróneas.
Ejecute el módulo desde la raíz del proyecto en macOS
Abra Terminal y cámbiese al directorio que contiene myapp. Reemplace la ruta de ejemplo con la ubicación real de su proyecto. Escriba la ruta entre comillas para que el shell gestione los espacios; el shell predeterminado de Terminal en macOS es zsh. La guía de Terminal de Apple documenta dicho valor predeterminado.
cd "/Users/yourname/Projects/project"
python3 -m myapp.cli
La opción -m le pide a Python que busque y ejecute el módulo por su nombre de paquete separado por puntos. Mantenga el nombre del paquete en el comando y omita .py y las barras del sistema de archivos. La ruta de búsqueda de módulos de Python comienza con el directorio actual cuando se invoca con -m, por lo que ejecutarlo desde la raíz del proyecto es fundamental. La documentación de sys.path de Python explica esta ruta inicial.
Si el comando utiliza la instalación incorrecta de Python, active primero el entorno virtual existente del proyecto y luego use el mismo patrón python -m. Para un .venv típico en la raíz del proyecto:
cd "/Users/yourname/Projects/project"
source .venv/bin/activate
python -m myapp.cli
Usar python después de la activación selecciona el intérprete del entorno. Si su entorno tiene un nombre o ubicación diferente, ajuste la ruta de activación; no asuma que .venv existe. Puede verificar el intérprete seleccionado con python -c 'import sys; print(sys.executable)'.
Si desea ejecutar el paquete en sí
Para un comando destinado a iniciar el paquete, agregue __main__.py dentro del paquete y mantenga el punto de entrada breve:
myapp/
├── __init__.py
├── __main__.py
├── cli.py
└── helpers.py
En myapp/__main__.py, importe y llame a la función de entrada del paquete. Luego ejecute python3 -m myapp desde la raíz del proyecto. Python ejecuta el archivo __main__.py del paquete para esta forma de invocación de -m, y ese archivo puede utilizar importaciones relativas al paquete. La documentación de __main__ de Python explica este patrón de punto de entrada del paquete.
Compruebe estos detalles si el error persiste
- Confirme su directorio de trabajo. Ejecute
pwden Terminal y asegúrese de que muestre la raíz del proyecto, el directorio que contienemyapp. Si el paquete está anidado bajosrc, por ejemplo, utilice el nombre del paquete importable y asegúrese de que el entorno configurado del proyecto lo tenga disponible; limitarse a cambiar a la carpeta superior equivocada no solucionará el descubrimiento de módulos. - Utilice el nombre real del paquete. En
python3 -m myapp.cli,myappdebe ser el nombre del directorio del paquete (o su nombre de importación), ycliel nombre de archivo del módulo sin.py. - Revise la línea del rastreo (traceback). Si la importación relativa se resuelve pero falla una importación posterior, es posible que se trate de un módulo o dependencia diferente que falta. Corregir el contexto de ejecución solo soluciona el fallo del contexto de paquete.
- Evite usar
PYTHONPATHcomo primera solución. Modifica la ruta de búsqueda de importación de Python, pero por sí solo no convierte un archivo ejecutado directamente en un módulo de paquete con el nombre correcto. Python también advierte quePYTHONPATHpuede afectar a múltiples versiones y entornos de Python. La referencia de inicialización de rutas de Python describe su alcance. - Si ejecutar el archivo directamente es un requisito, considere modificar el código para usar una importación absoluta adecuada y asegúrese de que el paquete esté instalado o sea importable de otro modo en ese entorno. Esa es una opción de configuración diferente; no mezcle una importación relativa al paquete con la expectativa de que el archivo funcione como un script independiente.
La regla práctica es hacer coincidir el comando con el estilo de importación: para una importación relativa al paquete, ejecute el módulo desde el directorio superior del paquete con python -m. Para un comando de paquete, incluya __main__.py y ejecute el paquete con -m.
Fuentes
- 5. The import system — Python Software Foundation
- The initialization of the sys.path module search path — Python Software Foundation
- main — Top-level code environment — Python Software Foundation
- Change the default shell in Terminal on Mac — Apple