Cómo solucionar el "Intento de importación relativa sin paquete" incluso con __init__.py

Resuelto skytreader asked hace 12 años • 22 respuestas

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.pyla 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í?

skytreader avatar Jul 18 '12 14:07 skytreader
Aceptado

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 -mopció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.

BrenBarn avatar Jul 18 '2012 08:07 BrenBarn

Sí. No lo estás usando como un paquete.

python -m pkg.tests.core_test
Ignacio Vazquez-Abrams avatar Jul 18 '2012 08:07 Ignacio Vazquez-Abrams

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
Paolo Rovelli avatar Jan 10 '2015 13:01 Paolo Rovelli

Puede usarlo import components.coredirectamente 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__))))
ihm avatar Oct 04 '2013 21:10 ihm

En core_test.py, haga lo siguiente:

import sys
sys.path.append('../components')
from core import GameLoopEvents
Allan Mwesigwa avatar Jan 10 '2017 17:01 Allan Mwesigwa