8.2. Trovare il percorso

Quando lanciate gli script Python da linea di comando, qualche volta torna utile sapere dov'è localizzato lo script corrente sul disco fisso.

Questo è uno degli oscuri e piccoli trucchi che sono virtualmente impossibili da imparare da soli, ma semplici da ricordare una volta visti. La chiave è nel sys.argv. Come abbiamo visto in Elaborare XML, sys.argv è una lista contenente gli argomenti da linea di comando. Comunque, contiene anche il nome dello script corrente, esattamente come se fosse chiamato da linea di comando, e questa è un'informazione sufficiente per determinarne la locazione.

Esempio 8.3. fullpath.py

Se non lo avete ancora fatto, potete scaricare questo ed altri esempi usati in questo libro.


import sys, os

print 'sys.argv[0] =', sys.argv[0]             1
pathname = os.path.dirname(sys.argv[0])        2
print 'path =', pathname
print 'full path =', os.path.abspath(pathname) 3
1 Senza curarsi di come lanciate lo script, sys.argv[0] conterrà sempre il nome dello script, esattamente come appare da linea di comando. Questo può o meno includere informazioni sul percorso, come vedremo fra breve.
2 os.path.dirname prende un filename come stringa e ritorna la porzione di percorso della directory. Se il filename dato non include informazioni sul percorso, os.path.dirname ritorna una stringa vuota.
3 os.path.abspath è il pezzo forte. Prende un percorso, che può essere parziale o anche vuoto, e ritorna un percorso completo.

os.path.abspath richiede ulteriori spiegazioni. È molto flessibile; può prendere ogni tipo di percorso.

Esempio 8.4. Ulteriori spiegazioni su os.path.abspath

>>> import os
>>> os.getcwd()                        1
/home/f8dy
>>> os.path.abspath('')                2
/home/f8dy
>>> os.path.abspath('.ssh')            3
/home/f8dy/.ssh
>>> os.path.abspath('/home/f8dy/.ssh') 4
/home/f8dy/.ssh
>>> os.path.abspath('.ssh/../foo/')    5
/home/f8dy/foo
1 os.getcwd() ritorna la directory di lavoro corrente.
2 Chiamando os.path.abspath con una stringa vuota ritorna la directory corrente, come os.getcwd().
3 Chiamando os.path.abspath con un percorso parziale viene costruito un percorso completo, basato sulla directory corrente.
4 Chiamando os.path.abspath con un percorso completo semplicemente restituisce sé stesso.
5 os.path.abspath si occupa anche di normalizzare i percorsi che ritorna. Notate che questo esempio funziona anche se non avete realmente una diretory 'foo'. os.path.abspath non controlla mai il vostro disco locale; è solo una manipolazione di stringhe.
Nota
I percorsi e i nomi di file che passate a os.path.abspath non è necessario che esistano.
Nota
os.path.abspath non realizza solo percorsi completi, si occupa anche di normalizzarli. Se siete nella directory /usr/, os.path.abspath('bin/../local/bin') ritornerà /usr/local/bin. Se volete solo normalizzare un percorso senza trasformarlo in un percorso completo, usate invece os.path.normpath.

Esempio 8.5. Esempio di output di fullpath.py

[f8dy@oliver py]$ python /home/f8dy/diveintopython/common/py/fullpath.py 1
sys.argv[0] = /home/f8dy/diveintopython/common/py/fullpath.py
path = /home/f8dy/diveintopython/common/py
full path = /home/f8dy/diveintopython/common/py
[f8dy@oliver diveintopython]$ python common/py/fullpath.py               2
sys.argv[0] = common/py/fullpath.py
path = common/py
full path = /home/f8dy/diveintopython/common/py
[f8dy@oliver diveintopython]$ cd common/py
[f8dy@oliver py]$ python fullpath.py                                     3
sys.argv[0] = fullpath.py
path = 
full path = /home/f8dy/diveintopython/common/py
1 Nel primo caso, sys.argv[0] include il percorso completo dello script. Possiamo poi usare la funzione os.path.dirname per rimuovere il nome dello script e ritornare il nome completo della directory, e os.path.abspath ritorna semplicemente ciò che gli diamo.
2 Se lo script lavora usando un percorso parziale, sys.argv[0] conterrà ancora esattamente quello che appare sulla linea di comando. os.path.dirname poi ci restituirà un percorso parziale (relativo alla directory corrente), mentre os.path.abspath costruirà un percorso completo dal percorso parziale.
3 Se lo script viene lanciato dalla directory corrente senza dargli alcun percorso, os.path.dirname restituirà semplicemente una stringa vuota. Con una stringa vuota, os.path.abspath ritorna la directory corrente, che è ciò che vogliamo, dato che lo script è stato lanciato proprio dalla directory corrente.
Nota
Come altre funzioni nei moduli os e os.path, anche os.path.abspath è multi-piattaforma. I vostri risultati sembreranno leggermente differenti dai miei esempi se state lavorando su Windows (che usa i backslash come separatori di percorso) o sul Mac OS (che usa due punti), ma funzionano comunque. Questo è il punto cruciale del modulo os.

Appendice.  Un lettore era insodddisfatto di questa soluzione, voleva poter lanciare tutti i test delle unità di codice nella directory corrente, non in quella dove si trovava regression.py. Suggerì quest'altro approccio:

Esempio 8.6. Lanciare script nella directory corrente

import sys, os, re, unittest

def regressionTest():
    path = os.getcwd()       1
    sys.path.append(path)    2
    files = os.listdir(path) 3
1 Invece di impostare path alla directory dove sta girando lo script, lo impostiamo invece alla corrente directory di lavoro. Questa sarà qualunque directory in cui vi trovavate prima di lanciare lo script, che non è necessariamente la stessa dove si trova lo script. (Leggete questa frase un po' di volte fino a che non l'avete capita.)
2 Aggiungete questa directory alla libreria di ricerca dei percorsi di Python, così quando importiamo dinamicamente i moduli di test delle unità di codice, più tardi, Python potrà trovarli. Noi non avevamo bisogno di farlo quando path era la directory dello script, perché Python cerca sempre in quella directory.
3 Il resto della funzione rimane invariato.

Questa tecnica vi permetterà di riutilizzare questo script regression.py in più progetti. È sufficiente che mettiate lo script in una directory comune e poi anche nella directory del progetto prima di lanciarlo. Tutti quei test delle unità di codice del progetto saranno trovati e provati, invece di quelli ubicati nella directory comune dove si trova regression.py.