HOWTO · Python
Resolver intento de importación relativa sin paquete principal conocido en Python
Este tutorial describe cómo resolver ImportError y lidiar con la importación relativa en Python.
En esta página
ImportError: attempted relative import with no known parent package significa que Python está ejecutando un módulo sin el contexto de paquete que necesita su importación con punto inicial. Por lo general, la solución consiste en ejecutar el módulo a través de su paquete o usar una importación absoluta que coincida con la estructura del proyecto. Añadir __init__.py por sí solo no proporciona un paquete padre a un script ejecutado directamente.
Por qué Python genera este ImportError
Una importación relativa como from .helpers import format_name parte del paquete del módulo actual. Python conoce ese paquete cuando importa el módulo como parte de un paquete, pero un archivo ejecutado directamente se llama __main__ y, por lo general, no tiene nombre de paquete. Por eso, python path/to/module.py puede fallar aunque estén presentes el directorio del paquete y el archivo importado.
La sintaxis también debe ser una importación from válida. import .module_name no es sintaxis válida de Python; usa una forma como from . import module_name o from .module_name import function_name. Un punto inicial significa «relativo a este paquete», no «busca en el mismo directorio sin importar cómo se haya iniciado este archivo».
Ejecuta el módulo dentro de su contexto de paquete
Para un paquete llamado myPackage, coloca los archivos en un mismo directorio del proyecto: IError/myPackage/__init__.py, IError/myPackage/myNewPackage.py y IError/myPackage/main.py. En main.py, puedes escribir una importación relativa así: from .myNewPackage import createSingleDict. Ejecútalo desde IError, el directorio que contiene myPackage, con python -m myPackage.main. La opción -m le pide a Python que localice y ejecute el módulo como parte de su paquete, de modo que la importación relativa tenga un paquete padre conocido.
Si el archivo depende de una importación relativa, no ejecutes myPackage/main.py directamente por su ruta. Tampoco ejecutes __init__.py directamente: ese archivo inicializa el paquete cuando Python lo importa y no es el punto de entrada del paquete.
Usa una importación absoluta si encaja con el proyecto
Una importación absoluta indica el módulo desde la ruta de búsqueda de importaciones; por ejemplo, from myPackage.myNewPackage import createSingleDict. Puede ser una buena opción si el proyecto ya usa importaciones desde la raíz del paquete y esta está disponible en sys.path (por ejemplo, porque ejecutas el programa desde la raíz del proyecto o porque instalaste el paquete en el entorno).
Elige un estilo de importación que encaje con la aplicación y con la forma de iniciarla. Cambiar a una importación absoluta no soluciona el problema si Python no encuentra el nombre del paquete; comprueba que el paquete indicado se pueda importar desde el entorno actual.
Comprueba la estructura del paquete y el directorio de trabajo
Confirma que el directorio indicado en la importación sea el paquete que quieres cargar, que el nombre del archivo del módulo coincida con la importación y que inicies Python desde la raíz prevista del proyecto. En el ejemplo anterior, el directorio de trabajo es IError, no IError/myPackage; el comando es python -m myPackage.main. Si el paquete está anidado, incluye cada componente del paquete en la ruta del módulo, por ejemplo, python -m project.tools.runner desde el directorio que contiene project.
Un paquete normal suele incluir __init__.py, aunque Python también admite paquetes de espacio de nombres en estructuras adecuadas. La clave para diagnosticar el problema es cómo se ejecuta el módulo: un archivo puede estar dentro del directorio de un paquete y aun así carecer de contexto de paquete si se inicia directamente. Comprueba también el intérprete y el entorno activos si el paquete se importa de forma distinta desde una terminal, un IDE y un ejecutor de pruebas.
Verifica la solución con un ejemplo mínimo
Este ejemplo autónomo crea un paquete pequeño en un directorio temporal y luego inicia su módulo de entrada con python -m. Ejecuta el ejemplo con Python; imprimirá el valor que devuelve la función importada.
from pathlib import Path
import subprocess
import sys
import tempfile
with tempfile.TemporaryDirectory() as temporary_directory:
project = Path(temporary_directory)
package = project / "myPackage"
package.mkdir()
(package / "__init__.py").write_text("", encoding="utf-8")
(package / "myNewPackage.py").write_text(
'def createSingleDict(name, value):\n'
' return {"name": name, "value": value}\n',
encoding="utf-8",
)
(package / "main.py").write_text(
"from .myNewPackage import createSingleDict\n"
'print(createSingleDict("Jacob", 25))\n',
encoding="utf-8",
)
result = subprocess.run(
[sys.executable, "-m", "myPackage.main"],
cwd=project,
check=True,
capture_output=True,
text=True,
)
print(result.stdout, end="")
El directorio de trabajo del comando es la raíz temporal del proyecto, por lo que Python puede encontrar myPackage y cargar main como módulo del paquete. Si adaptas este patrón, asegúrate de que la ruta del módulo después de -m coincida con las carpetas que hay bajo el directorio desde el que inicias Python.
Exporta nombres de submódulos desde init.py cuando sea necesario
Las exportaciones a nivel de paquete son una comodidad opcional. Si quieres que quienes usan el paquete puedan acceder a ciertos nombres como myPackage.createSingleDict, añade from .myNewPackage import createSingleDict a __init__.py; al importar myPackage, esa función quedará expuesta. Si hay varios módulos, __init__.py puede importar nombres seleccionados de cada uno; por ejemplo, from .otherPackage import printName.
Estas exportaciones determinan qué nombres resulta cómodo usar desde el espacio de nombres del paquete. No proporcionan contexto de paquete a un archivo ejecutado directamente; para resolver esa causa, ejecuta el módulo como parte del paquete o usa una importación absoluta adecuada.