Du code, du communisme

Path.py plus en détail

La première fois que j’avais rencontré path.py, je l’avais trouvé « juste pratique », et donc je n’avais pas passé plus de temps dessus. Un jour je me suis poussé à l’utiliser partout dans un projet type « labo » dans lequel je mettais plein de libs à l’épreuve du feu. Force est de constater que sur le long terme, cette bibliothèque fait gagner beaucoup à un projet pour un coût infime. Je l’inclus maintenant par défaut partout.

A quoi sert path.py ?

path.py est typiquement une lib qui ne sert à rien à première vue, car il n’y a rien qu’on puisse faire avec qu’on ne puisse déjà faire avec la lib standard de Python. C’est juste un wrapper autour de os, os.path et shutils. Un peu comme requests pour urllib en fait.
Elle permet juste de manipuler les fichiers, et chemin de fichiers. C’est tout.

Quel intérêt alors ?

L’API est propre, cohérente, simple, intuitive. En un mot, c’est beau. On gagne du temps à l’usage, à la relecture et au debugging, car le code est devenu tout petit et tellement facile à comprendre. Mais le plus fort, c’est que le coût de transition est quasi nul: tout code utilisant path.py est par défaut compatible avec le code précédent, sans changer une virgule.

Montre moi

<music>Lunatic Calm - Leave You Far Behind</music>
Installation (ceci dit ça tient dans un fichier…)

pip install path.py

On importe, et on crée un objet path à partir d’une chaîne de caractère représentant un chemin de fichier ou de dossier :

>>> from path import path
>>> tmp = path('/tmp/')

Manipuler un chemin de fichier n’a jamais été aussi facile

>>> new = tmp / 'new.txt' # "/" fait os.path.join()
>>> new
path('/tmp/new.txt')
>>> new.isfile()
False
>>> new.touch() # créer le fichier vide
>>> new.isfile()
True

Extraire des données également:

>>> new.ext
'.txt'
>>> new.name
path('new.txt')
>>> new.parent
path('/tmp')
>>> new.parent.parent
path('/')

C’est joli. On en mangerait. Notez que chaque méthode retourne un nouvel objet path() sur lequel on peut donc appliquer les mêmes méthodes.
En prime, un objet path() se comporte aussi comme une string:

>>> print new
/tmp/new.txt
>>> new.upper()
'/TMP/NEW.TXT'
>>> os.path.join(new.parent, 'new_new.txt')
path('/tmp/new_new.txt')

Du coup, aucun problème de migration de l’ancien code. Utiliser path.py n’a virtuellement que le coût de l’install et de l’import.
On a aussi accès à tout un tas de méthodes avancées:

>>> tmp.dirs() # listing du dossier courant
[path('/tmp/pulse-XnVNgklabjGI'), path('/tmp/ssh-vrCzN4692eCp'), path('/tmp/pulse-PKdhtXMmr18n'), path('/tmp/.truecrypt_aux_mnt1'), path('/tmp/.X11-unix'), path('/tmp/.ICE-unix'), path('/tmp/plugtmp'), path('/tmp/pulse-2L9K88eMlGn7'), path('/tmp/orbit-sam'), path('/tmp/.winbindd'), path('/tmp/tracker-sam')]
>>> tmp.files()
[path('/tmp/new.txt'), path('/tmp/backup_tem.zip'), path('/tmp/qtsingleapp-mediat-134e-3e8-lockfile'), path('/tmp/unity_support_test.0'), path('/tmp/apprentissage-130107043807-phpapp02.odp'), path('/tmp/nIzDHlLoho'), path('/tmp/.X0-lock'), path('/tmp/IDcRs0LUki')]
>>> tmp.files('*.txt') # filtres
[path('/tmp/new.txt')]
>>> tmp.walk() # walk, walkfiles et walkdirs == même chose, récursivement

>>> list(path('/etc/php5').walkfiles('*.ini'))
[path('/etc/php5/conf.d/10-pdo.ini'), path('/etc/php5/mods-available/pdo.ini'), path('/etc/php5/cli/php.ini'), path('/etc/php5/cli/conf.d/10-pdo.ini'), path('/etc/php5/apache2/php.ini'), path('/etc/php5/apache2/conf.d/10-pdo.ini')]
>>> (tmp / 'test/test/test').makedirs() # création récursive
>>> (tmp / 'test/test/test').isdir()
True
>>> (tmp / 'test/test/test').makedirs_p() # les "_p" ignorent certaines erreurs
>>> (tmp / 'test/test/test').removedirs()

Et quelques goodies:

>>> with path('./Work'):
    print path('.').realpath()
    print path('.').listdir()[0]
...
/home/sam/Work
./A référencer
>>> path('~').expanduser()
path('/home/sam')
>>> path('/etc/fstab').open().readline()
'# /etc/fstab: static file system information.\n'
>>> new.write_text("BAM d'un coup")
>>> new.text()
"BAM d'un coup"

Très pratique dans un shell.