holger krekel avatar holger krekel committed e8cc8dd

add breadthfirst and sorted options to visit(), refactor to a Visit class
to avoid passing around tons of arguments.

Comments (0)

Files changed (2)

py/_path/common.py

         except AttributeError:
             return str(self) < str(other)
 
-    def visit(self, fil=None, rec=None, ignore=NeverRaised):
+    def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
         """ yields all paths below the current one
 
             fil is a filter (glob pattern or callable), if not matching the
 
             ignore is an Exception class that is ignoredwhen calling dirlist()
             on any of the paths (by default, all exceptions are reported)
+
+            bf if True will cause a breadthfirst search instead of the
+            default depthfirst. Default: False
+
+            sort if True will sort entries within each directory level.
         """
-        if isinstance(fil, str):
-            fil = FNMatcher(fil)
-        if rec:
-            if isinstance(rec, str):
-                rec = fnmatch(fil)
-            elif not hasattr(rec, '__call__'):
-                rec = None
-        try:
-            entries = self.listdir()
-        except ignore:
-            return
-        dirs = [p for p in entries
-                    if p.check(dir=1) and (rec is None or rec(p))]
-        for subdir in dirs:
-            for p in subdir.visit(fil=fil, rec=rec, ignore=ignore):
-                yield p
-        for p in entries:
-            if fil is None or fil(p):
-                yield p
+        for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
+            yield x
 
     def _sortlist(self, res, sort):
         if sort:
         """ return True if other refers to the same stat object as self. """
         return self.strpath == str(other)
 
+class Visitor:
+    def __init__(self, fil, rec, ignore, bf, sort):
+        if isinstance(fil, str):
+            fil = FNMatcher(fil)
+        if rec:
+            if isinstance(rec, str):
+                rec = fnmatch(fil)
+            else:
+                assert hasattr(rec, '__call__')
+        self.fil = fil
+        self.rec = rec
+        self.ignore = ignore
+        self.breadthfirst = bf
+        self.optsort = sort and sorted or (lambda x: x)
+
+    def gen(self, path):
+        try:
+            entries = path.listdir()
+        except self.ignore:
+            return
+        rec = self.rec
+        dirs = self.optsort([p for p in entries
+                    if p.check(dir=1) and (rec is None or rec(p))])
+        if not self.breadthfirst:
+            for subdir in dirs:
+                for p in self.gen(subdir):
+                    yield p
+        for p in self.optsort(entries):
+            if self.fil is None or self.fil(p):
+                yield p
+        if self.breadthfirst:
+            for subdir in dirs:
+                for p in self.gen(subdir):
+                    yield p
+
 class FNMatcher:
     def __init__(self, pattern):
         self.pattern = pattern

testing/path/common.py

     def test_relto_wrong_type(self, path1):
         py.test.raises(TypeError, "path1.relto(42)")
 
+    def test_load(self, path1):
+        p = path1.join('samplepickle')
+        obj = p.load()
+        assert type(obj) is dict
+        assert obj.get('answer',None) == 42
+
     def test_visit_filesonly(self, path1):
         l = []
         for i in path1.visit(lambda x: x.check(file=1)):
         assert not "sampledir" in l
         assert path1.sep.join(["sampledir", "otherfile"]) in l
 
-    def test_load(self, path1):
-        p = path1.join('samplepickle')
-        obj = p.load()
-        assert type(obj) is dict
-        assert obj.get('answer',None) == 42
-
     def test_visit_nodotfiles(self, path1):
         l = []
         for i in path1.visit(lambda x: x.check(dotfile=0)):
         assert path1.sep.join(["sampledir", "otherfile"]) in l
         assert not ".dotfile" in l
 
+    def test_visit_breadthfirst(self, path1):
+        l = []
+        for i in path1.visit(bf=True):
+            l.append(i.relto(path1))
+        for i, p in enumerate(l):
+            if path1.sep in p:
+                for j in range(i, len(l)):
+                    assert path1.sep in l[j]
+                break
+        else:
+            py.test.fail("huh")
+
+    def test_visit_sort(self, path1):
+        l = []
+        for i in path1.visit(bf=True, sort=True):
+            l.append(i.relto(path1))
+        for i, p in enumerate(l):
+            if path1.sep in p:
+                break
+        assert l[:i] == sorted(l[:i])
+        assert l[i:] == sorted(l[i:])
+
     def test_endswith(self, path1):
         def chk(p):
             return p.check(endswith="pickle")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.