Pregunta Cómo solucionar "Intento de importación relativa en no-paquete" incluso con __init__.py


Estoy tratando de seguir PEP 328, con la siguiente estructura de directorio:

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

En core_test.py Tengo la siguiente declaración de importación

from ..components.core import GameLoopEvents

Sin embargo, cuando corro, 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 alrededor encontré "la ruta relativa no funciona incluso con __init__.py"y"Importar un módulo desde una ruta relativa"pero no ayudaron".

¿Hay algo que me falta aquí?


585
2017-07-18 07:59


origen


Respuestas:


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

python -m pkg.tests.core_test

363
2017-07-18 08:01



Para elaborar sobre Ignacio Vazquez-Abrams responder:

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, pero tiene "__main__" como su nombre en su lugar. Entonces las importaciones relativas no funcionan.

Puede, como Igancio sugirió, ejecutarlo usando el -m opción. Si tiene una parte de su paquete que debe ejecutarse como un script, también puede usar __package__ atributo para decirle a ese archivo qué nombre se supone que tiene en la jerarquía del paquete.

Ver http://www.python.org/dev/peps/pep-0366/ para detalles.


535
2017-07-18 08:26



Puedes usar 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__))))

189
2017-10-04 21:00



Depende de cómo quiera iniciar su script.

Si quieres lanza tu UnitTest desde la línea de comando de una manera clásica, eso es:

python tests/core_test.py

Entonces, dado que en este caso 'componentes' y 'pruebas' son carpetas de hermanos, puede importar el módulo relativo usando el insertar o el adjuntar método de la sys.path módulo. 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, puedes inicia tu script con el argumento '-m' (tenga en cuenta que en este caso, estamos hablando de un paquete, y por lo tanto no debe dar el '.py' extensión), eso es:

python -m pkg.tests.core_test

En tal caso, puede simplemente usar la importación relativa como estaba haciendo:

from ..components.core import GameLoopEvents

Finalmente puede mezclar los dos enfoques, para que su 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

155
2018-01-10 13:36



En core_test.py, haz lo siguiente:

import sys
sys.path.append('../components')
from core import GameLoopEvents

11
2018-01-10 17:44



Si su caso de uso es para ejecutar pruebas, y parece que sí lo es, entonces puede hacer lo siguiente. En lugar de ejecutar su script de prueba como python core_test.py utilizar un marco de prueba como pytest. Luego, en la línea de comando, puede ingresar

$$ py.test

Eso ejecutará las pruebas en su directorio. Esto evita el problema de __name__ siendo __main__ eso fue señalado por @BrenBarn. A continuación, pon un vacío __init__.py archivo en su directorio de prueba, esto hará que el directorio de prueba sea parte de su paquete. Entonces podrás hacer

from ..components.core import GameLoopEvents

Sin embargo, si ejecuta su script de prueba como un programa principal, las cosas fallarán una vez más. Así que solo usa el corrector de prueba. Quizás esto también funciona con otros corredores de prueba como nosetests pero no lo he comprobado Espero que esto ayude.


8
2017-11-13 17:00



Mi solución rápida es agregar el directorio a la ruta:

import sys
sys.path.insert(0, '../components/')

4
2017-11-01 07:19



Hilo viejo Descubrí que agregando un __all__= ['submodule', ...] al __init__.py archivo y luego usar el from <CURRENT_MODULE> import * en el objetivo funciona bien.


2
2018-04-18 17:13