[PATCH 1/2] Add RemoteRepoCache.resolve_refs() method with tests

Jannis Pohlmann jannis.pohlmann at codethink.co.uk
Mon Jan 7 18:40:36 GMT 2013


This method is for batch-resolving of refs into SHA1s using the remote
repo cache. It takes a list of (repo, ref) tuples and returns
dictionary that maps these tuples to dictionaries that look as follow
in successful cases

    {
        'repo': <original repo>,
        'ref': <original ref>,
        'repo-url': <expanded full repo URL>,
        'sha1': <resolved SHA1>,
        'tree': <tree SHA1 of the commit>,
    }

and like this in negative cases:

    {
        'repo': <original repo>,
        'ref': <original ref>,
        'repo-url': <expanded full repo URL>,
        'error': <error string>,
    }

The mapping may seem redundant but it might be useful when looking up
the results. If tuples are passed in they will be available at the
caller's end already.
---
 morphlib/localrepocache.py        |  1 +
 morphlib/remoterepocache.py       | 41 +++++++++++++++++++
 morphlib/remoterepocache_tests.py | 83 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/morphlib/localrepocache.py b/morphlib/localrepocache.py
index 465e9f0..47680da 100644
--- a/morphlib/localrepocache.py
+++ b/morphlib/localrepocache.py
@@ -195,6 +195,7 @@ class LocalRepoCache(object):
     def _cache_name(self, url):
         scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
         if scheme == 'file':
+
             return path
         else:
             basename = self._escape(url)
diff --git a/morphlib/remoterepocache.py b/morphlib/remoterepocache.py
index 5c2017d..4a73518 100644
--- a/morphlib/remoterepocache.py
+++ b/morphlib/remoterepocache.py
@@ -28,6 +28,13 @@ class ResolveRefError(cliapp.AppException):
             (ref, repo_name))
 
 
+class ResolveRefsError(cliapp.AppException):
+
+    def __init__(self, tuples):
+        cliapp.AppException.__init__(
+                self, 'Failed to resolve refs: %s' % tuples)
+
+
 class CatFileError(cliapp.AppException):
 
     def __init__(self, repo_name, ref, filename):
@@ -56,6 +63,13 @@ class RemoteRepoCache(object):
         except:
             raise ResolveRefError(repo_name, ref)
 
+    def resolve_refs(self, tuples):
+        pull_urls = [self._resolver.pull_url(repo) for repo, ref in tuples]
+        try:
+            return self._resolve_refs_for_repo_urls(tuples, pull_urls)
+        except:
+            raise ResolveRefsError(tuples)
+
     def cat_file(self, repo_name, ref, filename):
         repo_url = self._resolver.pull_url(repo_name)
         try:
@@ -76,6 +90,25 @@ class RemoteRepoCache(object):
         info = json.loads(data)
         return info['sha1'], info['tree']
 
+    def _resolve_refs_for_repo_urls(self, tuples, urls): # pragma: no cover
+        request_data = []
+        for n in xrange(0, len(tuples)):
+            request_data.append({'repo': urls[n], 'ref': tuples[n][1]})
+        response_data = self._make_post_request('sha1s', request_data)
+        data = {}
+        for n in xrange(0, len(tuples)):
+            data[tuples[n]] = {
+                'repo': tuples[n][0],
+                'repo-url': response_data[n]['repo'],
+                'ref': response_data[n]['ref'],
+            }
+            if 'error' in resonse_data[n]:
+                data[tuples[n]]['error'] = response_data[n]['error']
+            else:
+                data[tuples[n]]['sha1'] = response_data[n]['sha1']
+                data[tuples[n]]['tree'] = response_data[n]['tree']
+        return data
+
     def _cat_file_for_repo_url(self, repo_url, ref,
                                filename):  # pragma: no cover
         return self._make_request(
@@ -91,3 +124,11 @@ class RemoteRepoCache(object):
         url = urlparse.urljoin(server_url, '/1.0/%s' % path)
         handle = urllib2.urlopen(url)
         return handle.read()
+
+    def _make_post_request(self, path, data): # pragma: no cover
+        server_url = self.server_url
+        if not server_url.endswith('/'):
+            server_url += '/'
+        url = urlparse.urljoin(server_url, '/1.0/%s' % path)
+        handle = urllib2.urlopen(url, data)
+        return handle.read()
diff --git a/morphlib/remoterepocache_tests.py b/morphlib/remoterepocache_tests.py
index 5ef61f4..e448b91 100644
--- a/morphlib/remoterepocache_tests.py
+++ b/morphlib/remoterepocache_tests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012  Codethink Limited
+# Copyright (C) 2012-2013  Codethink Limited
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -25,6 +25,23 @@ class RemoteRepoCacheTests(unittest.TestCase):
     def _resolve_ref_for_repo_url(self, repo_url, ref):
         return self.sha1s[repo_url][ref]
 
+    def _resolve_refs_for_repo_urls(self, tuples, urls):
+        if self.fail_resolving:
+            raise Exception('Failed')
+        data = {}
+        for n in xrange(0, len(tuples)):
+            data[tuples[n]] = {
+                'repo': tuples[n][0],
+                'repo-url': urls[n],
+                'ref': tuples[n][1],
+            }
+            if tuples[n][0] == 'upstream:foo':
+                data[tuples[n]]['error'] = 'Failed'
+            else:
+                data[tuples[n]]['sha1'] = self.sha1s[urls[n]][tuples[n][1]]
+                data[tuples[n]]['tree'] = self.trees[urls[n]][tuples[n][1]]
+        return data
+
     def _cat_file_for_repo_url(self, repo_url, sha1, filename):
         return self.files[repo_url][sha1][filename]
 
@@ -39,6 +56,17 @@ class RemoteRepoCacheTests(unittest.TestCase):
         self.sha1s = {
             'git://gitorious.org/baserock/morph': {
                 'master': 'e28a23812eadf2fce6583b8819b9c5dbd36b9fb9'
+            },
+            'git://gitorious.org/baserock-morphs/linux': {
+                'foo/bar': 'aa363025d0b699b4251ee80aeec2b863e57dd7ec'
+            }
+        }
+        self.trees = {
+            'git://gitorious.org/baserock/morph': {
+                'master': 'f99e30eb68c28ea8bc2ca7710e2894c49bbaedbe'
+            },
+            'git://gitorious.org/baserock-morphs/linux': {
+                'foo/bar': '4c7b6184fe12775ceb83cefb405921e961495e9c'
             }
         }
         self.files = {
@@ -57,8 +85,11 @@ class RemoteRepoCacheTests(unittest.TestCase):
         self.cache = morphlib.remoterepocache.RemoteRepoCache(
             self.server_url, resolver)
         self.cache._resolve_ref_for_repo_url = self._resolve_ref_for_repo_url
+        self.cache._resolve_refs_for_repo_urls = \
+                self._resolve_refs_for_repo_urls
         self.cache._cat_file_for_repo_url = self._cat_file_for_repo_url
         self.cache._ls_tree_for_repo_url = self._ls_tree_for_repo_url
+        self.fail_resolving = False
 
     def test_sets_server_url(self):
         self.assertEqual(self.cache.server_url, self.server_url)
@@ -84,6 +115,56 @@ class RemoteRepoCacheTests(unittest.TestCase):
                           self.cache.resolve_ref, 'non-existent-repo',
                           'non-existent-ref')
 
+    def test_resolves_multiple_existing_refs(self):
+        sha1s = self.cache.resolve_refs(
+                [('baserock:morph', 'master'), ('upstream:linux', 'foo/bar')])
+        self.assertEqual(
+                sha1s[('baserock:morph', 'master')],
+                {
+                    'repo': 'baserock:morph',
+                    'repo-url': 'git://gitorious.org/baserock/morph',
+                    'ref': 'master',
+                    'sha1': 'e28a23812eadf2fce6583b8819b9c5dbd36b9fb9',
+                    'tree': 'f99e30eb68c28ea8bc2ca7710e2894c49bbaedbe'
+                })
+        self.assertEqual(
+                sha1s[('upstream:linux', 'foo/bar')],
+                {
+                    'repo': 'upstream:linux',
+                    'repo-url': 'git://gitorious.org/baserock-morphs/linux',
+                    'ref': 'foo/bar',
+                    'sha1': 'aa363025d0b699b4251ee80aeec2b863e57dd7ec',
+                    'tree': '4c7b6184fe12775ceb83cefb405921e961495e9c'
+                })
+
+    def test_throws_exception_when_failing_to_resolve_refs_entirely(self):
+        self.fail_resolving = True
+        self.assertRaises(
+                morphlib.remoterepocache.ResolveRefsError,
+                self.cache.resolve_refs,
+                [('baserock:morph', 'master'), ('upstream:linux', 'foo/bar')])
+
+    def test_fills_error_fields_when_failing_to_resolve_some_refs(self):
+        sha1s = self.cache.resolve_refs(
+                [('baserock:morph', 'master'), ('upstream:foo', 'bar')])
+        self.assertEqual(
+                sha1s[('baserock:morph', 'master')],
+                {
+                    'repo': 'baserock:morph',
+                    'repo-url': 'git://gitorious.org/baserock/morph',
+                    'ref': 'master',
+                    'sha1': 'e28a23812eadf2fce6583b8819b9c5dbd36b9fb9',
+                    'tree': 'f99e30eb68c28ea8bc2ca7710e2894c49bbaedbe'
+                })
+        self.assertEqual(
+                sha1s[('upstream:foo', 'bar')],
+                {
+                    'repo': 'upstream:foo',
+                    'repo-url': 'git://gitorious.org/baserock-morphs/foo',
+                    'ref': 'bar',
+                    'error': 'Failed',
+                })
+
     def test_cat_existing_file_in_existing_repo_and_ref(self):
         content = self.cache.cat_file(
             'upstream:linux', 'e28a23812eadf2fce6583b8819b9c5dbd36b9fb9',
-- 
1.7.11.4





More information about the baserock-dev mailing list