Cómo solucionar el "Intento de importación relativa sin paquete" incluso con __init__.py
Estoy intentando seguir PEP 328 , con la siguiente estructura de directorios:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
Tengo core_test.py
la siguiente declaración de importación.
from ..components.core import GameLoopEvents
Sin embargo, cuando lo ejecuto, aparece el siguiente error:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
Buscando, encontré " la ruta relativa no funciona incluso con __init__.py " e " Importar un módulo desde una ruta relativa ", pero no ayudaron.
¿Hay algo que me falta aquí?
Para profundizar en la respuesta de Ignacio Vázquez-Abrams :
El mecanismo de importación de Python funciona en relación con el __name__
del archivo actual. Cuando ejecuta un archivo directamente, no tiene su nombre habitual, sino que tiene "__main__"
como nombre. Entonces las importaciones relativas no funcionan.
Puedes, como sugirió Ignacio, ejecutarlo usando la -m
opción. Si tiene una parte de su paquete que debe ejecutarse como un script, también puede usar el __package__
atributo para decirle a ese archivo qué nombre se supone que debe tener en la jerarquía del paquete.
Consulte http://www.python.org/dev/peps/pep-0366/ para obtener más detalles.
Sí. No lo estás usando como un paquete.
python -m pkg.tests.core_test
Depende de cómo quieras iniciar tu script.
Si desea iniciar su UnitTest desde la línea de comando de forma clásica, es decir:
python tests/core_test.py
Luego, dado que en este caso 'componentes' y 'pruebas' son carpetas hermanas, puede importar el módulo relativo utilizando el método insert o append del módulo sys.path . Algo como:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
De lo contrario, puede iniciar su script con el argumento '-m' (tenga en cuenta que en este caso estamos hablando de un paquete y, por lo tanto, no debe dar la extensión '.py' ), es decir:
python -m pkg.tests.core_test
En tal caso, simplemente puedes usar la importación relativa como lo estabas haciendo:
from ..components.core import GameLoopEvents
Finalmente puedes combinar los dos enfoques, para que tu script funcione sin importar cómo se llame. Por ejemplo:
if __name__ == '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
else:
from ..components.core import GameLoopEvents
Puede usarlo import components.core
directamente si agrega el directorio actual a sys.path
:
if __name__ == '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
En core_test.py, haga lo siguiente:
import sys
sys.path.append('../components')
from core import GameLoopEvents