2015-06-05 3 views
5

Si consideri la seguente struttura del progetto:Perché l'importazione funziona con Python nose quando la directory di test contiene __init__.py?

a.py 
test/ 
    test_a.py 

con test_a.py modulo a importazione:

import a 

Come previsto, in esecuzione nosetests nei test risultati di directory in errore di importazione:

ERROR: Failure: ImportError (No module named a) 

Tuttavia , Ho notato che aggiungendo un vuotoIl filenella directory test rende l'importazione funzionante con nosetests (ma non quando si esegue test_a.py con Python). Potresti spiegare perché?

Capisco che l'aggiunta di __init__.py rende test un pacchetto. Ma vuol dire che l'importazione include la directory che contiene il pacchetto nella ricerca?

+0

http://stackoverflow.com/questions/448271/what-is-init-py-for – Mir

+3

@Mir capisco cosa '__init __. Py' per . Non capisco perché influisce sull'importazione di altri moduli. – vitaut

+0

Perché se non fosse lì, allora potresti importare qualsiasi directory per sbaglio, il che sarebbe quasi inutile. È un controllo programmatico di sorta. – Kris

risposta

5

La presenza di un file __init__.py nella directory trasforma test da una semplice vecchia directory in un python package. Ciò ha effetto su sys.path.

Modificare il modulo test_a.py in questo modo:

import sys 

def test_thing(): 
    for i, p in enumerate(sys.path): 
     print i, p 

try: 
    import a 
except ImportError: 
    print('caught import error') 

Quindi provare a eseguire nosetests -s dalla directory di prova, con e senza __init__.py in là.

Nota: è il test runner che munge sys.path. E questo è documentato in the second "Note" of this section here (grazie a @davidism). Non vedrai alcun cambiamento lì solo eseguendo python test_a.py con e senza la struttura del pacchetto.

+3

Lo capisco, ma in che modo influisce sulla ricerca dell'importazione? – vitaut

+0

Giusto. Ho aggiunto più dettagli. – wim

+1

[py.test (che i nosetests bifrontano da) lo spiega pure] (http://pytest.org/latest/goodpractises.html#choosing-a-test-layout-import-rules-) – davidism

2

Ho esaminato il codice sorgente del modulo del naso ed ecco perché.

def importFromPath(self, path, fqname): 
    """Import a dotted-name package whose tail is at path. In other words, 
    given foo.bar and path/to/foo/bar.py, import foo from path/to/foo then 
    bar from path/to/foo/bar, returning bar. 
    """ 
    # find the base dir of the package 
    path_parts = os.path.normpath(os.path.abspath(path)).split(os.sep) 
    name_parts = fqname.split('.') 
    if path_parts[-1] == '__init__.py': 
     path_parts.pop() 
    path_parts = path_parts[:-(len(name_parts))] 
    dir_path = os.sep.join(path_parts) 
    # then import fqname starting from that dir 
    return self.importFromDir(dir_path, fqname) 

def importFromDir(self, dir, fqname): 
    """Import a module *only* from path, ignoring sys.path and 
    reloading if the version in sys.modules is not the one we want. 
    """ 
    dir = os.path.normpath(os.path.abspath(dir)) 

Nel tuo caso quando importFromDir viene chiamato da importFromPath, 'dir' è la directory un livello superiore dalla directory __init__.py. Ecco perché aggiungere __init__.py al tuo test fa 'importare' un lavoro