python - top - Cómo corregir "Intento de importación relativa en un no paquete" incluso con__init__.py




python no module named (8)

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 ejecuto, me sale 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é " ruta relativa que no funciona incluso con __init__.py " e " Importar un módulo desde una ruta relativa " pero no ayudaron.

¿Hay algo que me esté perdiendo aquí?


Como dijo Paolo , tenemos 2 métodos de invocación:

1) python -m tests.core_test
2) python tests/core_test.py

Una diferencia entre ellos es sys.path [0] cadena. Como la interpretación buscará sys.path cuando se realice una importación , podemos hacerlo con tests/core_test.py :

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

Y más después de esto, podemos ejecutar core_test.py con otros métodos:

cd tests
python core_test.py
python -m core_test
...

Tenga en cuenta, py36 probado solamente.


Depende de cómo quieras lanzar tu script.

Si desea iniciar su UnitTest desde la línea de comandos de una manera clásica, es decir:

python tests/core_test.py

Luego, dado que en este caso los 'componentes' y 'pruebas' son carpetas de hermanos, puede importar el módulo relativo utilizando el método de inserción o el método de sys.path 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 puede 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

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

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

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 su nombre. Así que las importaciones relativas no funcionan.

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

Vea http://www.python.org/dev/peps/pep-0366/ para más detalles.


Puede usar from pkg.components.core import GameLoopEvents , por ejemplo, yo uso pycharm, la siguiente es la imagen de la estructura de mi proyecto, acabo de importar desde el paquete raíz, luego funciona:


Puede usar import components.core directamente si sys.path 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__))))

Si alguien está buscando una solución, me topé con uno. Aquí hay un poco de contexto. Quería probar uno de los métodos que tengo en un archivo. Cuando lo ejecuto desde dentro

if __name__ == "__main__":

Siempre se quejaba de las importaciones relativas. Intenté aplicar las soluciones anteriores, pero no funcionó, ya que había muchos archivos anidados, cada uno con múltiples importaciones.

Esto es lo que hice. Acabo de crear un lanzador, un programa externo que importaría los métodos necesarios y los llamaría. Aunque, no es una gran solución, funciona.


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

$$ py.test

Eso ejecutará las pruebas en su directorio. Esto __main__ el problema de que __name__ sea __main__ que fue señalado por @BrenBarn. Luego, coloque un archivo __init__.py vacío 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 volverán a fallar. Así que solo usa el corredor de prueba. Tal vez esto también funciona con otros corredores de prueba, como las nosetests pero no lo he comprobado. Espero que esto ayude.





init