Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 # | |
| 5 # Adapted from portage/getbinpkg.py -- Portage binary-package helper functions | |
| 6 # Copyright 2003-2004 Gentoo Foundation | |
| 7 # Distributed under the terms of the GNU General Public License v2 | |
| 8 | |
| 9 import operator | |
| 10 import os | |
| 11 import time | |
| 12 import urllib2 | |
| 13 | |
| 14 class PackageIndex(object): | |
|
dianders
2010/11/23 21:47:39
Docstring?
...would be nice to include a spec for
davidjames
2010/11/23 22:53:14
Thanks for all the feedback! Will address your fee
davidjames
2010/11/29 21:18:55
Done. (See http://codereview.chromium.org/5344002/
| |
| 15 | |
| 16 def __init__(self): | |
| 17 """Constructor.""" | |
|
dianders
2010/11/23 21:47:39
All of these members are public? Can you document
davidjames
2010/11/29 21:18:55
Done.
| |
| 18 self.header = {} | |
| 19 self.packages = [] | |
| 20 self.modified = False | |
| 21 | |
| 22 def _ReadPkgIndex(self, pkgfile): | |
| 23 """Read entry from packages file. | |
|
dianders
2010/11/23 21:47:39
Read entries (plural).
davidjames
2010/11/29 21:18:55
Done.
| |
| 24 | |
|
dianders
2010/11/23 21:47:39
Docstring:
This will read entries that look like
davidjames
2010/11/29 21:18:55
Done.
| |
| 25 Args: | |
| 26 pkgfile: A python file object. | |
|
dianders
2010/11/23 21:47:39
Returns: ?
davidjames
2010/11/29 21:18:55
Done.
| |
| 27 """ | |
| 28 d = {} | |
| 29 for line in pkgfile: | |
| 30 line = line.rstrip('\n') | |
| 31 if not line: | |
| 32 break | |
| 33 line = line.split(': ', 1) | |
| 34 if len(line) == 2: | |
| 35 k, v = line | |
| 36 d[k] = v | |
| 37 return d | |
| 38 | |
| 39 def _WritePkgIndex(self, pkgfile, items): | |
| 40 """Write entry to packages file. | |
| 41 | |
|
dianders
2010/11/23 21:47:39
This will terminate the list of items with a blank
davidjames
2010/11/29 21:18:55
Done.
| |
| 42 Args: | |
| 43 pkgfile: A python file object. | |
| 44 items: A list of tuples containing the entries to write. | |
|
dianders
2010/11/23 21:47:39
...containing (key, value) pairs to write.
davidjames
2010/11/29 21:18:55
Done.
| |
| 45 """ | |
| 46 for k, v in items: | |
|
dianders
2010/11/23 21:47:39
assert ':' not in k
davidjames
2010/11/29 21:18:55
Skipped this one.
| |
| 47 pkgfile.write('%s: %s\n' % (k, v)) | |
| 48 pkgfile.write('\n') | |
| 49 | |
| 50 def Read(self, pkgfile): | |
| 51 """Read entire packages file. | |
| 52 | |
|
dianders
2010/11/23 21:47:39
This is a shortcut for calling ReadHeader() and Re
davidjames
2010/11/29 21:18:55
Made those two functions private.
| |
| 53 Args: | |
| 54 pkgfile: A python file object. | |
| 55 """ | |
| 56 self.ReadHeader(pkgfile) | |
| 57 self.ReadBody(pkgfile) | |
| 58 | |
| 59 def ReadHeader(self, pkgfile): | |
| 60 """Read header of packages file. | |
| 61 | |
| 62 Args: | |
| 63 pkgfile: A python file object. | |
| 64 """ | |
| 65 self.header.update(self._ReadPkgIndex(pkgfile)) | |
| 66 | |
| 67 def ReadBody(self, pkgfile): | |
| 68 """Read body of packages file. | |
| 69 | |
| 70 Args: | |
| 71 pkgfile: A python file object. | |
|
dianders
2010/11/23 21:47:39
The header and the blank line following the header
| |
| 72 """ | |
|
dianders
2010/11/23 21:47:39
Comment: Read all the sections in the body by loop
| |
| 73 while True: | |
| 74 d = self._ReadPkgIndex(pkgfile) | |
| 75 if not d: | |
| 76 break | |
|
dianders
2010/11/23 21:47:39
In order to be a valid body section, there must be
| |
| 77 if d.get('CPV'): | |
|
dianders
2010/11/23 21:47:39
if 'CPV' in d:
| |
| 78 self.packages.append(d) | |
| 79 | |
| 80 def Write(self, pkgfile): | |
| 81 """Write a packages file to disk. | |
| 82 | |
|
dianders
2010/11/23 21:47:39
Document: This has a side effect of updating the T
davidjames
2010/11/23 22:53:14
That'd be a better name, but Portage calls the var
| |
| 83 Args: | |
| 84 pkgfile: A python file object. | |
| 85 """ | |
| 86 if self.modified: | |
| 87 self.header['TIMESTAMP'] = str(long(time.time())) | |
| 88 self.header['PACKAGES'] = str(len(self.packages)) | |
|
dianders
2010/11/23 21:47:39
Should we be clearing .modified?
| |
| 89 keys = list(self.header) | |
| 90 keys.sort() | |
| 91 self._WritePkgIndex(pkgfile, [(k, self.header[k]) \ | |
| 92 for k in keys if self.header[k]]) | |
|
dianders
2010/11/23 21:47:39
AKA:
self._WritePkgIndex(pkgfile, sorted((k, v) f
| |
| 93 for metadata in sorted(self.packages, key=operator.itemgetter('CPV')): | |
|
dianders
2010/11/23 21:47:39
Comment: Will write each item in self.packages (is
| |
| 94 cpv = metadata['CPV'] | |
| 95 keys = list(metadata) | |
| 96 keys.sort() | |
| 97 self._WritePkgIndex(pkgfile, | |
| 98 [(k, metadata[k]) for k in keys if metadata[k]]) | |
| 99 | |
| 100 | |
| 101 def GrabRemotePackageIndex(binhost_url): | |
|
dianders
2010/11/23 21:47:39
Technically, this could be a class method on Packa
| |
| 102 """Grab the latest binary package database from the specified URL. | |
| 103 | |
| 104 Args: | |
| 105 binhost_url: Base URL of remote packages (PORTAGE_BINHOST). | |
| 106 | |
| 107 Returns: | |
| 108 A PackageIndex object | |
|
dianders
2010/11/23 21:47:39
COmment: Might return None in certain error condit
| |
| 109 """ | |
| 110 | |
| 111 def _RetryUrlOpen(url, tries=3): | |
|
dianders
2010/11/23 21:47:39
Why does this need to be an encapsulated function?
| |
| 112 """Open the specified url, retrying if we run into temporary errors. | |
| 113 | |
| 114 We retry for both network errors and 5xx Server Errors. We do not retry | |
| 115 for HTTP errors with a non-5xx code. | |
|
dianders
2010/11/23 21:47:39
Does this timeout, or can it ever get stuck foreve
dianders
2010/11/23 21:47:39
Comment: We will sleep for 10 seconds before retry
| |
| 116 | |
| 117 Args: | |
| 118 url: The specified url. | |
| 119 tries: The number of times to try. | |
| 120 | |
| 121 Returns: | |
| 122 The result of urllib2.urlopen(url). | |
|
dianders
2010/11/23 21:47:39
This is a file-like object.
| |
| 123 """ | |
| 124 for i in range(tries): | |
| 125 try: | |
| 126 return urllib2.urlopen(url) | |
| 127 except urllib2.HTTPError as e: | |
| 128 if i + 1 >= tries or e.code < 500: | |
| 129 raise | |
| 130 else: | |
| 131 print 'Cannot GET %s: %s' % (url, str(e)) | |
| 132 except urllib2.URLError as e: | |
| 133 if i + 1 >= tries: | |
| 134 raise | |
| 135 else: | |
| 136 print 'Cannot GET %s: %s' % (url, str(e)) | |
| 137 print "Sleeping for 10 seconds before retrying..." | |
| 138 time.sleep(10) | |
| 139 | |
| 140 url = os.path.join(binhost_url, 'Packages') | |
|
dianders
2010/11/23 21:47:39
No, not os.path.join. This will fail on windows o
| |
| 141 try: | |
| 142 f = _RetryUrlOpen(url) | |
| 143 except urllib2.HTTPError as e: | |
| 144 if e.code == 404: | |
| 145 return None | |
| 146 raise | |
| 147 | |
| 148 pkgindex = PackageIndex() | |
| 149 pkgindex.Read(f) | |
| 150 f.close() | |
| 151 return pkgindex | |
| 152 | |
| 153 | |
| OLD | NEW |