usbThread.py
Aller à la documentation de ce fichier.
# $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $
licenceEn="""
file usbThread.py
this file is part of the project scolasync
Copyright (C) 2010-2012 Georges Khaznadar <georgesk@ofset.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import subprocess, threading, re, os, os.path, shutil
import time, glob, shlex, io
from PyQt5.QtCore import *
_threadNumber=0
def ensureDirExists(destpath):
os.path.isdir(destpath) or os.makedirs(destpath, mode=0o755)
return
class ThreadRegister:
def __init__(self):
self.dico={}
def __str__(self):
return "ThreadRegister: %s" %self.dico
def push(self, ud, thread):
if ud.getOwner() not in self.dico.keys():
self.dico[ud.getOwner()]=[thread]
else:
self.dico[ud.getOwner()].append(thread)
def pop(self, ud, thread):
self.dico[ud.getOwner()].remove(thread)
def busy(self, owner):
if owner in self.dico.keys():
return self.dico[owner]
return []
def threadSet(self):
result=set()
for o in self.dico.keys():
for t in self.dico[o]:
result.add(t)
return result
def _sanitizePath(path):
pattern=re.compile(".*([^/]+)")
m=pattern.match(str(path))
if m:
return m.group(1)
else:
return str(path).replace('/','_')
# fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
# suivi d'une chaîne relative à la clé USB
# @param ud une instance de uDisk
# @return un nom de thread unique
#
#
# @protected
def _threadName(ud):
global _threadNumber
if hasattr(ud, "path"):
name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
else:
name="th_%04d_%s" %(_threadNumber,"dummy")
_threadNumber+=1
return name
def _date():
return time.strftime("%Y/%m/%d-%H:%M:%S")
# Une classe abstraite, qui sert de creuset pour les classe servant
# aux copies et aux effacements.
#
# Les classes filles doivent redéfinir la méthode \b toDo : c'est celle qui
# est démarrée quand le thread est lancé. Cette méthode est appelée dans
# le contexte « \b with ud.rlock », qui évite que deux threads en même temps
# ne cherchent à accéder au même média.
#
# Une méthode \b copytree est définie pour remplacer shutils.copytree
# qui ne fait pas tout à fait l'affaire.
#
class abstractThreadUSB(threading.Thread):
# Constructeur
# Crée un thread pour copier une liste de fichiers vers une clé USB.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à traiter
# @param subdir un sous-répertoire de la clé USB
# @param dest un répertoire de destination si nécessaire, None par défaut
# @param logfile un fichier de journalisation, /dev/null par défaut
# @param parent un widget qui recevra de signaux en début et en fin
# d'exécution
#
def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null",
parent=None):
threading.Thread.__init__(self, name=_threadName(ud))
self._args=(ud, fileList, subdir, dest, logfile)
self.ud=ud
if hasattr(ud,"threadRunning"): ud.threadRunning=True
self.fileList=fileList
self.subdir=subdir
self.dest=dest
self.logfile=logfile
self.parent=parent
def run(self):
with self.ud.rlock:
self.toDo(*self._args)
def writeToLog(self, msg):
open(os.path.expanduser(self.logfile),"a").write(msg+"\n")
return
# Une version modifiée de shutil.copytree qui accepte que les
# repertoires destination soient déjà existants. Cette source dérive
# de la documentation fournie avec Python 2.7
# @param src un nom de fichier ou de répertoire
# @param dst un nom de de répertoire (déjà existant ou à créer)
# @param symlinks vrai si on veut recopier les liens tels quels
# @param ignore une fonction qui construit une liste de fichiers à ignorer (profil : répertoire, liste de noms de fichiers -> liste de noms de fichiers à ignorer)
# @param erase s'il est vrai la source est effacée après copie réussie
# @param errors la liste d'erreurs déjà relevées jusque là
# @return une liste d'erreurs éventuellement relevées, sinon une liste vide
#
def copytree(self,src, dst, symlinks=False, ignore=None, erase=False, errors=[]):
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
try:
os.makedirs(dst)
except OSError as err:
pass
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
if not errors and erase:
os.unlink(srcname)
elif os.path.isdir(srcname):
errors=self.copytree(srcname, dstname,
symlinks=symlinks, ignore=ignore,
erase=erase, errors=errors)
if not errors and erase:
os.rmdir(srcname)
else:
shutil.copy2(srcname, dstname)
if not errors and erase:
os.unlink(srcname)
# XXX What about devices, sockets etc.?
except IOError as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except os.error as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except Exception as err:
errors.extend(err.args[0])
return errors
def __str__(self):
result="%s(\n" %self.threadType()
result+=" ud = %s\n" %self.ud
result+=" fileList = %s\n" %self.fileList
result+=" subdir = %s\n" %self.subdir
result+=" dest = %s\n" %self.dest
result+=" logfile = %s\n" %self.logfile
result+="\n"
return result
def threadType(self):
return "abstractThreadUSB"
def toDo(self, ud, fileList, subdir, dest, logfile):
# ça ne fait rien du tout pour un thread abstrait
pass
class threadCopyToUSB(abstractThreadUSB):
# Constructeur
# Crée un thread pour copier une liste de fichiers vers une clé USB.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param subdir le sous-répertoire de la clé USB où faire la copie
# @param logfile un fichier de journalisation, /dev/null par défaut
# @param parent un widget qui recevra de signaux en début et en fin
# d'exécution
#
def __init__(self,ud, fileList, subdir, logfile="/dev/null",
parent=None):
abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile, parent=parent)
def threadType(self):
return "threadCopyToUSB"
# Copie une liste de fichiers vers une clé USB sous un répertoire donné.
# Ce répertoire est composé de ud.visibleDir() joint au
# sous-répertoire subdir.
# À chaque fichier ou répertoire copié, une ligne est journalisée dans le
# fichier de journal de l'application.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param logfile un fichier de journalisation
# @param subdir le sous-répertoire de la clé USB où faire la copie
#
def toDo(self, ud, fileList, subdir, dest, logfile):
while subdir[0]=='/':
subdir=subdir[1:]
destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
ensureDirExists(destpath)
# boucle de copie
for f in fileList:
cmd="Copie de {0} vers {1}".format(f, destpath)
if self.parent:
self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
destpath1=os.path.join(destpath, os.path.basename(f))
# copie d'arbre si on copie un répertoire, ou de simple fichier
if os.path.isdir(f):
errors=self.copytree(f, destpath1)
else:
errors=[]
try:
shutil.copy2(f, destpath1)
except Exception as err:
errors.append([f, destpath1, str(err)])
msg="[%s] " %_date()
if not errors:
msg+="Success: "
else:
msg+="Error: "
msg+=cmd
for e in errors:
msg+= " <%s>" %str(e)
if self.parent:
self.parent.popCmdSignal.emit(ud.getOwner(), cmd)
self.writeToLog(msg)
class threadCopyFromUSB(abstractThreadUSB):
# Constructeur
# Crée un thread pour copier une liste de fichiers depuis une clé USB
# vers un répertoire de disque.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param subdir le sous-répertoire de la clé USB d'où faire la copie
# @param dest un répertoire de destination
# @param logfile un fichier de journalisation, /dev/null par défaut
# @param parent un widget qui recevra de signaux en début et en fin
# d'exécution
#
def __init__(self,ud, fileList, subdir=".", dest="/tmp",
rootPath="/", logfile="/dev/null", parent=None):
abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
logfile=logfile, parent=parent)
self.rootPath=rootPath
# Copie une liste de fichiers d'une clé USB sous un répertoire donné.
# À chaque fichier ou répertoire copié, une ligne est journalisée
# dans le fichier de journal de l'application.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier, qui peut contenir des jokers
# @param dest un répertoire de destination
# @param logfile un fichier de journalisation
# @param subdir le sous-répertoire de la clé USB où faire la copie
#
def toDo(self, ud, fileList, subdir, dest, logfile):
for f in fileList:
fromPath=os.path.join(ud.ensureMounted(), f)
owner=ud.getOwner()
newName="%s_%s" %(owner,os.path.dirname(f))
toPath=os.path.join(dest,newName)
# crée le répertoire cible si nécessaire
ensureDirExists(toPath)
cmd="Copie de {0} vers {1}".format(fromPath, toPath)
if self.parent:
self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
destpath1=os.path.join(toPath, os.path.basename(f))
if os.path.isdir(fromPath):
errors=self.copytree(fromPath, destpath1)
else:
errors=[]
try:
shutil.copy2(fromPath, destpath1)
except Exception as err:
errors.extend((fromPath, destpath1, str(err)))
msg="[%s] " %_date()
if not errors:
msg += "Success: "
else:
msg += "Error: "
msg += cmd
for e in errors:
msg += " <%s>" %e
if self.parent:
self.parent.popCmdSignal.emit(ud.getOwner(), msg)
self.writeToLog(msg)
class threadMoveFromUSB(abstractThreadUSB):
# Constructeur
# Crée un thread pour déplacer une liste de fichiers depuis une clé USB
# vers un répertoire de disque.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param subdir le sous-répertoire de la clé USB d'où faire la copie
# @param dest un répertoire de destination
# @param logfile un fichier de journalisation, /dev/null par défaut
# @param parent un widget qui recevra de signaux en début et en fin
# d'exécution
#
def __init__(self,ud, fileList, subdir=".", dest="/tmp",
rootPath="/", logfile="/dev/null", parent=None):
abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
logfile=logfile, parent=parent)
self.rootPath=rootPath
# Copie une liste de fichiers d'une clé USB sous un répertoire donné.
# Après chaque copie réussie la source est effacée.
# À chaque fichier ou répertoire copié, une ligne est journalisée
# dans le fichier de journal de l'application.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param dest un répertoire de destination
# @param logfile un fichier de journalisation
# @param subdir le sous-répertoire de la clé USB où faire la copie
#
def toDo(self, ud, fileList, subdir, dest, logfile):
for f in fileList:
fromPath=os.path.join(ud.ensureMounted(), f)
owner=ud.getOwner()
newName="%s_%s" %(owner,os.path.dirname(f))
toPath=os.path.join(dest,newName)
# crée le répertoire cible si nécessaire
ensureDirExists(toPath)
cmd="copying %s to %s" %(fromPath, toPath)
if self.parent:
self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
destpath1=os.path.join(toPath, os.path.basename(f))
if os.path.isdir(fromPath):
errors=self.copytree(fromPath, destpath1, erase=True)
try:
os.rmdir(fromPath)
except Exception as err:
errors.extend((fromPath, destpath1, str(err)))
else:
errors=[]
try:
shutil.copy2(fromPath, destpath1)
os.unlink(fromPath)
except Exception as err:
errors.extend((fromPath, destpath1, str(err)))
msg="[%s] " %_date()
if not errors:
msg += "Success: "
else:
msg += "Error: "
msg += cmd
for e in errors:
msg += " <%s>" %e
if self.parent:
self.parent.popCmdSignal.emit(ud.getOwner(), msg)
self.writeToLog(msg)
class threadDeleteInUSB(abstractThreadUSB):
# Constructeur
# Crée un thread pour supprimer une liste de fichiers dans une clé USB.
# @param ud l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à supprimer
# @param subdir le sous-répertoire de la clé USB où faire les suppressions
# @param logfile un fichier de journalisation, /dev/null par défaut
# @param parent un widget qui recevra de signaux en début et en fin
# d'exécution
#
def __init__(self,ud, fileList, subdir, logfile="/dev/null",
parent=None):
abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None,
logfile=logfile, parent=parent)
# Supprime une liste de fichiers dans une clé USB.
# La liste est prise sous un répertoire donné. Le répertoire visible
# qui dépend du constructuer d ela clé est pris en compte.
# À chaque fichier ou répertoire supprimé, une ligne est
# journalisée dans le fichier de journal de l'application.
# @param l'instance uDisk correspondant à une partition de clé USB
# @param fileList la liste des fichiers à copier
# @param dest un répertoire de destination
# @param logfile un fichier de journalisation
# @param subdir le sous-répertoire de la clé USB où faire la copie
#
def toDo(self, ud, fileList, subdir, dest, logfile):
for f in fileList:
toDel=os.path.join(ud.ensureMounted(), f)
cmd="Effacement de {0}".format(toDel)
errors=[]
if self.parent:
self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
if os.path.isdir(toDel):
try:
for root, dirs, files in os.walk(toDel, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(toDel)
except Exception as err:
errors.expand((toDel,str(err)))
else:
try:
os.unlink(toDel)
except Exception as err:
errors.expand((toDel,str(err)))
msg="[%s] " %_date()
if not errors:
msg += "Success: "
else:
msg += "Error: "
msg += cmd
for e in errors:
msg += " <%s>" %e
if self.parent:
self.parent.popCmdSignal.emit(ud.getOwner(), msg)
self.writeToLog(msg)
if __name__=="__main__":
import sys, ownedUsbDisk, subprocess
def test_copytree():
t=abstractThreadUSB(None, sys.argv[1:-1], sys.argv[-1])
if len(sys.argv) < 3:
print("Usage : %s répertoire_source répertoire_destination" %sys.argv[0])
print("Ça doit créer sous répertoire_destination la même arborescence que sous répertoire_source")
print("et ça crée répertoire_destination à la volée si nécessaire.")
sys.exit(-1)
errors=t.copytree(sys.argv[1],sys.argv[2])
print("Erreurs = %s" %errors)
subprocess.call ("diff -ruN %s %s" %(sys.argv[1],sys.argv[2]), shell=True)
print ("Ne pas oublier d'effacer %s si nécessaire" %sys.argv[2])
# Teste la copie d'un fichier vers une destination telle qu'elle est pratiquée
# dans la méthode copytree de abstractThreadUSB
#
def test_copy2():
if len(sys.argv) < 3:
print("Usage : %s fichier répertoire_destination" %sys.argv[0])
print("Ça doit créer sous répertoire_destination une copie du fichier")
print("et ça crée répertoire_destination à la volée si nécessaire.")
sys.exit(-1)
srcname=sys.argv[1]
dstname=os.path.join(sys.argv[2],sys.argv[1])
shutil.copy2(srcname, dstname)
print ("fin de la copie de %s vers %s, listing de %s" %(sys.argv[1],sys.argv[2],sys.argv[2]))
subprocess.call("ls %s" %sys.argv[2], shell=True)
#test_copytree()
test_copy2()
src.usbThread.abstractThreadUSB.__init__
def __init__(self, ud, fileList, subdir, dest=None, logfile="/dev/null", parent=None)
Definition: usbThread.py:159
src.usbThread.abstractThreadUSB.dest
dest
Definition: usbThread.py:166
src.usbThread.abstractThreadUSB.writeToLog
def writeToLog(self, msg)
Écrit un message dans le fichier de journalisation.
Definition: usbThread.py:179
src.usbThread.abstractThreadUSB.ud
ud
Definition: usbThread.py:162
src.usbThread.threadMoveFromUSB.toDo
def toDo(self, ud, fileList, subdir, dest, logfile)
La fonction abstraite pour les choses à faire.
Definition: usbThread.py:443
src.usbThread.abstractThreadUSB.logfile
logfile
Definition: usbThread.py:167
src.usbThread.ThreadRegister.pop
def pop(self, ud, thread)
Definition: usbThread.py:70
src.usbThread.threadCopyToUSB.__init__
def __init__(self, ud, fileList, subdir, logfile="/dev/null", parent=None)
Definition: usbThread.py:288
src.usbThread.abstractThreadUSB.copytree
def copytree(self, src, dst, symlinks=False, ignore=None, erase=False, errors=[])
Definition: usbThread.py:195
src.usbThread.abstractThreadUSB.__str__
def __str__(self)
Renvoie une chaîne informative sur le thread.
Definition: usbThread.py:245
src.usbThread.abstractThreadUSB
Definition: usbThread.py:147
src.usbThread.abstractThreadUSB.toDo
def toDo(self, ud, fileList, subdir, dest, logfile)
La fonction abstraite pour les choses à faire.
Definition: usbThread.py:270
src.usbThread.threadCopyFromUSB
Classe pour les threads copiant depuis les clés USB.
Definition: usbThread.py:345
src.usbThread.abstractThreadUSB.run
def run(self)
Definition: usbThread.py:171
src.usbThread.threadCopyFromUSB.rootPath
rootPath
Definition: usbThread.py:361
src.usbThread.ThreadRegister.busy
def busy(self, owner)
Indique si le disque est occupé par des threads.
Definition: usbThread.py:78
src.usbThread.ThreadRegister.threadSet
def threadSet(self)
renvoie l'ensemble des threads actifs
Definition: usbThread.py:86
src.usbThread.ensureDirExists
def ensureDirExists(destpath)
force l'existence d'un répertoire, récursivement si nécessaire
Definition: usbThread.py:36
src.usbThread.test_copytree
def test_copytree()
Teste la fonction copytree.
Definition: usbThread.py:553
src.usbThread.ThreadRegister.__str__
def __str__(self)
Definition: usbThread.py:51
src.usbThread.abstractThreadUSB._args
_args
Definition: usbThread.py:161
src.usbThread.threadMoveFromUSB
Classe pour les threads déplaçant des fichiers depuis les clés USB.
Definition: usbThread.py:413
src.usbThread.abstractThreadUSB.threadType
def threadType(self)
information sur le thread.
Definition: usbThread.py:259
src.usbThread.ThreadRegister.__init__
def __init__(self)
Le constructure met en place un dictionnaire.
Definition: usbThread.py:48
src.usbThread.threadCopyFromUSB.toDo
def toDo(self, ud, fileList, subdir, dest, logfile)
La fonction abstraite pour les choses à faire.
Definition: usbThread.py:374
src.usbThread.ThreadRegister.dico
dico
Definition: usbThread.py:49
src.usbThread.abstractThreadUSB.parent
parent
Definition: usbThread.py:168
src.usbThread.abstractThreadUSB.fileList
fileList
Definition: usbThread.py:164
src.usbThread.threadCopyToUSB.toDo
def toDo(self, ud, fileList, subdir, dest, logfile)
La fonction abstraite pour les choses à faire.
Definition: usbThread.py:309
QtCore
src.usbThread.threadMoveFromUSB.__init__
def __init__(self, ud, fileList, subdir=".", dest="/tmp", rootPath="/", logfile="/dev/null", parent=None)
Definition: usbThread.py:426
src.usbThread.threadMoveFromUSB.rootPath
rootPath
Definition: usbThread.py:429
src.usbThread.test_copy2
def test_copy2()
Definition: usbThread.py:569
src.usbThread.threadDeleteInUSB.__init__
def __init__(self, ud, fileList, subdir, logfile="/dev/null", parent=None)
Definition: usbThread.py:498
src.usbThread.abstractThreadUSB.subdir
subdir
Definition: usbThread.py:165
src.usbThread.threadDeleteInUSB.toDo
def toDo(self, ud, fileList, subdir, dest, logfile)
La fonction abstraite pour les choses à faire.
Definition: usbThread.py:515
src.usbThread.threadCopyFromUSB.__init__
def __init__(self, ud, fileList, subdir=".", dest="/tmp", rootPath="/", logfile="/dev/null", parent=None)
Definition: usbThread.py:358
src.usbThread.ThreadRegister
Une classe pour tenir un registre des threads concernant les baladeurs.
Definition: usbThread.py:43
src.usbThread.ThreadRegister.push
def push(self, ud, thread)
Definition: usbThread.py:59
src.usbThread.threadCopyToUSB.threadType
def threadType(self)
Definition: usbThread.py:295
src.usbThread.threadDeleteInUSB
Classe pour les threads effaçant des sous-arbres dans les clés USB.
Definition: usbThread.py:487