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 interê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éfault 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éer 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
<generator object walk at 0x2e17960>
>>> 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.

12 thoughts on “Path.py plus en détail

  • hdsdi3g

    Ça me fait penser à la classe Java File dans certains cas, et a quelques autres fait maison.

    En fait ça n’a pas l’air, mais une bonne API de manipulation de chemins de fichiers, c’est indispensable.

  • Yohann

    J’arrive un peu après la bataille mais pour les suivants:

    1) Je n’ai pas trouvé la documentation d’api pour cette lib, ce qui est très curieux car il y a une conf spinx + autodoc sur leur repo github (et que je crois me souvenir qu’il est simple dans ces cas de publié la doc sur readthedoc) du coup j’ai construit la doc (un simple make html , c’est ici: documentation api path.py

    2) Je rencontre un souci avec walkfiles et les fichier contenant des caractères non ascii (le fameux UnicodeDecodeError)

  • Yohann

    Errata:
    concernant le souci d’unicode, il s’avère que je rencontre le même problème avec os.path, cd qui inocente path.py, honte à moi

  • Sam Post author

    Pour tout ceux qui ont un problème avec de l’unicode :

    sametmax.com/lencoding-en-python-une-bonne-fois-pour-toute/

    :-)

  • Raphaël

    Pour info, une autre bibliothèque similaire, pathlib, est incluse dans la la standard library à partir de python 3.4

  • Sam Post author

    Et pathlib n’a pas la moitié des features de path.py, et n’hérite pas de string, rendant son usage moins productif. Path.py est vraiment un meilleur produit que pathlib a tous les niveaux, l’API est plus complète, l’ergonomie est meilleure, il y a plein de petits plus qui ont été pensés par l’usage et non par la théorie. Basiquement pathlib c’est os.path en version objet, alors que path.py est plutôt le request de os.path.

  • Fed

    En tant que fervent disciple du PEP8 et du formatage CamelCase pour les classes, pourquoi écris-tu path() au lieu de Path() ?

    Ça me trouble, même si ça va vite me passer.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.