Come risolvere "Tentativo di importazione relativa in non pacchetto" anche con __init__.py


744

Sto cercando di seguire PEP 328 , con la seguente struttura di directory:

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

In core_test.pyho la seguente dichiarazione di importazione

from ..components.core import GameLoopEvents

Tuttavia, quando corro, ottengo il seguente errore:

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

Cercando in giro ho trovato " percorso relativo non funzionante anche con __init__.py " e " Importa un modulo da un percorso relativo " ma non mi hanno aiutato.

C'è qualcosa che mi manca qui?


17
Sono stato anche molto confuso dai vari modi di strutturare i unittestprogetti, quindi ho scritto questo progetto di esempio abbastanza esaustivo che copre l'annidamento profondo dei moduli, le importazioni relative e assolute (dove il lavoro e no) e il riferimento relativo e assoluto dall'interno di un pacchetto, nonché importazione di classi singola, doppia e a livello di pacchetto. Aiutato le cose in chiaro proprio per me!
cod3monk3y,

1
Non sono riuscito a far funzionare i tuoi test. Continua a ricevere no module named myimports.fooquando li eseguo.
Blairg23,

@ Blairg23 Sto indovinando l'invocazione destinato è quello cdin PyImports, ed eseguire python -m unittest tests.test_abs, per esempio.
duozmo,

7
Sono d'accordo con Gene. Vorrei che ci fosse un meccanismo per il debug del processo di importazione che fosse un po 'più utile. Nel mio caso, ho due file nella stessa directory. Sto cercando di importare un file nell'altro file. Se ho un file init .py in quella directory, ottengo un ValueError: tentativo di importazione relativa in un errore non di pacchetto. Se rimuovo il file .py di init , allora ricevo un errore, nessun modulo chiamato errore "NAME".
user1928764

Nel mio caso, ho due file nella stessa directory. Sto cercando di importare un file nell'altro file. Se ho un file init .py in quella directory, ottengo un ValueError: tentativo di importazione relativa in un errore non di pacchetto. Se rimuovo il file .py di init , allora ricevo un errore, nessun modulo chiamato errore "NAME". Ciò che è davvero frustrante è che ho avuto questo lavoro, e poi mi sono sparato ai piedi cancellando il file .bashrc, che imposta PYTHONPATH su qualcosa, e ora non funziona.
user1928764

Risposte:


443

Sì. Non lo stai usando come pacchetto.

python -m pkg.tests.core_test

51
A gotcha: Nota che non c'è '.py' alla fine!
mindthief

497
Non sono uno dei downvoter, ma penso che questo potrebbe usare un po ' più di dettagli, data la popolarità di questa domanda e risposta. Notare cose come da quale directory eseguire il comando shell di cui sopra, il fatto che tu ne abbia bisogno __init__.pyfino in fondo, e il __package__trucco di modifica (descritto di seguito da BrenBarn) necessario per consentire queste importazioni per script eseguibili (ad esempio quando usi un shebang e fare ./my_script.pycon la shell Unix) sarebbe tutto utile. L'intero problema è stato piuttosto complicato per me capire o trovare documentazione concisa e comprensibile.
Mark Amery,

16
Nota: è necessario trovarsi all'esterno della directory pkgnel punto in cui si chiama questa linea dalla CLI. Quindi, dovrebbe funzionare come previsto. Se sei dentro pkge chiami python -m tests.core_test, non funzionerà. Almeno non è stato per me.
Blairg23,

94
Davvero, puoi spiegare cosa sta succedendo nella tua risposta?
Pinocchio,

18
@MarkAmery Ho quasi perso la testa cercando di capire come funziona tutto ciò, le relative importazioni all'interno di un progetto con sottodirectory con file PY che hanno __init__.pyfile ma continui a ricevere l' ValueError: Attempted relative import in non-packageerrore. Pagherei davvero un buon prezzo per qualcuno, da qualche parte, per spiegare finalmente in parole povere come funziona tutto questo.
AdjunctProfessorFalcon

635

Per approfondire la risposta di Ignacio Vazquez-Abrams :

Il meccanismo di importazione di Python funziona in relazione al __name__file corrente. Quando si esegue direttamente un file, non ha il suo solito nome, ma ha "__main__"invece il suo nome. Quindi le importazioni relative non funzionano.

Come suggerito da Igancio, puoi eseguirlo usando l' -mopzione. Se hai una parte del tuo pacchetto che deve essere eseguita come script, puoi anche usare l' __package__attributo per dire a quel file quale nome dovrebbe avere nella gerarchia del pacchetto.

Vedi http://www.python.org/dev/peps/pep-0366/ per i dettagli.


55
Mi ci è voluto un po 'di tempo per rendermi conto che non puoi correre python -m core_testdalla testssottodirectory: deve essere dal genitore o devi aggiungere il genitore al percorso.
Aram Kocharyan,

3
@DannyStaple: non esattamente. È possibile utilizzare __package__per assicurarsi che i file di script eseguibili possano importare relativamente altri moduli dallo stesso pacchetto. Non c'è modo di importare relativamente da "l'intero sistema". Non sono nemmeno sicuro del motivo per cui vorresti farlo.
BrenBarn,

2
Voglio dire se il __package__simbolo è impostato su "parent.child", allora saresti in grado di importare "parent.other_child". Forse non l'ho detto così bene.
Danny Staple,

5
@DannyStaple: Bene, come funziona è descritto nella documentazione collegata. Se hai uno script script.pynel pacchetto pack.subpack, quindi impostarlo __package__su pack.subpackti consentirà from ..module import somethingdi importare qualcosa da pack.module. Si noti che, come dice la documentazione, è ancora necessario disporre del pacchetto di livello superiore nel percorso di sistema. Questo è già il modo in cui funzionano le cose per i moduli importati. L'unica cosa che __package__fa è permetterti di usare quel comportamento anche per gli script eseguiti direttamente.
BrenBarn,

3
Uso __package__lo script che viene eseguito direttamente ma purtroppo viene visualizzato il seguente errore: "Modulo genitore 'xxx' non caricato, impossibile eseguire l'importazione relativa"
mononoke

202

È possibile utilizzare import components.coredirettamente se si aggiunge la directory corrente 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__))))

35
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))questo funzionerà anche
ajay il

26
from os import syssembra barare :)
volare pecore il

3
@Piotr: potrebbe essere considerato migliore perché mostra leggermente più chiaramente a cosa viene aggiunto sys.path- il genitore della directory in cui si trova il file corrente.
martineau

8
@flyingsheep: D'accordo, userei solo un normale import sys, os.path as path.
martineau,

10
Cordiali saluti, di utilizzare questo in un quaderno ipython, mi sono adattato a questa risposta: import os; os.sys.path.append(os.path.dirname(os.path.abspath('.'))). Quindi una scala import components.corefunziona per me, importando dalla directory padre del notebook come desiderato.
Racing Tadpole,

195

Dipende da come si desidera avviare lo script.

Se vuoi avviare UnitTest dalla riga di comando in modo classico, vale a dire:

python tests/core_test.py

Quindi, poiché in questo caso 'componenti' e 'test' sono cartelle di fratelli, è possibile importare il modulo relativo utilizzando il metodo insert o append del modulo sys.path . Qualcosa di simile a:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

Altrimenti, puoi lanciare il tuo script con l'argomento '-m' (nota che in questo caso stiamo parlando di un pacchetto, e quindi non devi dare l' estensione '.py' ), cioè:

python -m pkg.tests.core_test

In tal caso, puoi semplicemente utilizzare l'importazione relativa mentre stavi facendo:

from ..components.core import GameLoopEvents

Puoi finalmente mescolare i due approcci, in modo che il tuo script funzioni indipendentemente da come viene chiamato. Per esempio:

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

3
cosa devo fare se sto cercando di utilizzare il PDB per il debug? poiché utilizzi python -m pdb myscript.pyper avviare la sessione di debug.
Danny,

1
@dannynjust - Questa è una buona domanda poiché non puoi avere 2 moduli principali. In genere, durante il debug, preferisco passare manualmente al debugger nel primo punto in cui desidero iniziare il debug. Puoi farlo inserendo a import pdb; pdb.set_trace()nel codice (inline).
mgilson

3
È meglio usare insertinvece di append? Cioè,sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SparkAndShine

2
L'uso di insert è una corrispondenza migliore per la relativa semantica di importazione, in cui i nomi dei pacchetti locali hanno la precedenza sui pacchetti installati. Soprattutto per i test, di solito si desidera testare la versione locale, non quella installata (a meno che l'infrastruttura di test non installi il codice in prova, nel qual caso le importazioni relative non sono necessarie e non si avrà questo problema).
Alex Dupuy,

1
dovresti anche menzionare che non puoi essere nella directory contenente core_test quando esegui un modulo (sarebbe troppo facile)
Joseph Garvin,

25

In core_test.py, procedi come segue:

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

10

Se il tuo caso d'uso è per l'esecuzione di test, e sembra che lo sia, allora puoi fare quanto segue. Invece di eseguire lo script di test, python core_test.pyutilizzare un framework di test come pytest. Quindi dalla riga di comando è possibile inserire

$$ py.test

Ciò eseguirà i test nella tua directory. Questo aggira il problema __name__dell'essere __main__che è stato sottolineato da @BrenBarn. Quindi, inserisci un __init__.pyfile vuoto nella directory di test, questo renderà la directory di test parte del tuo pacchetto. Allora sarai in grado di fare

from ..components.core import GameLoopEvents

Tuttavia, se esegui il tuo script di test come programma principale, le cose falliranno ancora una volta. Quindi basta usare il test runner. Forse questo funziona anche con altri test runner come nosetestsma non l'ho verificato. Spero che sia di aiuto.


9

La mia soluzione rapida è aggiungere la directory al percorso:

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

6
Il tuo approccio non funzionerà in tutti i casi perché la parte '../' viene risolta dalla directory dalla quale esegui il tuo script (core_test.py). Con il tuo approccio sei costretto a fare cd su "test" prima di eseguire lo scritp core_test.py.
Xyman,

7

Il problema riguarda il metodo di test,

hai provato python core_test.py

allora otterrai questo errore ValueError: Tentativo di importazione relativa in non pacchetto

Motivo: stai testando la tua confezione da una fonte non di imballaggio

quindi prova il tuo modulo dal pacchetto sorgente.

se questa è la struttura del tuo progetto,

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

cd pkg

python -m tests.core_test # dont use .py

o dall'esterno pkg /

python -m pkg.tests.core_test

singolo .se si desidera importare dalla cartella nella stessa directory. per ogni passo indietro aggiungine uno in più.

hi/
  hello.py
how.py

in how.py

from .hi import hello

se vuoi importare come da hello.py

from .. import how

1
Risposta migliore di quella accettata
GabrielBB,

Nell'esempio from .. import how, come importare una specifica classe / metodo dal file 'how'. quando faccio l'equivalente di from ..how import fooallora ricevo "tentata importazione relativa oltre il pacchetto di primo livello"
James Hulse

3

Filo vecchio. Ho scoperto che l'aggiunta di un __all__= ['submodule', ...]al file __init__.py e quindi l'utilizzo di from <CURRENT_MODULE> import *nella destinazione funziona correttamente.


3

Puoi usare from pkg.components.core import GameLoopEvents, ad esempio, io uso pycharm, il seguente è l'immagine della struttura del mio progetto, ho appena importato dal pacchetto radice, quindi funziona:

inserisci qui la descrizione dell'immagine


3
Questo non ha funzionato per me. Hai dovuto impostare il percorso nella tua configurazione?
Mohammad Mahjoub

3

Come ha detto Paolo , abbiamo 2 metodi di invocazione:

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

Una differenza tra loro è la stringa sys.path [0]. Dato che interpret cercherà sys.path durante l'importazione , possiamo fare 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>

E ancora dopo, possiamo eseguire core_test.py con altri metodi:

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

Nota, solo py36 testato.


3

Questo approccio ha funzionato per me ed è meno disordinato rispetto ad alcune soluzioni:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

La directory padre è nel mio PYTHONPATH e ci sono __init__.pyfile nella directory padre e in questa directory.

Quanto sopra ha sempre funzionato in Python 2, ma a volte Python 3 ha colpito un ImportError o ModuleNotFoundError (quest'ultimo è nuovo in Python 3.6 e una sottoclasse di ImportError), quindi il seguente tweak funziona per me in entrambi Python 2 e 3:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents


1

Se qualcuno è alla ricerca di una soluzione alternativa, mi sono imbattuto in uno. Ecco un po 'di contesto. Volevo testare uno dei metodi che ho in un file. Quando lo eseguo dall'interno

if __name__ == "__main__":

si è sempre lamentato delle importazioni relative. Ho provato ad applicare le soluzioni di cui sopra, ma non ho funzionato, poiché c'erano molti file nidificati, ognuno con più importazioni.

Ecco cosa ho fatto. Ho appena creato un launcher, un programma esterno che avrebbe importato i metodi necessari e li avrebbe chiamati. Tuttavia, non un'ottima soluzione, funziona.


0

Ecco un modo che farà incazzare tutti ma funzionerà abbastanza bene. Nei test eseguiti:

ln -s ../components components

Quindi importa solo i componenti come faresti normalmente.


0

Questo è molto confuso e se stai usando IDE come Pycharm, è un po 'più confuso. Cosa ha funzionato per me: 1. Effettua le impostazioni del progetto pycharm (se stai eseguendo python da un VE o dalla directory python) 2. Non c'è modo sbagliato di definire. a volte funziona con dalla classe di importazione folder1.file1

se non funziona, utilizzare import folder1.file1 3. La variabile di ambiente deve essere correttamente menzionata nel sistema o specificarla nell'argomento della riga di comando.


-2

Poiché il codice contiene if __name__ == "__main__", che non viene importato come pacchetto, è consigliabile utilizzarlo sys.path.append()per risolvere il problema.


Non credo che avere if __name__ == "__main__"nel tuo file faccia la differenza per tutto ciò che riguarda l'importazione.
user48956
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.