16.2. Trouver le chemin

Lorsque vous exécutez des scripts Python depuis la ligne de commande, il est parfois utile de savoir l'emplacement sur le disque du script en cours d'exécution.

C'est un de ces trucs obscurs qui sont pratiquement impossibles à deviner seul, mais qu'il est simple de se souvenir une fois que vous l'avez vu. L'élément-clé en est sys.argv. Comme vous l'avez vu au Chapitre 9, Traitement de données XML, c'est une liste qui contient la liste des arguments de ligne de commande. Mais elle contient également le nom du script en cours d'exécution comme il a été appelé depuis la ligne de commande et cela suffit à déterminer son emplacement.

Exemple 16.3. fullpath.py

Si vous ne l’avez pas déjà fait, vous pouvez télécharger cet exemple ainsi que les autres exemples du livre.


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 Indépendamment de la manière dont vous exécutez un script, sys.argv[0] contient toujours le nom de ce script, exactement comme il apparaît sur la ligne de commande. Le chemin peut être inclus ou non, comme nous allons le voir.
2 os.path.dirname prend un nom de fichier sous forme de chaîne et en retourne la partie qui représente le chemin. Si le nom de fichier ne contient pas d'information de chemin, os.path.dirname retourne une chaîne vide.
3 os.path.abspath, l'élément-clé, prend un chemin, qui peut être partiel ou même vide et retourne un chemin complet.

os.path.abspath demande une explication plus détaillée. C'est une fonction très souple, qui peut prendre n'importe quel type de chemin en argument.

Exemple 16.4. Explication détaillée de os.path.abspath

>>> import os
>>> os.getcwd()                        1
/home/you
>>> os.path.abspath('')                2
/home/you
>>> os.path.abspath('.ssh')            3
/home/you/.ssh
>>> os.path.abspath('/home/you/.ssh') 4
/home/you/.ssh
>>> os.path.abspath('.ssh/../foo/')    5
/home/you/foo
1 os.getcwd() retourne le répertoire de travail en cours.
2 L'appel de os.path.abspath avec une chaîne vide retourne le répertoire de travail en cours, comme os.getcwd().
3 L'appel de os.path.abspath avec un chemin partiel construit un chemin complet à partir de ce chemin partiel, basé sur le répertoire de travail en cours.
4 L'appel de os.path.abspath avec un chemin complet le retourne simplement.
5 os.path.abspath normalise également le chemin qu'il retourne. Notez que cet exemple marche alors que je n'ai pas de répertoire 'foo', os.path.abspath ne vérifie jamais votre disque, ce n'est que de la manipulation de chaînes.
NOTE
Les chemins et noms de fichier que vous passez à os.path.abspath n'ont pas besoin d'exister sur le disque.
NOTE
os.path.abspath ne construit pas seulement des chemins complets, il les normalise. Cela signifie que si vous êtes dans le répertoire /usr/, os.path.abspath('bin/../local/bin') retournera /usr/local/bin. Il normalise le chemin en le rendant aussi simple que possible. Si vous voulez normaliser un chemin de cette manière sans le transformer en chemin complet, utilisez os.path.normpath.

Exemple 16.5. Exemple de sortie de fullpath.py

[you@localhost py]$ python /home/you/diveintopython/common/py/fullpath.py 1
sys.argv[0] = /home/you/diveintopython/common/py/fullpath.py
path = /home/you/diveintopython/common/py
full path = /home/you/diveintopython/common/py
[you@localhost diveintopython]$ python common/py/fullpath.py               2
sys.argv[0] = common/py/fullpath.py
path = common/py
full path = /home/you/diveintopython/common/py
[you@localhost diveintopython]$ cd common/py
[you@localhost py]$ python fullpath.py                                     3
sys.argv[0] = fullpath.py
path = 
full path = /home/you/diveintopython/common/py
1 Dans le premier cas, sys.argv[0] inclut le chemin complet du script. Vous pouvez ensuite utiliser la fonction os.path.dirname pour enlever le nom du script et retourner le chemin complet du répertoire, os.path.abspath retournera simplement ce que vous lui donnez.
2 Si le script est exécuté avec un chemin partiel, sys.argv[0] contient exactement ce qui a été tapé en ligne de commande. os.path.dirname vous retournera un chemin partiel (relatif au répertoire en cours) et os.path.abspath construira le chemin complet à partir du chemin partiel.
3 Si le script est exécuté depuis le répertoire en cours sans donner de chemin, os.path.dirname retournera simplement une chaîne vide. À partir d'une chaîne vide, os.path.abspath retourne le répertoire en cours, ce qui est bien la valeur recherchée puisque le script a été exécuté depuis le répertoire en cours.
NOTE
Comme les autres fonctions des modules os et os.path, os.path.abspath est multiplate-forme. Les résultats que vous obtiendrez seront légèrement ifférents si vous utilisez Windows (qui utilise le le backslash comme séparateur de chemin) ou Mac OS (qui utilise les deux points), mais le script fonctionnera. C'est là le rôle du module os.

Addendum. Un lecteur ne s'est pas trouvé satisfait par cette solution, il voulait pouvoir exécuter tous les tests unitaires depuis le répertoire en cours, pas celui où regression.py est situé. Il suggère l'approche suivante :

Exemple 16.6. Exécuter les scripts dans le répertoire en cours

import sys, os, re, unittest

def regressionTest():
    path = os.getcwd()       1
    sys.path.append(path)    2
    files = os.listdir(path) 3
1 Au lieu de changer le chemin vers le répertoire ou le script en cours d'exécution est situé, on lui assigne le répertoire en cours. C'est le répertoire dans lequel vous étiez quand vous avez lancé le script, qui n'est pas forcément le même que celui dans lequel se trouve le script (relisez cette phrase jusqu'à que vous ayez compris).
2 Ce chemin est ajouté au chemin de recherche des bibliothèques de Python, de manière à ce que Python puisse trouver les modules de tests unitaires lorsque vous les importerez. Vous n'aviez pas besoin de le faire lorsque le chemin était le répertoire du script en cours d'exécution, car Python cherche toujours dans ce répertoire.
3 Le reste de la fonction est inchangé.

Cette technique vous permettra de réutiliser ce script regression.py dans d'autres projets. Le script est mis dans un répertoire commun et vous l'exécutez depuis le répertoire du projet. Tous les tests unitaires de ce projet sont trouvés et exécutés, au lieu des tests unitaires du répertoire commun dans lequel est situé regression.py.