Merge lp:~mac9416/unwrapt/dependency_statuses into lp:unwrapt

Proposed by mac9416
Status: Merged
Merge reported by: Chris Oliver
Merged at revision: not available
Proposed branch: lp:~mac9416/unwrapt/dependency_statuses
Merge into: lp:unwrapt
Diff against target: 282 lines (+57/-42)
3 files modified
example.py (+4/-1)
unwrapt/DefinitionBase.py (+5/-2)
unwrapt/definitions/aptdef/__init__.py (+48/-39)
To merge this branch: bzr merge lp:~mac9416/unwrapt/dependency_statuses
Reviewer Review Type Date Requested Status
Chris Oliver Approve
Review via email: mp+32369@code.launchpad.net

This proposal supersedes a proposal from 2010-08-11.

Description of the change

Started work on the dependency package statuses. I say "statuses" because there will have to be one for "to be downloaded" and "to be installed".

I have everything in order except getting on_mark_package to mark the top-level package as "to be donwloaded" and the dependencies as "dependency to be downloaded". See huge comment at top of on_mark_package.

I also dropped a few unrelated TODOs, FIXMEs, and improvements.

To post a comment you must log in.
Revision history for this message
mac9416 (mac9416) wrote :

I resubmitted the request because I _think_ I have dependency statuses implemented. I added a `dependency` argument to the mark_package function that is False by default but True when mark_package calls itself.

Ingenious, eh?

Revision history for this message
Chris Oliver (excid3) wrote :

Exactly how I planned on doing it. :D Reviewing it now, but it looks great so far!

Revision history for this message
Chris Oliver (excid3) wrote :

Good good good upgrade to the url_join function. I noticed it was annoying me nesting it, but I did not even think of fixing that. Thanks!! :D

Revision history for this message
Chris Oliver (excid3) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'example.py'
2--- example.py 2010-08-01 19:34:08 +0000
3+++ example.py 2010-08-11 19:11:23 +0000
4@@ -51,7 +51,10 @@
5
6 print "%i available packages" % len(apt.get_available_package_names())
7
8-#apt.update(directory="/var/lib/apt/lists", download=False)
9+#FIXME: /var/lib/apt is not the download directory. We should have the ability
10+# to individually specify the download directories.
11+#apt.set_download_directory("/var/lib/apt")
12+#apt.update(download=False)
13
14 apt.update()
15
16
17=== modified file 'unwrapt/DefinitionBase.py'
18--- unwrapt/DefinitionBase.py 2010-08-01 19:34:08 +0000
19+++ unwrapt/DefinitionBase.py 2010-08-11 19:11:23 +0000
20@@ -90,6 +90,7 @@
21
22 - directory is the location of the
23 """
24+ #FIXME: that docstring.
25
26 self.download_directory = os.path.abspath(os.path.expanduser(directory))
27
28@@ -163,6 +164,7 @@
29
30 pass
31
32+
33 @callback
34 def update(self, reporthook=None, directory=None, download=True):
35 """
36@@ -202,6 +204,7 @@
37
38 pass
39
40+
41 @callback
42 def get_latest_binary(self, package):
43 """
44@@ -268,7 +271,8 @@
45
46 For example:
47
48- client.mark_package("firefox")
49+ package = client.get_latest_binary("firefox")
50+ client.mark_package(package)
51 """
52
53 pass
54@@ -289,7 +293,6 @@
55 For example:
56
57 client.apply_changes()
58-
59 """
60
61 pass
62
63=== modified file 'unwrapt/definitions/aptdef/__init__.py'
64--- unwrapt/definitions/aptdef/__init__.py 2010-08-01 19:34:08 +0000
65+++ unwrapt/definitions/aptdef/__init__.py 2010-08-11 19:11:23 +0000
66@@ -51,14 +51,12 @@
67
68
69 #TODO: Move this code to proper library location
70-def url_join(first, last):
71+def url_join(*args):
72 """ Returns full URL """
73- if first.endswith('/'):
74- if last.startswith('/'): return first + last[1:]
75- else: return first + last
76- else:
77- if last.startswith('/'): return first + last
78- else: return first + '/' + last
79+ # Strip any leading or trailing slashes from the parts.
80+ args = [x.strip("/") for x in args]
81+
82+ return "/".join(args)
83
84
85 #class Repository(Base):
86@@ -108,10 +106,15 @@
87
88
89 def to_url(repository, architecture, format):
90- return url_join(repository["url"], url_join(architecture, format))
91+ return url_join(repository["url"], architecture, format)
92
93
94 def to_filename(directory, url):
95+ """
96+ Forms a full filename from a directory and url.
97+ i.e. Strips the url of the protocol prefix, replacse all slashes with
98+ underscores, and appends it to directory.
99+ """
100 return os.path.join(directory, url.split("//")[1].replace("/", "_"))
101
102
103@@ -147,8 +150,13 @@
104 supported = ["amd64", "armel", "i386", "ia64", "powerpc", "sparc"]
105 status_properties = ["Package", "Version", "Status", "Provides"]
106 binary_dependencies = ["Pre-Depends", "Depends", "Recommends"]
107- supported_statuses = ["install ok installed", "to be installed", "to be downloaded"]
108+ supported_statuses = ["install ok installed",
109+ "to be downloaded",
110+ "dependency to be downloaded",
111+ "to be installed",
112+ "dependency to be installed"]
113
114+ #FIXME: This seems redundant. Could it be moved to DefinitionBase?
115 def on_set_proxy(self, proxy, username=None, password=None):
116 self.proxy = {"proxy": proxy,
117 "user": username,
118@@ -184,7 +192,7 @@
119 self.repositories[count]["url"] = url
120 self.repositories[count]["dist"] = dist
121 self.repositories[count]["section"] = section
122- self.repositories[count]["url"] = url_join(url, url_join("dists", url_join(dist, section)))
123+ self.repositories[count]["url"] = url_join(url, "dists", dist, section)
124
125 count += 1
126
127@@ -328,7 +336,6 @@
128 installed statuses.
129 """
130
131-
132 f = open(status, "rb")
133
134 self.status = {}
135@@ -420,7 +427,7 @@
136 return self.packages[package]
137
138
139- def on_mark_package(self, metadata):
140+ def on_mark_package(self, metadata, dependency=False):
141 """
142 Get a list of dependencies based on package metadata
143 """
144@@ -436,7 +443,8 @@
145 self.status[metadata["Package"]]["Status"]
146
147 # Mark the package itself
148- metadata["Status"] = "to be downloaded"
149+ if not dependency: metadata["Status"] = "to be downloaded"
150+ else: metadata["Status"] = "dependency to be downloaded"
151 self.status[metadata["Package"]] = metadata
152
153 logging.info("Finding dependencies for %s..." % metadata["Package"])
154@@ -492,7 +500,7 @@
155
156 # Mark sub-dependencies as well
157 if pkg:
158- self.on_mark_package(pkg)
159+ self.on_mark_package(pkg, dependency=True)
160
161
162 def on_apply_changes(self):
163@@ -500,7 +508,7 @@
164 directory = os.path.join(self.download_directory, "packages")
165
166 # Build the list of package urls to download
167- downloads = [(key, value["Repository"]["url"].split("dists")[0] + value["Filename"]) for key, value in self.status.items() if value["Status"] == "to be downloaded"]
168+ downloads = [(key, value["Repository"]["url"].split("dists")[0] + value["Filename"]) for key, value in self.status.items() if value["Status"] in ["to be downloaded", "dependency to be downloaded"]]
169
170 #downloads = []
171 #for key, value in self.status.items():
172@@ -517,12 +525,16 @@
173 for key, url in downloads:
174 download_url(url, "%s/%s" % (directory, url.rsplit("/", 1)[1]), proxy=self.proxy["proxy"], username=self.proxy["user"], password=self.proxy["pass"])
175 # Once it's downloaded, mark this package status to "to be installed"
176- self.status[key]["Status"] = "to be installed"
177+ # or "dependency to be installed", depending on what it is now.
178+ if self.status[key]["Status"] == "to be downloaded":
179+ self.status[key]["Status"] = "to be installed"
180+ elif self.status[key]["Status"] == "dependency to be downloaded":
181+ self.status[key]["Status"] = "dependency to be installed"
182
183
184 def on_save_changes(self, status):
185
186- # This will NOT create a staus file to override /var/lib/dpkg/status
187+ # This will NOT create a status file to override /var/lib/dpkg/status
188 # so DO NOT try to replace the system status file.
189 # YOU HAVE BEEN WARNED
190
191@@ -541,22 +553,19 @@
192
193
194 def on_cancel_changes(self, downloads, installs):
195-
196- cancellations = []
197
198 for key, value in self.status.items():
199- if downloads and value["Status"] == "to be downloaded" or \
200- installs and value["Status"] == "to be installed":
201- cancellations.append(key)
202-
203- for key in cancellations:
204- del self.status[key]
205+ if downloads and value["Status"] in \
206+ ["to be downloaded", "dependency to be downloaded"] or \
207+ installs and value["Status"] in \
208+ ["to be installed", "dependency to be installed"]:
209+ del self.status[key]
210
211
212 def on_get_changes_size(self):
213
214 # Build list of packages to be downloaded
215- packages = [(value["Package"], value["Version"]) for key, value in self.status.items() if value["Status"] == "to be downloaded"]
216+ packages = [(value["Package"], value["Version"]) for key, value in self.status.items() if value["Status"] in ["to be downloaded", "dependency to be downloaded"]]
217
218 count = 0
219 total = 0
220@@ -582,36 +591,36 @@
221 We will take the approach of installing by copying the lists to
222 /var/lib/apt/lists and the packages to /var/cache/apt/archives and
223 calling apt-get update and then apt-get install on the packages
224- which have the stats of "to be installed". This prevents tampering
225+ which have the status of "to be installed". This prevents tampering
226 with sources.list and works more or less the exact same if we made
227 a local repository.
228 """
229
230- if not os.geteuid()==0:
231+ if not os.geteuid() == 0:
232 raise PermissionsError, "You may only install as root"
233
234 # Copy lists over
235- try:
236- for repo in self.__iter_repositories():
237- url = to_url(repo, self.architecture, "Packages")
238- filename = to_filename(os.path.join(self.download_directory, "lists"), url)
239+ for repo in self.__iter_repositories():
240+ url = to_url(repo, self.architecture, "Packages")
241+ filename = to_filename(os.path.join(self.download_directory, "lists"), url)
242
243+ try:
244 # Extract the gz
245 g = gzip.open("%s.gz" % filename, "rb")
246 f = open(os.path.join("/var/lib/apt/lists", os.path.basename(filename)), "wb")
247 f.write(g.read())
248 f.close()
249 g.close()
250- except IOError, e:
251- # We will just ignore this, it only trip out if the user did download=False on update()
252- pass
253+ except IOError, e:
254+ # We will just ignore this, it only trip out if the user did download=False on update()
255+ pass
256
257
258 # Copy packages over
259 for key, value in self.status.items():
260- if value["Status"] == "to be installed":
261+ if value["Status"] in ["to be installed", "dependency to be installed"]:
262 pkg_filename = self.get_binary_version(value["Package"], value["Version"])["Filename"].rsplit("/", 1)[1]
263- filename = os.path.join(self.download_directory, os.path.join("packages", pkg_filename))
264+ filename = os.path.join(self.download_directory, "packages", pkg_filename)
265 dest = os.path.join("/var/cache/apt/archives", os.path.basename(filename))
266 shutil.copyfile(filename, dest)
267
268@@ -619,6 +628,8 @@
269 # Call apt-get install with the packages
270 packages = [value["Package"] for key, value in self.status.items() if value["Status"] == "to be installed"]
271
272+ #FIXME: apt-get update will fail when installing on an offline machine.
273+ # `apt-cache gencaches` should be used.
274 subprocess.call("apt-get update", shell=True)
275 subprocess.call("apt-get -y install %s" % " ".join(packages), shell=True)
276
277@@ -642,5 +653,3 @@
278
279
280 return upgrades
281-
282-

Subscribers

People subscribed via source and target branches