Je me suis mis depuis quelques mois à Python. Il y a énormément de choses très appréciables dans le langage et sa logique. À vrai dire, plus le temps passe, et moins je lui trouve de points faibles. Sûrement le début d’une longue série d’articles, de tous niveaux.

Mais en attendant, je vais ici relater mon premier bout de code un petit peu « tricky ». Python propose un lot de méthodes pour accéder aux répertoires/fichiers : os.listdir, walk… Mais ces méthodes ont le défaut de retourner une liste construite des éléments. Du coup, lorsque le temps de traitement devient critique, on aimerait bien pouvoir itérer directement sur les descripteurs de fichiers sans attendre une liste de potentiellement 10 000 éléments. Bref, la possibilité d’accéder aux fonctions Posix opendir() et readdir().

Le soucis ? Python ne propose pas ces fonctions. Mais il y a la possibilité d’accéder à des API du langage C, et dans un environnement Unix, on y retrouve nos chères fonctions si convoitées ! Après avoir étudié cette piste, voilà un snippet fort utile :

#!/usr/bin/python
from ctypes import CDLL, c_char_p, c_int, c_long, c_ushort, c_byte, c_char
from ctypes import Structure, POINTER
from ctypes.util import find_library

class c_dir(Structure):
    """ Defined C struct DIR """
    pass

class c_dirent(Structure):
    """ Directory entry structure equivalent """
    _fields_ = (
        ('d_ino', c_long),  # inode number
        ('d_off', c_long),  # offset to the next dirent
        ('d_reclen', c_ushort), # length of this record
        ('d_type', c_byte),  # type of files; os specific
        ('d_name', c_char * 4096) # filename
        )

c_dirent_p = POINTER(c_dirent)
c_dir_p = POINTER(c_dir)
c_lib = CDLL(find_library("c"))

opendir = c_lib.opendir
opendir.argtypes = [c_char_p]
opendir.restype = c_dir_p

readdir = c_lib.readdir_r
readdir.argtypes = [c_dir_p]
readdir.restype = c_dirent_p

closedir = c_lib.closedir
closedir.argtypes = [c_dir_p]
closedir.restype = c_int

def listdir(path):
    """
    A generator to return the names of files in the directory passed in
    """
    dir_p = opendir(".")
    try:
        while True:
            p = readdir(dir_p)
            if not p:
                break
            yield p.contents.d_name
    finally:
        closedir(dir_p)

Attention, listdir() retournera les répertoires « . » et « .. » ! Notez l’utilisation du yield pour définir ici un générateur (sujet d’un autre article). De ce fait, notre fonction s’utilise comme cela :

if __name__ == "__main__":
    for name in listdir("."):
        print name

En espérant que ça puisse servir à quelqu’un d’autre…