Index: chromite/lib/binpkg.py |
diff --git a/chromite/lib/binpkg.py b/chromite/lib/binpkg.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..31febfdcc25ffe6d48e4b4cad7fa64c5ff741f22 |
--- /dev/null |
+++ b/chromite/lib/binpkg.py |
@@ -0,0 +1,153 @@ |
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+# |
+# Adapted from portage/getbinpkg.py -- Portage binary-package helper functions |
+# Copyright 2003-2004 Gentoo Foundation |
+# Distributed under the terms of the GNU General Public License v2 |
+ |
+import operator |
+import os |
+import time |
+import urllib2 |
+ |
+class PackageIndex(object): |
+ |
+ def __init__(self): |
+ """Constructor.""" |
+ self.header = {} |
+ self.packages = [] |
+ self.modified = False |
+ |
+ def _ReadPkgIndex(self, pkgfile): |
+ """Read entry from packages file. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ """ |
+ d = {} |
+ for line in pkgfile: |
+ line = line.rstrip('\n') |
+ if not line: |
+ break |
+ line = line.split(': ', 1) |
+ if len(line) == 2: |
+ k, v = line |
+ d[k] = v |
+ return d |
+ |
+ def _WritePkgIndex(self, pkgfile, items): |
+ """Write entry to packages file. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ items: A list of tuples containing the entries to write. |
+ """ |
+ for k, v in items: |
+ pkgfile.write('%s: %s\n' % (k, v)) |
+ pkgfile.write('\n') |
+ |
+ def Read(self, pkgfile): |
+ """Read entire packages file. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ """ |
+ self.ReadHeader(pkgfile) |
+ self.ReadBody(pkgfile) |
+ |
+ def ReadHeader(self, pkgfile): |
+ """Read header of packages file. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ """ |
+ self.header.update(self._ReadPkgIndex(pkgfile)) |
+ |
+ def ReadBody(self, pkgfile): |
+ """Read body of packages file. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ """ |
+ while True: |
+ d = self._ReadPkgIndex(pkgfile) |
+ if not d: |
+ break |
+ if d.get('CPV'): |
+ self.packages.append(d) |
+ |
+ def Write(self, pkgfile): |
+ """Write a packages file to disk. |
+ |
+ Args: |
+ pkgfile: A python file object. |
+ """ |
+ if self.modified: |
+ self.header['TIMESTAMP'] = str(long(time.time())) |
+ self.header['PACKAGES'] = str(len(self.packages)) |
+ keys = list(self.header) |
+ keys.sort() |
+ self._WritePkgIndex(pkgfile, [(k, self.header[k]) \ |
+ for k in keys if self.header[k]]) |
+ for metadata in sorted(self.packages, key=operator.itemgetter('CPV')): |
+ cpv = metadata['CPV'] |
+ keys = list(metadata) |
+ keys.sort() |
+ self._WritePkgIndex(pkgfile, |
+ [(k, metadata[k]) for k in keys if metadata[k]]) |
+ |
+ |
+def GrabRemotePackageIndex(binhost_url): |
+ """Grab the latest binary package database from the specified URL. |
+ |
+ Args: |
+ binhost_url: Base URL of remote packages (PORTAGE_BINHOST). |
+ |
+ Returns: |
+ A PackageIndex object |
+ """ |
+ |
+ def _RetryUrlOpen(url, tries=3): |
+ """Open the specified url, retrying if we run into temporary errors. |
+ |
+ We retry for both network errors and 5xx Server Errors. We do not retry |
+ for HTTP errors with a non-5xx code. |
+ |
+ Args: |
+ url: The specified url. |
+ tries: The number of times to try. |
+ |
+ Returns: |
+ The result of urllib2.urlopen(url). |
+ """ |
+ for i in range(tries): |
+ try: |
+ return urllib2.urlopen(url) |
+ except urllib2.HTTPError as e: |
+ if i + 1 >= tries or e.code < 500: |
+ raise |
+ else: |
+ print 'Cannot GET %s: %s' % (url, str(e)) |
+ except urllib2.URLError as e: |
+ if i + 1 >= tries: |
+ raise |
+ else: |
+ print 'Cannot GET %s: %s' % (url, str(e)) |
+ print "Sleeping for 10 seconds before retrying..." |
+ time.sleep(10) |
+ |
+ url = os.path.join(binhost_url, 'Packages') |
+ try: |
+ f = _RetryUrlOpen(url) |
+ except urllib2.HTTPError as e: |
+ if e.code == 404: |
+ return None |
+ raise |
+ |
+ pkgindex = PackageIndex() |
+ pkgindex.Read(f) |
+ f.close() |
+ return pkgindex |
+ |
+ |