ScolaSync  5.1
usbThread.py
Aller à la documentation de ce fichier.
1 # $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $
2 
3 licenceEn="""
4  file usbThread.py
5  this file is part of the project scolasync
6 
7  Copyright (C) 2010-2012 Georges Khaznadar <georgesk@ofset.org>
8 
9  This program is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version3 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program. If not, see <http://www.gnu.org/licenses/>.
21 """
22 
23 import subprocess, threading, re, os, os.path, shutil
24 import time, glob, shlex, io
25 from PyQt5.QtCore import *
26 
27 
30 _threadNumber=0
31 
32 
35 
36 def ensureDirExists(destpath):
37  os.path.isdir(destpath) or os.makedirs(destpath, mode=0o755)
38  return
39 
40 
42 
44 
45 
47 
48  def __init__(self):
49  self.dico={}
50 
51  def __str__(self):
52  return "ThreadRegister: %s" %self.dico
53 
54 
58 
59  def push(self, ud, thread):
60  if ud.getOwner() not in self.dico.keys():
61  self.dico[ud.getOwner()]=[thread]
62  else:
63  self.dico[ud.getOwner()].append(thread)
64 
65 
69 
70  def pop(self, ud, thread):
71  self.dico[ud.getOwner()].remove(thread)
72 
73 
77 
78  def busy(self, owner):
79  if owner in self.dico.keys():
80  return self.dico[owner]
81  return []
82 
83 
85 
86  def threadSet(self):
87  result=set()
88  for o in self.dico.keys():
89  for t in self.dico[o]:
90  result.add(t)
91  return result
92 
93 
100 
101 def _sanitizePath(path):
102  pattern=re.compile(".*([^/]+)")
103  m=pattern.match(str(path))
104  if m:
105  return m.group(1)
106  else:
107  return str(path).replace('/','_')
108 
109 # fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
110 # suivi d'une chaîne relative à la clé USB
111 # @param ud une instance de uDisk
112 # @return un nom de thread unique
113 #
114 #
115 # @protected
116 
117 def _threadName(ud):
118  global _threadNumber
119  if hasattr(ud, "path"):
120  name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
121  else:
122  name="th_%04d_%s" %(_threadNumber,"dummy")
123  _threadNumber+=1
124  return name
125 
126 
131 
132 def _date():
133  return time.strftime("%Y/%m/%d-%H:%M:%S")
134 
135 # Une classe abstraite, qui sert de creuset pour les classe servant
136 # aux copies et aux effacements.
137 #
138 # Les classes filles doivent redéfinir la méthode \b toDo : c'est celle qui
139 # est démarrée quand le thread est lancé. Cette méthode est appelée dans
140 # le contexte « \b with ud.rlock », qui évite que deux threads en même temps
141 # ne cherchent à accéder au même média.
142 #
143 # Une méthode \b copytree est définie pour remplacer shutils.copytree
144 # qui ne fait pas tout à fait l'affaire.
145 #
146 
147 class abstractThreadUSB(threading.Thread):
148  # Constructeur
149  # Crée un thread pour copier une liste de fichiers vers une clé USB.
150  # @param ud l'instance uDisk correspondant à une partition de clé USB
151  # @param fileList la liste des fichiers à traiter
152  # @param subdir un sous-répertoire de la clé USB
153  # @param dest un répertoire de destination si nécessaire, None par défaut
154  # @param logfile un fichier de journalisation, /dev/null par défaut
155  # @param parent un widget qui recevra de signaux en début et en fin
156  # d'exécution
157  #
158 
159  def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null",
160  parent=None):
161  threading.Thread.__init__(self, name=_threadName(ud))
162  self._args=(ud, fileList, subdir, dest, logfile)
163  self.ud=ud
164  if hasattr(ud,"threadRunning"): ud.threadRunning=True
165  self.fileList=fileList
166  self.subdir=subdir
167  self.dest=dest
168  self.logfile=logfile
169  self.parent=parent
170 
171  def run(self):
172  with self.ud.rlock:
173  self.toDo(*self._args)
174 
175 
178 
179  def writeToLog(self, msg):
180  open(os.path.expanduser(self.logfile),"a").write(msg+"\n")
181  return
182 
183  # Une version modifiée de shutil.copytree qui accepte que les
184  # repertoires destination soient déjà existants. Cette source dérive
185  # de la documentation fournie avec Python 2.7
186  # @param src un nom de fichier ou de répertoire
187  # @param dst un nom de de répertoire (déjà existant ou à créer)
188  # @param symlinks vrai si on veut recopier les liens tels quels
189  # @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)
190  # @param erase s'il est vrai la source est effacée après copie réussie
191  # @param errors la liste d'erreurs déjà relevées jusque là
192  # @return une liste d'erreurs éventuellement relevées, sinon une liste vide
193  #
194 
195  def copytree(self,src, dst, symlinks=False, ignore=None, erase=False, errors=[]):
196  names = os.listdir(src)
197  if ignore is not None:
198  ignored_names = ignore(src, names)
199  else:
200  ignored_names = set()
201 
202  try:
203  os.makedirs(dst)
204  except OSError as err:
205  pass
206  for name in names:
207  if name in ignored_names:
208  continue
209  srcname = os.path.join(src, name)
210  dstname = os.path.join(dst, name)
211  try:
212  if symlinks and os.path.islink(srcname):
213  linkto = os.readlink(srcname)
214  os.symlink(linkto, dstname)
215  if not errors and erase:
216  os.unlink(srcname)
217  elif os.path.isdir(srcname):
218  errors=self.copytree(srcname, dstname,
219  symlinks=symlinks, ignore=ignore,
220  erase=erase, errors=errors)
221  if not errors and erase:
222  os.rmdir(srcname)
223  else:
224  shutil.copy2(srcname, dstname)
225  if not errors and erase:
226  os.unlink(srcname)
227  # XXX What about devices, sockets etc.?
228  except IOError as why:
229  errors.append((srcname, dstname, str(why)))
230  # catch the Error from the recursive copytree so that we can
231  # continue with other files
232  except os.error as why:
233  errors.append((srcname, dstname, str(why)))
234  # catch the Error from the recursive copytree so that we can
235  # continue with other files
236  except Exception as err:
237  errors.extend(err.args[0])
238  return errors
239 
240 
244 
245  def __str__(self):
246  result="%s(\n" %self.threadType()
247  result+=" ud = %s\n" %self.ud
248  result+=" fileList = %s\n" %self.fileList
249  result+=" subdir = %s\n" %self.subdir
250  result+=" dest = %s\n" %self.dest
251  result+=" logfile = %s\n" %self.logfile
252  result+="\n"
253  return result
254 
255 
258 
259  def threadType(self):
260  return "abstractThreadUSB"
261 
262 
269 
270  def toDo(self, ud, fileList, subdir, dest, logfile):
271  # ça ne fait rien du tout pour un thread abstrait
272  pass
273 
274 
276 
277 class threadCopyToUSB(abstractThreadUSB):
278  # Constructeur
279  # Crée un thread pour copier une liste de fichiers vers une clé USB.
280  # @param ud l'instance uDisk correspondant à une partition de clé USB
281  # @param fileList la liste des fichiers à copier
282  # @param subdir le sous-répertoire de la clé USB où faire la copie
283  # @param logfile un fichier de journalisation, /dev/null par défaut
284  # @param parent un widget qui recevra de signaux en début et en fin
285  # d'exécution
286  #
287 
288  def __init__(self,ud, fileList, subdir, logfile="/dev/null",
289  parent=None):
290  abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile, parent=parent)
291 
292 
294 
295  def threadType(self):
296  return "threadCopyToUSB"
297 
298  # Copie une liste de fichiers vers une clé USB sous un répertoire donné.
299  # Ce répertoire est composé de ud.visibleDir() joint au
300  # sous-répertoire subdir.
301  # À chaque fichier ou répertoire copié, une ligne est journalisée dans le
302  # fichier de journal de l'application.
303  # @param ud l'instance uDisk correspondant à une partition de clé USB
304  # @param fileList la liste des fichiers à copier
305  # @param logfile un fichier de journalisation
306  # @param subdir le sous-répertoire de la clé USB où faire la copie
307  #
308 
309  def toDo(self, ud, fileList, subdir, dest, logfile):
310  while subdir[0]=='/':
311  subdir=subdir[1:]
312  destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
313  ensureDirExists(destpath)
314  # boucle de copie
315  for f in fileList:
316  cmd="Copie de {0} vers {1}".format(f, destpath)
317  if self.parent:
318  self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
319  destpath1=os.path.join(destpath, os.path.basename(f))
320  # copie d'arbre si on copie un répertoire, ou de simple fichier
321  if os.path.isdir(f):
322  errors=self.copytree(f, destpath1)
323  else:
324  errors=[]
325  try:
326  shutil.copy2(f, destpath1)
327  except Exception as err:
328  errors.append([f, destpath1, str(err)])
329 
330  msg="[%s] " %_date()
331  if not errors:
332  msg+="Success: "
333  else:
334  msg+="Error: "
335  msg+=cmd
336  for e in errors:
337  msg+= " <%s>" %str(e)
338  if self.parent:
339  self.parent.popCmdSignal.emit(ud.getOwner(), cmd)
340  self.writeToLog(msg)
341 
342 
344 
346  # Constructeur
347  # Crée un thread pour copier une liste de fichiers depuis une clé USB
348  # vers un répertoire de disque.
349  # @param ud l'instance uDisk correspondant à une partition de clé USB
350  # @param fileList la liste des fichiers à copier
351  # @param subdir le sous-répertoire de la clé USB d'où faire la copie
352  # @param dest un répertoire de destination
353  # @param logfile un fichier de journalisation, /dev/null par défaut
354  # @param parent un widget qui recevra de signaux en début et en fin
355  # d'exécution
356  #
357 
358  def __init__(self,ud, fileList, subdir=".", dest="/tmp",
359  rootPath="/", logfile="/dev/null", parent=None):
360  abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
361  logfile=logfile, parent=parent)
362  self.rootPath=rootPath
363 
364  # Copie une liste de fichiers d'une clé USB sous un répertoire donné.
365  # À chaque fichier ou répertoire copié, une ligne est journalisée
366  # dans le fichier de journal de l'application.
367  # @param ud l'instance uDisk correspondant à une partition de clé USB
368  # @param fileList la liste des fichiers à copier, qui peut contenir des jokers
369  # @param dest un répertoire de destination
370  # @param logfile un fichier de journalisation
371  # @param subdir le sous-répertoire de la clé USB où faire la copie
372  #
373 
374  def toDo(self, ud, fileList, subdir, dest, logfile):
375  for f in fileList:
376 
377  fromPath=os.path.join(ud.ensureMounted(), f)
378  owner=ud.getOwner()
379 
380  newName="%s_%s" %(owner,os.path.dirname(f))
381 
382  toPath=os.path.join(dest,newName)
383  # crée le répertoire cible si nécessaire
384  ensureDirExists(toPath)
385  cmd="Copie de {0} vers {1}".format(fromPath, toPath)
386  if self.parent:
387  self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
388  destpath1=os.path.join(toPath, os.path.basename(f))
389  if os.path.isdir(fromPath):
390  errors=self.copytree(fromPath, destpath1)
391  else:
392  errors=[]
393  try:
394  shutil.copy2(fromPath, destpath1)
395  except Exception as err:
396  errors.extend((fromPath, destpath1, str(err)))
397 
398  msg="[%s] " %_date()
399  if not errors:
400  msg += "Success: "
401  else:
402  msg += "Error: "
403  msg += cmd
404  for e in errors:
405  msg += " <%s>" %e
406  if self.parent:
407  self.parent.popCmdSignal.emit(ud.getOwner(), msg)
408  self.writeToLog(msg)
409 
410 
412 
414  # Constructeur
415  # Crée un thread pour déplacer une liste de fichiers depuis une clé USB
416  # vers un répertoire de disque.
417  # @param ud l'instance uDisk correspondant à une partition de clé USB
418  # @param fileList la liste des fichiers à copier
419  # @param subdir le sous-répertoire de la clé USB d'où faire la copie
420  # @param dest un répertoire de destination
421  # @param logfile un fichier de journalisation, /dev/null par défaut
422  # @param parent un widget qui recevra de signaux en début et en fin
423  # d'exécution
424  #
425 
426  def __init__(self,ud, fileList, subdir=".", dest="/tmp",
427  rootPath="/", logfile="/dev/null", parent=None):
428  abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
429  logfile=logfile, parent=parent)
430  self.rootPath=rootPath
431 
432  # Copie une liste de fichiers d'une clé USB sous un répertoire donné.
433  # Après chaque copie réussie la source est effacée.
434  # À chaque fichier ou répertoire copié, une ligne est journalisée
435  # dans le fichier de journal de l'application.
436  # @param ud l'instance uDisk correspondant à une partition de clé USB
437  # @param fileList la liste des fichiers à copier
438  # @param dest un répertoire de destination
439  # @param logfile un fichier de journalisation
440  # @param subdir le sous-répertoire de la clé USB où faire la copie
441  #
442 
443  def toDo(self, ud, fileList, subdir, dest, logfile):
444  for f in fileList:
445 
446  fromPath=os.path.join(ud.ensureMounted(), f)
447  owner=ud.getOwner()
448 
449  newName="%s_%s" %(owner,os.path.dirname(f))
450 
451  toPath=os.path.join(dest,newName)
452  # crée le répertoire cible si nécessaire
453  ensureDirExists(toPath)
454  cmd="copying %s to %s" %(fromPath, toPath)
455  if self.parent:
456  self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
457  destpath1=os.path.join(toPath, os.path.basename(f))
458  if os.path.isdir(fromPath):
459  errors=self.copytree(fromPath, destpath1, erase=True)
460  try:
461  os.rmdir(fromPath)
462  except Exception as err:
463  errors.extend((fromPath, destpath1, str(err)))
464  else:
465  errors=[]
466  try:
467  shutil.copy2(fromPath, destpath1)
468  os.unlink(fromPath)
469  except Exception as err:
470  errors.extend((fromPath, destpath1, str(err)))
471 
472  msg="[%s] " %_date()
473  if not errors:
474  msg += "Success: "
475  else:
476  msg += "Error: "
477  msg += cmd
478  for e in errors:
479  msg += " <%s>" %e
480  if self.parent:
481  self.parent.popCmdSignal.emit(ud.getOwner(), msg)
482  self.writeToLog(msg)
483 
484 
486 
488  # Constructeur
489  # Crée un thread pour supprimer une liste de fichiers dans une clé USB.
490  # @param ud l'instance uDisk correspondant à une partition de clé USB
491  # @param fileList la liste des fichiers à supprimer
492  # @param subdir le sous-répertoire de la clé USB où faire les suppressions
493  # @param logfile un fichier de journalisation, /dev/null par défaut
494  # @param parent un widget qui recevra de signaux en début et en fin
495  # d'exécution
496  #
497 
498  def __init__(self,ud, fileList, subdir, logfile="/dev/null",
499  parent=None):
500  abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None,
501  logfile=logfile, parent=parent)
502 
503  # Supprime une liste de fichiers dans une clé USB.
504  # La liste est prise sous un répertoire donné. Le répertoire visible
505  # qui dépend du constructuer d ela clé est pris en compte.
506  # À chaque fichier ou répertoire supprimé, une ligne est
507  # journalisée dans le fichier de journal de l'application.
508  # @param l'instance uDisk correspondant à une partition de clé USB
509  # @param fileList la liste des fichiers à copier
510  # @param dest un répertoire de destination
511  # @param logfile un fichier de journalisation
512  # @param subdir le sous-répertoire de la clé USB où faire la copie
513  #
514 
515  def toDo(self, ud, fileList, subdir, dest, logfile):
516  for f in fileList:
517  toDel=os.path.join(ud.ensureMounted(), f)
518  cmd="Effacement de {0}".format(toDel)
519  errors=[]
520  if self.parent:
521  self.parent.pushCmdSignal.emit(ud.getOwner(), cmd)
522  if os.path.isdir(toDel):
523  try:
524  for root, dirs, files in os.walk(toDel, topdown=False):
525  for name in files:
526  os.remove(os.path.join(root, name))
527  for name in dirs:
528  os.rmdir(os.path.join(root, name))
529  os.rmdir(toDel)
530  except Exception as err:
531  errors.expand((toDel,str(err)))
532  else:
533  try:
534  os.unlink(toDel)
535  except Exception as err:
536  errors.expand((toDel,str(err)))
537  msg="[%s] " %_date()
538  if not errors:
539  msg += "Success: "
540  else:
541  msg += "Error: "
542  msg += cmd
543  for e in errors:
544  msg += " <%s>" %e
545  if self.parent:
546  self.parent.popCmdSignal.emit(ud.getOwner(), msg)
547  self.writeToLog(msg)
548 
549 if __name__=="__main__":
550  import sys, ownedUsbDisk, subprocess
551 
552 
554  t=abstractThreadUSB(None, sys.argv[1:-1], sys.argv[-1])
555  if len(sys.argv) < 3:
556  print("Usage : %s répertoire_source répertoire_destination" %sys.argv[0])
557  print("Ça doit créer sous répertoire_destination la même arborescence que sous répertoire_source")
558  print("et ça crée répertoire_destination à la volée si nécessaire.")
559  sys.exit(-1)
560  errors=t.copytree(sys.argv[1],sys.argv[2])
561  print("Erreurs = %s" %errors)
562  subprocess.call ("diff -ruN %s %s" %(sys.argv[1],sys.argv[2]), shell=True)
563  print ("Ne pas oublier d'effacer %s si nécessaire" %sys.argv[2])
564 
565  # Teste la copie d'un fichier vers une destination telle qu'elle est pratiquée
566  # dans la méthode copytree de abstractThreadUSB
567  #
568 
569  def test_copy2():
570  if len(sys.argv) < 3:
571  print("Usage : %s fichier répertoire_destination" %sys.argv[0])
572  print("Ça doit créer sous répertoire_destination une copie du fichier")
573  print("et ça crée répertoire_destination à la volée si nécessaire.")
574  sys.exit(-1)
575  srcname=sys.argv[1]
576  dstname=os.path.join(sys.argv[2],sys.argv[1])
577  shutil.copy2(srcname, dstname)
578  print ("fin de la copie de %s vers %s, listing de %s" %(sys.argv[1],sys.argv[2],sys.argv[2]))
579  subprocess.call("ls %s" %sys.argv[2], shell=True)
580 
581  #test_copytree()
582  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