6.6. Assembler les pièces

A nouveau, tous les dominos sont en place. Nous avons vu comment chaque ligne de code fonctionne. Maintenant prenons un peut de recul pour voir comment tout cela s’assemble.

Exemple 6.21. listDirectory


def listDirectory(directory, fileExtList):                                         1
    "get list of file info objects for files of particular extensions"
    fileList = [os.path.normcase(f)
                for f in os.listdir(directory)]           
    fileList = [os.path.join(directory, f) 
               for f in fileList
                if os.path.splitext(f)[1] in fileExtList]                          2
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):       3
        "get file info class from filename extension"                             
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]        4
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo 5
    return [getFileInfoClass(f)(f) for f in fileList]                              6
1 listDirectory est l’attraction principale de ce module. Elle prend un répertoire (c:\music\_singles\ dans mon cas) et une liste d’extensions intéressantes (comme ['.mp3']) et elle retourne une liste d’instances de classe qui se comportent comme des dictionnaires et qui contiennent des métadonnées concernant chaque fichier intéressant de ce répertoire. Et elle le fait en une poignée de ligne simples et directes.
2 Comme nous l’avons vu dans la section précédente, cette ligne de code permet d’obtenir une liste de noms de chemin complets de tous les fichiers de directory qui ont une extension de fichier intéressante (comme spécifiée par fileExtList).
3 Les programmeurs Pascal à l’ancienne les connaissent bien, mais la plupart des gens me jettent un regard vide quand je leur dit que Python supporte les fonctions imbriquées -- littéralement une fonction à l’intérieur d’une fonction. La fonction imbriquée getFileInfoClass peut seulement être appelée de la fonction dans laquelle elle est définie, listDirectory. Comme pour toute autre fonction, vous n’avez pas besoin d’une déclaration d’interface ou de quoi que ce soit d’autre, définissez juste la fonction et écrivez-la.
4 Maintenant que vous avez vu le module os, cette ligne devrait être plus compréhensible. Elle obtient l’extension du fichier (os.path.splitext(filename)[1]), la force en majuscules (.upper()), découpe le point ([1:]) et construit un nom de classe en formatant la chaîne. Donc, c:\music\ap\mahadeva.mp3 devient .mp3, puis .MP3, puis MP3 et enfin MP3FileInfo.
5 Ayant construit le nom de la classe qui doit manipuler ce fichier, nous vérifions si cette classe existe dans ce module. Si c’est le cas, nous retournons la classe, sinon, nous retournons la classe de base, FileInfo. C’est un point très important : cette fonction retourne une classe. Pas une instance de classe, mais la classe elle-même.
6 Pour chaque fichier dans notre liste de «fichiers intéressants» (fileList), nous appelons getFileInfoClass avec le nom de fichier (f). Appeler getFileInfoClass(f) retourne une classe, nous ne savons pas exactement laquelle mais cela ne nous intéresse pas. Nous créons alors une instance de cette classe (quelle qu’elle soit) et passons le nom du fichier (encore f), à la méthode __init__. Comme nous l’avons vu auparavant dans ce chapitre, la méthode __init__ de FileInfo définit self["name"], ce qui déclenche __setitem__, qui est redéfini dans la classe descendante (MP3FileInfo) comme une fonction traitant le fichier de manière à en extraire les métadonnées. Nous faisons cela pour tous les fichiers intéressants et retournons une liste des instances ainsi créées.

Notez que listDirectory est complètement générique. Il ne sait pas à l’avance quels types de fichiers iI va obtenir, ou quelles sont les classes qui pourraient triter ces fichiers. Il inspecte le répertoire à la recherche de fichiers à traiter, puis recourt à l’introspection sur son propre module pour voir quelles classes de traitement (comme MP3FileInfo) sont définies. Vous pouvez étendre ce programme pour gérer d’autres types de fichiers simplement en définissant une classe portant un nom approprié : HTMLFileInfo pour les fichiers HTML, DOCFileInfo pour les fichiers .doc de Word, etc. listDirectory les prendra tous en charge, sans modification, en se déchargeant du traitement proprement dit sur les classes appropriées et en assemblant les résultats.