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 |