Bug 1168607 - Add get method to finders; r=glandium
authorGregory Szorc <gps@mozilla.com>
Thu, 04 Jun 2015 11:24:03 -0700
changeset 270996 187dfb251703ddce7ffbb553850efcabf3bf8774
parent 270995 553615c0705ea3aa76fea98e3b4b14dcbabcc760
child 270997 1a470efc9fc2b3d76831f683dc2eae1c143628c1
push id2738
push userwcosta@mozilla.com
push dateWed, 10 Jun 2015 13:39:02 +0000
reviewersglandium
bugs1168607
milestone41.0a1
Bug 1168607 - Add get method to finders; r=glandium Today, the *Finder classes are optimized for doing matching and returning multiple results. However, sometimes only looking for a single file is wanted. This commit implements the "get" method on all the *Finder classes. It returns a BaseFile or None. FileFinder._find_file has been refactored into FileFinder.get to avoid code duplication.
python/mozbuild/mozpack/files.py
python/mozbuild/mozpack/test/test_files.py
--- a/python/mozbuild/mozpack/files.py
+++ b/python/mozbuild/mozpack/files.py
@@ -708,16 +708,30 @@ class BaseFinder(object):
         mozpack.path.match documentation for a description of the handled
         patterns.
         '''
         while pattern.startswith('/'):
             pattern = pattern[1:]
         for p, f in self._find(pattern):
             yield p, self._minify_file(p, f)
 
+    def get(self, path):
+        """Obtain a single file.
+
+        Where ``find`` is tailored towards matching multiple files, this method
+        is used for retrieving a single file. Use this method when performance
+        is critical.
+
+        Returns a ``BaseFile`` if at most one file exists or ``None`` otherwise.
+        """
+        files = list(self.find(path))
+        if len(files) != 1:
+            return None
+        return files[0][1]
+
     def __iter__(self):
         '''
         Iterates over all files under the base directory (excluding files
         starting with a '.' and files at any level under a directory starting
         with a '.').
             for path, file in finder:
                 ...
         '''
@@ -783,17 +797,18 @@ class FileFinder(BaseFinder):
         Note all files with a name starting with a '.' are ignored when
         scanning directories, but are not ignored when explicitely requested.
         '''
         if '*' in pattern:
             return self._find_glob('', mozpath.split(pattern))
         elif os.path.isdir(os.path.join(self.base, pattern)):
             return self._find_dir(pattern)
         else:
-            return self._find_file(pattern)
+            f = self.get(pattern)
+            return ((pattern, f),) if f else ()
 
     def _find_dir(self, path):
         '''
         Actual implementation of FileFinder.find() when the given pattern
         corresponds to an existing directory under the base directory.
         Ignores file names starting with a '.' under the given path. If the
         path itself has leafs starting with a '.', they are not ignored.
         '''
@@ -805,33 +820,29 @@ class FileFinder(BaseFinder):
         # likely dependent on filesystem implementation details, such as
         # inode ordering.
         for p in sorted(os.listdir(os.path.join(self.base, path))):
             if p.startswith('.'):
                 continue
             for p_, f in self._find(mozpath.join(path, p)):
                 yield p_, f
 
-    def _find_file(self, path):
-        '''
-        Actual implementation of FileFinder.find() when the given pattern
-        corresponds to an existing file under the base directory.
-        '''
+    def get(self, path):
         srcpath = os.path.join(self.base, path)
         if not os.path.exists(srcpath):
-            return
+            return None
 
         for p in self.ignore:
             if mozpath.match(path, p):
-                return
+                return None
 
         if self.find_executables and is_executable(srcpath):
-            yield path, ExecutableFile(srcpath)
+            return ExecutableFile(srcpath)
         else:
-            yield path, File(srcpath)
+            return File(srcpath)
 
     def _find_glob(self, base, pattern):
         '''
         Actual implementation of FileFinder.find() when the given pattern
         contains globbing patterns ('*' or '**'). This is meant to be an
         equivalent of:
             for p, f in self:
                 if mozpath.match(p, pattern):
--- a/python/mozbuild/mozpack/test/test_files.py
+++ b/python/mozbuild/mozpack/test/test_files.py
@@ -906,16 +906,26 @@ class TestFileFinder(MatchTestTemplate, 
         do_check(self, self.finder, pattern, result)
 
     def test_file_finder(self):
         self.prepare_match_test(with_dotfiles=True)
         self.finder = FileFinder(self.tmpdir)
         self.do_match_test()
         self.do_finder_test(self.finder)
 
+    def test_get(self):
+        self.prepare_match_test()
+        finder = FileFinder(self.tmpdir)
+
+        self.assertIsNone(finder.get('does-not-exist'))
+        res = finder.get('bar')
+        self.assertIsInstance(res, File)
+        self.assertEqual(mozpath.normpath(res.path),
+                         mozpath.join(self.tmpdir, 'bar'))
+
     def test_ignored_dirs(self):
         """Ignored directories should not have results returned."""
         self.prepare_match_test()
         self.add('fooz')
 
         # Present to ensure prefix matching doesn't exclude.
         self.add('foo/quxz')
 
@@ -964,16 +974,19 @@ class TestJarFinder(MatchTestTemplate, T
     def test_jar_finder(self):
         self.jar = JarWriter(file=self.tmppath('test.jar'))
         self.prepare_match_test()
         self.jar.finish()
         reader = JarReader(file=self.tmppath('test.jar'))
         self.finder = JarFinder(self.tmppath('test.jar'), reader)
         self.do_match_test()
 
+        self.assertIsNone(self.finder.get('does-not-exist'))
+        self.assertIsInstance(self.finder.get('bar'), DeflatedFile)
+
 
 class TestComposedFinder(MatchTestTemplate, TestWithTmpDir):
     def add(self, path, content=None):
         # Put foo/qux files under $tmp/b.
         if path.startswith('foo/qux/'):
             real_path = mozpath.join('b', path[8:])
         else:
             real_path = mozpath.join('a', path)
@@ -995,11 +1008,14 @@ class TestComposedFinder(MatchTestTempla
         open(self.tmppath('a/foo/qux/hoge'), 'wb').write('hoge')
         open(self.tmppath('a/foo/qux/bar'), 'wb').write('not the right content')
         self.finder = ComposedFinder({
             '': FileFinder(self.tmppath('a')),
             'foo/qux': FileFinder(self.tmppath('b')),
         })
         self.do_match_test()
 
+        self.assertIsNone(self.finder.get('does-not-exist'))
+        self.assertIsInstance(self.finder.get('bar'), File)
+
 
 if __name__ == '__main__':
     mozunit.main()