Salut à tous
Mon titre, aux airs érotique, cache un problème python bien concret, surlequel je vous propose de vous penchez.
Je partage avec vous une problématique, qui est relativement simple à exprimer, mais dont la recherche d'une solution est un vrai sac de noeuds. Je suis tout de même parvenu à une solution, que je vous donne pour avoir vos avis, car je la trouve quand même super compliquée. Mais tout d'abord la problématique !
Le besoin est le suivant. J'ai une variable stockant un chemin vers un dossier. Ce dossier peut ne pas exister. S'il existe alors on a rien à faire et il n'y a pas de souci. Mettons nous donc dans le cas où il n'existe pas. Dans ce cas, je souhaite pouvoir définir ce chemin de telle sorte que le dossier se créé à la première utilisation quelconque de ce chemin.
Exemple :
La première ligne c'est moi qui la définie, et les suivantes c'est mon utilisateur qui peut utiliser x (ou non). Un contournement du problème serait de créér le répertoire tout le temps, meme s'il n'est pas utilisé, mais je ne veux pas imposer ca à mon utilisateur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 x = 'un_chemin_qui_n_existe_pas' with open(os.path.join(x, 'mon_fichier.txt')) as f : ### Il faudrait ici que ca cree le repertoire de maniere discrete ....
J'ai donc pensé à faire une classe, qui se comporte comme une string, et qui gererait la creation du repertoire.
Comme c'est moi qui défini la variable (x ici), je peux modifier cette ligne :
et je considère mon utilisateur responsable, c'est à dire qu'écrire print(x), c'est utiliser x, et donc ça induit de créer le répertoire x s'il n'existe pas (meme si mon utilisateur n'écrit ensuite rien dans ce répertoire).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 x = PotentialFolder( 'un_chemin_qui_n_existe_pas', .... eventuellement d'autres arguments ... ) with open(os.path.join(x, 'mon_fichier.txt')) as f : ### Creation du repertoire de maniere discrete ....
Première idée, utiliser une UserString, intercepter le premier appel à __getattribute__, et faire la création du dossier là.
Problème : Si mon utilisateur fait un os.path.join(x, 'mon_fichier.txt'), et bien x n'est pas une instance de str (puisque UserString en fait un proxy, qui va stocker la str originelle dans un attribut data). Donc os.path.join renvoit une erreur...
Pour parer cette erreur, héritons alors de UserString et de str. Après quelques ajustements ca donne cela :
Noter la présence d'un argument supplémentaire, qu'il me faut définir par défaut à False. Car os.path.join recréé des instance des objets qui lui sont passés ! (Pourquoi, je n'en sais rien, mais un print dans la méthode __init__ me l'a bien démontré.)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 import os from collections import UserString class PotentialFolder(UserString, str): def __new__(cls, s, create_folder_if_needed=False): obj=str.__new__(cls, s) return obj def __init__(self, s, create_folder_if_needed=False): UserString.__init__(self, s) self.create_folder_if_needed = create_folder_if_needed self.folder_exist = os.path.exists(s) def __getattribute__(self, attrname): if attrname == 'data' : if not self.folder_exist and self.create_folder_if_needed : self.folder_exist = True ### A laisser au début, sinon recursion infinie ! # os.makedirs(self.data) ### A décommenter si on veut vraiment creer le dossier print('Creation of folder: %s'%self.data) ### /!\ self.data call __getattribute__ return super().__getattribute__(attrname) x=PotentialFolder(r'./toto', create_folder_if_needed=True) print(x) ### Cree x print(os.path.join(x,'tata')) y=PotentialFolder(r'./titi', create_folder_if_needed=True) print(os.path.join(y,'le_piaf.txt'))## Cree y
J'ai mis également un moment à identifier qu'il fallait que je place str.__new__ dans le __new__, et un UserString.__init__ dans le __init__. Je ne comprends pas pourquoi. Et ne pas utiliser super(), ni ne faire que les 2 init dans l'__init__.
Si qqn peut apporter des explications et/ou un code alternatif je suis preneur. Car bien que cette solution semble fonctionner, j'avoue ne pas entièrement comprendre tout ce qui est fait (par Python) dans ce que j'ai moi même écrit !
PS : Attention, si vous tester vos alternatives, vous pouvez rapidement arriver à des problèmes de récursion infinie, qui ont la fâcheuse tendance de vous bloquer python (ou votre IDE) et de vous obligez à killer le process !
Partager