import os, os.path, stat, sys import threading import posixpath # instead of os.path for gnomevfs operations import time import gobject, gtk from gnomevfs import * import fringtools WALKER_CLASSIC = 0 WALKER_GNOMEVFS = 1 WALKER_CPP = 2 def print_tree( treearray, tab=0 ): """ An utility function to print out a tree array """ fn, data, size = treearray print " "*tab,"%s (%i)"%(fn,size) if not data: return if tab > 1: return for e in data: print_tree(e,tab+1) def treearray_cmp_fn( a, b ): """ a and b are tuples describing a directory tree. Compare first by directory status, then by size, and finally by name. """ a_dir = a[1] != None b_dir = b[1] != None if a_dir and not b_dir: return 1 elif b_dir and not a_dir: return -1 elif a_dir and b_dir: return cmp(a[2], b[2]) # compare sizes else: return cmp(a[0], b[0]) # compare names class FringWalker( gobject.GObject ): """ Manages requests for walking directories Directory entries are represented by tuples of the following form: (name, contents, size) name: the file name of the directory contents: a list of entries within the entry. If None, the entry is not a directory size: the size of the entry. """ __gsignals__ = { 'list-changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'finished': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'manually-stopped': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), 'progress': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,gobject.TYPE_INT)), } def __init__(self): gobject.GObject.__init__(self) self.thread = None self.showhidden = False def walk(self,uri,method=WALKER_GNOMEVFS): self.stop() print "walkwalk",uri self.thread = WalkThread(self,uri,self.showhidden,method) self.thread.start() def stop(self): if self.thread: self.emit("manually-stopped") self.thread.stopsignal = True self.thread = None def _progress_fn(self, walkthread, c, l, r): # only emit if called from the current request if walkthread == self.thread: gtk.gdk.threads_enter() self.emit("progress", c, l) self.emit("list-changed", r) gtk.gdk.threads_leave() def _finished_fn(self, walkthread, r): if walkthread == self.thread: gtk.gdk.threads_enter() self.emit("finished", r) gtk.gdk.threads_leave() self.thread = None class WalkThread( threading.Thread ): """ A separate class for the thread. """ def __init__(self, master, uri, showhidden, method=WALKER_CLASSIC): """ Parameters: A FringWalker instance, a string with the path and a bool """ threading.Thread.__init__(self) self.stopsignal = False self.master = master self.uri = uri self.showhidden = showhidden self.method = method print "open",uri self.path = get_local_path_from_uri( str(uri) ) print "local",self.path def build_tree_python(self, path, showhidden): l = [] total = 0 for fn in os.listdir(path): if self.stopsignal: return (None,None,0) if not showhidden and fn[0] == ".": continue try: p = os.path.join(path, fn) except: continue s = os.lstat(p) if stat.S_ISDIR(s.st_mode): sub = self.build_tree_python(p,showhidden) l.append( sub ) total += sub[2] elif stat.S_ISREG(s.st_mode): l.append( (fn, None, s.st_size) ) total += s.st_size return (os.path.split(path)[-1], l, total) def __uri_tail(self, uri): """ Return the filename in a gnomevfs uri """ f = format_uri_for_display(str(uri)) f = posixpath.split( f )[-1] return f def build_tree_gnomevfs(self, uri, showhidden): try: h = DirectoryHandle(uri) except InvalidURIError: print uri,"is not a valid uri, skipped" return (str(uri), None, 0) except NotFoundError: print uri,"not found, skipped" return (str(uri), None, 0) l = [] total = 0 d = h.next() try: while True: if self.stopsignal: return (None,None,0) d = h.next() if not showhidden and d.name[0] == ".": continue if d.type == 2: # directory sub = self.build_tree_gnomevfs(uri.append_path(d.name),showhidden) l.append( sub ) total += sub[2] else: l.append( (d.name, None, d.size) ) total += d.size except StopIteration: pass return (self.__uri_tail(uri), l, total) def run(self): """ Parse the root directory """ starttime = time.time() print "start walking",self.path, if self.method == WALKER_CPP: print "(using c++ extension)" elif self.method == WALKER_GNOMEVFS: print "(using python and gnomevfs)" else: print "(using classic python)" l = [] i = 0 subdirectories = [] total = 0 # make a first run for the root directory try: for fn in os.listdir(self.path): if self.stopsignal: return None if not self.showhidden and fn[0] == '.': continue try: p = os.path.join(self.path, fn) except: continue s = os.lstat(p) if stat.S_ISDIR(s.st_mode): subdirectories.append( (fn,p) ); elif stat.S_ISREG(s.st_mode): l.append((fn, None, s.st_size)) total += s.st_size i += 1 except OSError: pass # emit an intermediate version to fill up the screen while waiting self.master._progress_fn(self, 0, len(subdirectories), (os.path.split(self.path)[-1], l, total)) # now walk the subdirectories with the faster extension function c = 0 for fn, p in subdirectories: c += 1 if self.method == WALKER_CPP: sub = fringtools.build_tree(p, self.showhidden) elif self.method == WALKER_GNOMEVFS: sub = self.build_tree_gnomevfs(URI("file://"+p), self.showhidden) else: sub = self.build_tree_python(p, self.showhidden) if self.stopsignal: return None total += sub[2] l.append( (fn,sub[1],sub[2]) ); l.sort(treearray_cmp_fn) # emit an intermediate version after each directory self.master._progress_fn(self, c, len(subdirectories), (os.path.split(self.path)[-1], l, total)) l.sort(treearray_cmp_fn) # emit final signal self.master._finished_fn(self,(os.path.split(self.path)[-1], l, total)) print "finished walking",self.path,"(time=%s)"%round(time.time()-starttime,2)