ScolaSync 1.0
usbThread.py
Aller à la documentation de ce fichier.
00001 # -*- coding: utf-8 -*-    
00002 # $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $  
00003 
00004 licenceEn="""
00005     file usbThread.py
00006     this file is part of the project scolasync
00007     
00008     Copyright (C) 2010 Georges Khaznadar <georgesk@ofset.org>
00009 
00010     This program is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation, either version3 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019 
00020     You should have received a copy of the GNU General Public License
00021     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 """
00023 
00024 import subprocess, threading, re, os.path, time, glob, shlex
00025 from PyQt4.QtCore import *
00026 
00027 _threadNumber=0
00028 
00029 ##
00030 # 
00031 #     Une classe pour tenir un registre des threads concernant les baladeurs.
00032 #     
00033 class ThreadRegister:
00034 
00035     ##
00036     # 
00037     #         Le constructure met en place un dictionnaire
00038     #         
00039     def __init__(self):
00040         self.dico={}
00041 
00042     def __str__(self):
00043         return "ThreadRegister: %s" %self.dico
00044         
00045     ##
00046     # 
00047     #         @param ud un disque
00048     #         @param thread un thread
00049     #         Empile un thread pour le baladeur ud
00050     #         
00051     def push(self, ud, thread):
00052         if ud.owner not in self.dico.keys():
00053             self.dico[ud.owner]=[thread]
00054         else:
00055             self.dico[ud.owner].append(thread)
00056 
00057     ##
00058     # 
00059     #         @param ud un disque
00060     #         @param thread un thread
00061     #         Dépile un thread pour le baladeur ud
00062     #         
00063     def pop(self, ud, thread):
00064         self.dico[ud.owner].remove(thread)
00065 
00066     ##
00067     # 
00068     #         Indique si le disque est occupé par des threads
00069     #         @param owner le propriétaire du disque
00070     #         @return les données associées par le dictionnaire
00071     #         
00072     def busy(self, owner):
00073         if owner in self.dico.keys():
00074             return self.dico[owner]
00075         return []
00076 
00077     ##
00078     # 
00079     #         renvoie l'ensemble des threads actifs
00080     #         
00081     def threadSet(self):
00082         result=set()
00083         for o in self.dico.keys():
00084             for t in self.dico[o]:
00085                 result.add(t)
00086         return result
00087             
00088 ##
00089 # 
00090 #     Évite d'avoir des <i>slashes</i> dans un nom de thread
00091 #     @return la fin du nom de chemin, après le dernier <i>slash</i> ;
00092 #     si le chemin ne finit pas bien, remplace les <i>slashes</i> par
00093 #     des sous-tirets "_".
00094 #     
00095 def _sanitizePath(path):
00096     pattern=re.compile(".*([^/]+)")
00097     m=pattern.match(str(path))
00098     if m:
00099         return m.group(1)
00100     else:
00101         return str(path).replace('/','_')
00102 
00103 ##
00104 # 
00105 #     fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
00106 #     suivi d'une chaîne relative à la clé USB
00107 #     @param ud une instance de uDisk
00108 #     @return un nom de thread unique
00109 #     
00110 def _threadName(ud):
00111     global _threadNumber
00112     name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
00113     _threadNumber+=1
00114     return name
00115 
00116 ##
00117 # 
00118 #     Renvoie la date et l'heure dans un format court
00119 #     @return une chaîne donnée par strftime et le format %Y/%m/%d-%H:%M:%S
00120 #     
00121 def _date():
00122     return time.strftime("%Y/%m/%d-%H:%M:%S")
00123 
00124 ##
00125 # 
00126 #     Lance une commande dans un shell, et journalise la réussite ou l'échec
00127 #     @param cmd la commande shell, si cette commande contient des points-virgules elle sera découpée en plusieurs sous-commandes, chacune traitée séparément.
00128 #     @param logfile le fichier de journalisation
00129 #     @param ud le uDisk concerné
00130 #     @param parent un widget qui recevra des signaux de début et de fin
00131 #        de commande. Par defaut: None.
00132 #     
00133 def _call(cmd, logfile, ud, parent=None):
00134     if parent:
00135         parent.emit(SIGNAL("pushCmd(QString, QString)"), ud.owner, cmd)
00136     for command in cmd.split(";"):
00137         retcode=subprocess.call(command, shell=True)
00138         msg="[%s] " %_date()
00139         if retcode==0:
00140             msg += "Success: "
00141         else:
00142             msg += "Error:   "
00143         msg += command
00144     if parent:
00145         parent.emit(SIGNAL("popCmd(QString, QString)"), ud.owner, cmd+msg)
00146 
00147 ##
00148 # 
00149 #     Une classe abstraite
00150 #      Cette classe sert de creuset pour les classe servant aux copies
00151 #      et aux effacement.
00152 #     
00153 class abstractThreadUSB(threading.Thread):
00154     ##
00155     # 
00156     #         Constructeur
00157     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00158     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00159     #         @param fileList la liste des fichiers à traiter
00160     #         @param subdir un sous-répertoire de la clé USB
00161     #         @param dest un répertoire de destination si nécessaire, None par défaut
00162     #         @param logfile un fichier de journalisation, /dev/null par défaut
00163     #         @param parent un widget qui recevra de signaux en début et en fin
00164     #           d'exécution
00165     #         
00166     def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null",
00167                  parent=None):
00168         threading.Thread.__init__(self,target=self.toDo,
00169                                   args=(ud, fileList, subdir, dest, logfile),
00170                                   name=_threadName(ud))        
00171         self.cmd=u"echo This is an abstract method, don't call it"
00172         self.ud=ud
00173         ud.threadRunning=True
00174         self.fileList=fileList
00175         self.subdir=subdir
00176         self.dest=dest
00177         self.logfile=logfile
00178         self.parent=parent
00179 
00180     ##
00181     # 
00182     #         Renvoie une chaîne informative sur le thread
00183     #         @return une chaine donnant des informations sur ce qui va
00184     #         se passer dans le thread qui a été créé.
00185     #         
00186     def __str__(self):
00187         result="%s(\n" %self.threadType()
00188         result+="  ud       = %s\n" %self.ud
00189         result+="  fileList = %s\n" %self.fileList
00190         result+="  subdir   = %s\n" %self.subdir
00191         result+="  dest     = %s\n" %self.dest
00192         result+="  logfile  = %s\n" %self.logfile
00193         result+="  cmd      = %s\n" %self.cmd
00194         result+="\n"
00195         return result
00196 
00197     ##
00198     # 
00199     #         @return une chaîne courte qui informe sur le type de thread
00200     #         
00201     def threadType(self):
00202         return "abstractThreadUSB"
00203 
00204     ##
00205     # 
00206     #         La fonction abstraite pour les choses à faire
00207     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00208     #         @param fileList la liste des fichiers à traiter
00209     #         @param subdir un sous-répertoire de la clé USB
00210     #         @param dest un répertoire de destination
00211     #         @param logfile un fichier de journalisation
00212     #         
00213     def toDo(self, ud, fileList, subdir, dest, logfile):
00214         # ça ne fait rien du tout pour un thread abstrait
00215         pass
00216 
00217     ##
00218     # 
00219     #         protège une chaine unicode par des guillemets (pour un shell POSIX)
00220     #         à moins que celle-ci contienne des jokers.
00221     #         @param s la chaine d'entrée
00222     #         @return la même, protégée si nécessaire
00223     #         
00224     def protectPath(self, s):
00225         if "*" in s or "?" in s:
00226             return s
00227         else:
00228             return u'"%s"' %s
00229     
00230 ##
00231 # 
00232 #     Classe pour les threads copiant vers les clés USB
00233 #     
00234 class threadCopyToUSB(abstractThreadUSB):
00235     ##
00236     # 
00237     #         Constructeur
00238     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00239     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00240     #         @param fileList la liste des fichiers à copier
00241     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00242     #         @param logfile un fichier de journalisation, /dev/null par défaut
00243     #         @param parent un widget qui recevra de signaux en début et en fin
00244     #           d'exécution
00245     #         
00246     def __init__(self,ud, fileList, subdir, logfile="/dev/null",
00247                  parent=None):
00248         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile, parent=parent)
00249         self.cmd=u'mkdir -p "{toDir}"; cp -R {fromFile} "{toDir}"'
00250 
00251     ##
00252     # 
00253     #         @return une chaîne courte qui informe sur le type de thread
00254     #         
00255     def threadType(self):
00256         return "threadCopyToUSB"
00257 
00258     ##
00259     # 
00260     #         Copie une liste de fichiers vers une clé USB sous un répertoire donné.
00261     #          Ce répertoire est composé de ud.visibleDir() joint au
00262     #          sous-répertoire subdir.
00263     #          À chaque fichier ou répertoire copié, une ligne est journalisée dans le
00264     #          fichier de journal de l'application.
00265     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00266     #         @param fileList la liste des fichiers à copier
00267     #         @param logfile un fichier de journalisation
00268     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00269     #         
00270     def toDo(self, ud, fileList, subdir, dest, logfile):
00271         while subdir[0]=='/':
00272             subdir=subdir[1:]
00273         destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
00274         for f in fileList:
00275             fileName=os.path.basename(f)
00276             cmd=self.cmd.format(fromFile=f,
00277                                 toDir=destpath)
00278             _call(cmd, logfile, ud, self.parent)
00279 
00280 ##
00281 # 
00282 #     Classe pour les threads copiant depuis les clés USB
00283 #     
00284 class threadCopyFromUSB(abstractThreadUSB):
00285     ##
00286     # 
00287     #         Constructeur
00288     #         Crée un thread pour copier une liste de fichiers depuis une clé USB
00289     #         vers un répertoire de disque.
00290     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00291     #         @param fileList la liste des fichiers à copier
00292     #         @param subdir le sous-répertoire de la clé USB d'où faire la copie
00293     #         @param dest un répertoire de destination
00294     #         @param logfile un fichier de journalisation, /dev/null par défaut
00295     #         @param parent un widget qui recevra de signaux en début et en fin
00296     #           d'exécution
00297     #         
00298     def __init__(self,ud, fileList, subdir=".", dest="/tmp",
00299                  rootPath="/", logfile="/dev/null", parent=None):
00300         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
00301                                    logfile=logfile, parent=parent)
00302         self.rootPath=rootPath
00303         self.cmd=u'mkdir -p "{toPath}"; cp -R {fromPath} "{toPath}"'
00304 
00305     ##
00306     # 
00307     #         Copie une liste de fichiers d'une clé USB sous un répertoire donné.
00308     #          À chaque fichier ou répertoire copié, une ligne est journalisée
00309     #          dans le fichier de journal de l'application.
00310     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00311     #         @param fileList la liste des fichiers à copier, qui peut contenir des jokers
00312     #         @param dest un répertoire de destination
00313     #         @param logfile un fichier de journalisation
00314     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00315     #         
00316     def toDo(self, ud, fileList, subdir, dest, logfile):
00317         
00318         for f in fileList:
00319             ## prend le fichier ou le répertoire sur le disque courant
00320             fromPath=self.protectPath(os.path.join(ud.ensureMounted(), f))
00321             owner=ud.ownerByDb()
00322             ## personnalise le nom de la destination
00323             newName=u"%s_%s" %(owner,os.path.dirname(f))
00324             ## calcule le point de copie et le répertoire à créer s'il le faut
00325             toPath=os.path.join(dest,newName)
00326             cmd=self.cmd.format(fromPath=fromPath, toPath=toPath)
00327             _call(cmd,logfile, ud, self.parent)
00328             
00329 ##
00330 # 
00331 #     Classe pour les threads déplaçant des fichiers depuis les clés USB
00332 #     
00333 class threadMoveFromUSB(abstractThreadUSB):
00334     ##
00335     # 
00336     #         Constructeur
00337     #         Crée un thread pour déplacer une liste de fichiers depuis une clé USB
00338     #         vers un répertoire de disque.
00339     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00340     #         @param fileList la liste des fichiers à copier
00341     #         @param subdir le sous-répertoire de la clé USB d'où faire la copie
00342     #         @param dest un répertoire de destination
00343     #         @param logfile un fichier de journalisation, /dev/null par défaut
00344     #         @param parent un widget qui recevra de signaux en début et en fin
00345     #           d'exécution
00346     #         
00347     def __init__(self,ud, fileList, subdir=".", dest="/tmp",
00348                  rootPath="/", logfile="/dev/null", parent=None):
00349         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
00350                                    logfile=logfile, parent=parent)
00351         self.rootPath=rootPath
00352         self.cmd=u'mkdir -p "{toPath}"; cp -R {fromPath} "{toPath}" && rm -rf {fromPath}'
00353 
00354     ##
00355     # 
00356     #         Copie une liste de fichiers d'une clé USB sous un répertoire donné.
00357     #          Après chaque copie réussie la source est effacée.
00358     #          À chaque fichier ou répertoire copié, une ligne est journalisée
00359     #          dans le fichier de journal de l'application.
00360     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00361     #         @param fileList la liste des fichiers à copier
00362     #         @param dest un répertoire de destination
00363     #         @param logfile un fichier de journalisation
00364     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00365     #         
00366     def toDo(self, ud, fileList, subdir, dest, logfile):
00367         for f in fileList:
00368             ## prend le fichier ou le répertoire sur le disque courant
00369             fromPath=self.protectPath(os.path.join(ud.ensureMounted(), f))
00370             owner=ud.ownerByDb()
00371             ## personnalise le nom de la destination
00372             newName=u"%s_%s" %(owner,os.path.dirname(f))
00373             ## calcule le point de copie et le répertoire à créer s'il le faut
00374             toPath=os.path.join(dest,newName)
00375             cmd=self.cmd.format(fromPath=fromPath, toPath=toPath)
00376             _call(cmd,logfile, ud, self.parent)
00377             
00378 ##
00379 # 
00380 #     Classe pour les threads effaçant des sous-arbres dans les clés USB
00381 #     
00382 class threadDeleteInUSB(abstractThreadUSB):
00383     ##
00384     # 
00385     #         Constructeur
00386     #          Crée un thread pour supprimer une liste de fichiers dans une clé USB.
00387     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00388     #         @param fileList la liste des fichiers à supprimer
00389     #         @param subdir le sous-répertoire de la clé USB où faire les suppressions
00390     #         @param logfile un fichier de journalisation, /dev/null par défaut
00391     #         @param parent un widget qui recevra de signaux en début et en fin
00392     #           d'exécution
00393     #         
00394     def __init__(self,ud, fileList, subdir, logfile="/dev/null",
00395                  parent=None):
00396         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None,
00397                                    logfile=logfile, parent=parent)
00398         self.cmd=u'rm -rf {toDel}'
00399 
00400     ##
00401     # 
00402     #         Supprime une liste de fichiers dans une clé USB.
00403     #          La liste est prise sous un répertoire donné. Le répertoire visible
00404     #          qui dépend du constructuer d ela clé est pris en compte.
00405     #          À chaque fichier ou répertoire supprimé, une ligne est
00406     #          journalisée dans le fichier de journal de l'application.
00407     #         @param l'instance uDisk correspondant à une partition de clé USB
00408     #         @param fileList la liste des fichiers à copier
00409     #         @param dest un répertoire de destination
00410     #         @param logfile un fichier de journalisation
00411     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00412     #         
00413     def toDo(self, ud, fileList, subdir, dest, logfile):
00414         for f in fileList:
00415             toDel=os.path.join(ud.ensureMounted(), f)
00416             cmd=self.cmd.format(toDel=toDel)
00417             _call(cmd,logfile, ud, self.parent)
00418 
 Tout Classes Espaces de nommage Fichiers Fonctions Variables