diff options
Diffstat (limited to 'src/fringlib/fringwalker.py')
| -rw-r--r-- | src/fringlib/fringwalker.py | 290 | 
1 files changed, 191 insertions, 99 deletions
diff --git a/src/fringlib/fringwalker.py b/src/fringlib/fringwalker.py index bb6195b..a341955 100644 --- a/src/fringlib/fringwalker.py +++ b/src/fringlib/fringwalker.py @@ -1,147 +1,239 @@ -import os, os.path, stat, sys, time +import os, os.path, stat, sys +import threading +import posixpath # instead of os.path for gnomevfs operations +import time +  import gobject, gtk -from threading import Thread +from gnomevfs import * -class sum_list: -    def __init__(self, l, name = None): +try: import fringtools +except: print 'Error: Could not find "fringtools" extension module' -        self.the_sum = 0 -        self.data = list(l) -        self.name = name +WALKER_CLASSIC = 0 +WALKER_GNOMEVFS = 1 +WALKER_CPP = 2 -        for fn, i in self.data: +def print_tree( treearray, tab=0 ): +    """ An utility function to print out a tree array """ -            if isinstance(i, sum_list): -                self.the_sum += i.the_sum -            else: -                self.the_sum += i +    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 __str__(self): -        return self.name+": "+str(self.data) -    def sort(self): +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. """     -        def cmp_fn(a, b): -            a_dir = isinstance(a[1], sum_list) -            b_dir = isinstance(b[1], sum_list) +    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: -                return cmp(a[1].the_sum, b[1].the_sum) -            else: -                return cmp(a[1], b[1]) +    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 -        self.data.sort(cmp_fn)  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.stopsignal = False          self.showhidden = False -    def walk(self,path): -        self.thread = Thread(None,self._parse,None,(path,)) -        self.stopsignal = False -        print "start thread (%s)"%path +    def walk(self,uri,method=WALKER_GNOMEVFS): +        self.stop() +        self.thread = WalkThread(self,uri,self.showhidden,method)          self.thread.start()      def stop(self): -        if self.thread is None: -            return - -        if not self.thread.isAlive(): +        if self.thread: +            self.emit("manually-stopped") +            self.thread.stopsignal = True              self.thread = None -            return - -        self.stopsignal = True -        print "stopping thread:" -        #self.thread.join() -        print "ok" - -    def _parse(self,path): -        """ Parse the root directory """ -        def progress_fn(c, l, r): -            # emit signals +    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() -             -        r = self._build_tree(path, progress_fn) -        gtk.gdk.threads_enter() -        self.emit("finished", 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 -        print "finished walking", path -    def _build_tree(self, path, progress_fn = None): -        """ Parse directories recursively """ -         -        ret = [] -        tmp_dirs = [] -         +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(uri) +        self.showhidden = showhidden +        self.method = method + +    def __uri_tail(self, uri): +        """ Return the tail (the filename) of a gnomevfs uri """ +        f = format_uri_for_display(str(uri)) +        f = posixpath.split( f )[-1] +        return f + +    def build_tree_python(self, path): +        l = [] +        total = 0 +        for fn in os.listdir(path): +            if self.stopsignal: return (None,None,0) +            if not self.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) +                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 build_tree_gnomevfs(self, uri): + +        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: -            # walk files in directory -            for fn in os.listdir(path): +            while True: +                if self.stopsignal: return (None,None,0) +                d = h.next() +                if not self.showhidden and d.name[0] == ".": continue -                if self.stopsignal: -                    return sum_list([]) +                if d.type == 2: # directory +                    sub = self.build_tree_gnomevfs(uri.append_path(d.name)) +                    l.append( sub ) +                    total += sub[2] +                else: +                    l.append( (d.name, None, d.size) ) +                    total += d.size -                if not self.showhidden and fn[0] == '.': -                    continue +        except StopIteration: pass           +        return (self.__uri_tail(uri), l, total) -                try: -                    p = os.path.join(path, fn) -                    s = os.lstat(p) -                except: -                    continue -                if stat.S_ISDIR(s.st_mode):  -                    tmp_dirs.append(fn); -                elif stat.S_ISREG(s.st_mode): -                    ret.append((fn, s.st_size)) -        except OSError: -            pass +    def run(self): +        """ Parse the root directory """ +        # write some debug information +        starttime = time.time() +        print "start walking",self.uri, +        if self.method == WALKER_CPP: print "(using c++ extension)" +        elif self.method == WALKER_GNOMEVFS: print "(using python and gnomevfs)" +        else: print "(using classic python)" + +        # scan root directory first (using gnomevfs) +        try: h = DirectoryHandle(self.uri) +        except InvalidURIError: +            print uri,"is not a valid uri, skipped" +            return (str(self.uri), None, 0) +        except NotFoundError: +            print uri,"not found, skipped" +            return (str(self.uri), None, 0) + +        subdirectories = [] +        l = [] +        total = 0 +        d = h.next()          try: -            c = 0 -             -            for fn in tmp_dirs: -                c += 1 - -                if self.stopsignal: -                    return sum_list([]) -                 -                try: -                    p = os.path.join(path, fn) -                except: -                    continue +            while True: +                if self.stopsignal: return +                d = h.next() +                if not self.showhidden and d.name[0] == ".": continue + +                if d.type == 2: # directory +                    subdirectories.append( d.name ); +                else: +                    l.append( (d.name, None, d.size) ) +                    total += d.size +        except StopIteration: pass +     +        # emit an intermediate version to fill up the screen while waiting +        self.master._progress_fn(self, +            0, len(subdirectories), +            (self.__uri_tail(self.uri), l, total)) + +        # now walk the subdirectories with the faster extension function +        c = 0 + +        for directory in subdirectories: +            c += 1 + +            if self.method == WALKER_CPP: +                path = get_local_path_from_uri(str(self.uri))+os.sep+directory +                sub = fringtools.build_tree(path, self.showhidden)   +            elif self.method == WALKER_GNOMEVFS: +                uri = self.uri +                sub = self.build_tree_gnomevfs(uri.append_path(directory)) +            else: +                path = get_local_path_from_uri(str(self.uri))+os.sep+directory +                sub = self.build_tree_python(path) -                ret.append((fn, self._build_tree(p))) +            if self.stopsignal: return -                if not (progress_fn is None): -                    r = sum_list(ret, os.path.split(path)[-1]) -                    r.sort() -                    progress_fn(c, len(tmp_dirs), r) -                     -        except OSError: -            pass +            total += sub[2] +            l.append( (directory,sub[1],sub[2]) );  -        r = sum_list(ret, os.path.split(path)[-1]) -        r.sort() +            l.sort(treearray_cmp_fn) -        return r +            # emit an intermediate version after each directory +            self.master._progress_fn(self, +                c, len(subdirectories), +                (self.__uri_tail(self.uri), l, total)) +             +         +        l.sort(treearray_cmp_fn) +        # emit final signal +        self.master._finished_fn(self,(self.__uri_tail(self.uri), l, total)) +        print "finished walking",self.uri,"(time=%s)"%round(time.time()-starttime,2) +          | 
