Merge lp:~mac9416/unwrapt/dependency_statuses into lp:unwrapt
- dependency_statuses
- Merge into stable
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 |
Related bugs: |
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.
Commit message
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 : | # |
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 | 51 | 51 | ||
6 | 52 | print "%i available packages" % len(apt.get_available_package_names()) | 52 | print "%i available packages" % len(apt.get_available_package_names()) |
7 | 53 | 53 | ||
9 | 54 | #apt.update(directory="/var/lib/apt/lists", download=False) | 54 | #FIXME: /var/lib/apt is not the download directory. We should have the ability |
10 | 55 | # to individually specify the download directories. | ||
11 | 56 | #apt.set_download_directory("/var/lib/apt") | ||
12 | 57 | #apt.update(download=False) | ||
13 | 55 | 58 | ||
14 | 56 | apt.update() | 59 | apt.update() |
15 | 57 | 60 | ||
16 | 58 | 61 | ||
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 | 90 | 90 | ||
22 | 91 | - directory is the location of the | 91 | - directory is the location of the |
23 | 92 | """ | 92 | """ |
24 | 93 | #FIXME: that docstring. | ||
25 | 93 | 94 | ||
26 | 94 | self.download_directory = os.path.abspath(os.path.expanduser(directory)) | 95 | self.download_directory = os.path.abspath(os.path.expanduser(directory)) |
27 | 95 | 96 | ||
28 | @@ -163,6 +164,7 @@ | |||
29 | 163 | 164 | ||
30 | 164 | pass | 165 | pass |
31 | 165 | 166 | ||
32 | 167 | |||
33 | 166 | @callback | 168 | @callback |
34 | 167 | def update(self, reporthook=None, directory=None, download=True): | 169 | def update(self, reporthook=None, directory=None, download=True): |
35 | 168 | """ | 170 | """ |
36 | @@ -202,6 +204,7 @@ | |||
37 | 202 | 204 | ||
38 | 203 | pass | 205 | pass |
39 | 204 | 206 | ||
40 | 207 | |||
41 | 205 | @callback | 208 | @callback |
42 | 206 | def get_latest_binary(self, package): | 209 | def get_latest_binary(self, package): |
43 | 207 | """ | 210 | """ |
44 | @@ -268,7 +271,8 @@ | |||
45 | 268 | 271 | ||
46 | 269 | For example: | 272 | For example: |
47 | 270 | 273 | ||
49 | 271 | client.mark_package("firefox") | 274 | package = client.get_latest_binary("firefox") |
50 | 275 | client.mark_package(package) | ||
51 | 272 | """ | 276 | """ |
52 | 273 | 277 | ||
53 | 274 | pass | 278 | pass |
54 | @@ -289,7 +293,6 @@ | |||
55 | 289 | For example: | 293 | For example: |
56 | 290 | 294 | ||
57 | 291 | client.apply_changes() | 295 | client.apply_changes() |
58 | 292 | |||
59 | 293 | """ | 296 | """ |
60 | 294 | 297 | ||
61 | 295 | pass | 298 | pass |
62 | 296 | 299 | ||
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 | 51 | 51 | ||
68 | 52 | 52 | ||
69 | 53 | #TODO: Move this code to proper library location | 53 | #TODO: Move this code to proper library location |
71 | 54 | def url_join(first, last): | 54 | def url_join(*args): |
72 | 55 | """ Returns full URL """ | 55 | """ Returns full URL """ |
79 | 56 | if first.endswith('/'): | 56 | # Strip any leading or trailing slashes from the parts. |
80 | 57 | if last.startswith('/'): return first + last[1:] | 57 | args = [x.strip("/") for x in args] |
81 | 58 | else: return first + last | 58 | |
82 | 59 | else: | 59 | return "/".join(args) |
77 | 60 | if last.startswith('/'): return first + last | ||
78 | 61 | else: return first + '/' + last | ||
83 | 62 | 60 | ||
84 | 63 | 61 | ||
85 | 64 | #class Repository(Base): | 62 | #class Repository(Base): |
86 | @@ -108,10 +106,15 @@ | |||
87 | 108 | 106 | ||
88 | 109 | 107 | ||
89 | 110 | def to_url(repository, architecture, format): | 108 | def to_url(repository, architecture, format): |
91 | 111 | return url_join(repository["url"], url_join(architecture, format)) | 109 | return url_join(repository["url"], architecture, format) |
92 | 112 | 110 | ||
93 | 113 | 111 | ||
94 | 114 | def to_filename(directory, url): | 112 | def to_filename(directory, url): |
95 | 113 | """ | ||
96 | 114 | Forms a full filename from a directory and url. | ||
97 | 115 | i.e. Strips the url of the protocol prefix, replacse all slashes with | ||
98 | 116 | underscores, and appends it to directory. | ||
99 | 117 | """ | ||
100 | 115 | return os.path.join(directory, url.split("//")[1].replace("/", "_")) | 118 | return os.path.join(directory, url.split("//")[1].replace("/", "_")) |
101 | 116 | 119 | ||
102 | 117 | 120 | ||
103 | @@ -147,8 +150,13 @@ | |||
104 | 147 | supported = ["amd64", "armel", "i386", "ia64", "powerpc", "sparc"] | 150 | supported = ["amd64", "armel", "i386", "ia64", "powerpc", "sparc"] |
105 | 148 | status_properties = ["Package", "Version", "Status", "Provides"] | 151 | status_properties = ["Package", "Version", "Status", "Provides"] |
106 | 149 | binary_dependencies = ["Pre-Depends", "Depends", "Recommends"] | 152 | binary_dependencies = ["Pre-Depends", "Depends", "Recommends"] |
108 | 150 | supported_statuses = ["install ok installed", "to be installed", "to be downloaded"] | 153 | supported_statuses = ["install ok installed", |
109 | 154 | "to be downloaded", | ||
110 | 155 | "dependency to be downloaded", | ||
111 | 156 | "to be installed", | ||
112 | 157 | "dependency to be installed"] | ||
113 | 151 | 158 | ||
114 | 159 | #FIXME: This seems redundant. Could it be moved to DefinitionBase? | ||
115 | 152 | def on_set_proxy(self, proxy, username=None, password=None): | 160 | def on_set_proxy(self, proxy, username=None, password=None): |
116 | 153 | self.proxy = {"proxy": proxy, | 161 | self.proxy = {"proxy": proxy, |
117 | 154 | "user": username, | 162 | "user": username, |
118 | @@ -184,7 +192,7 @@ | |||
119 | 184 | self.repositories[count]["url"] = url | 192 | self.repositories[count]["url"] = url |
120 | 185 | self.repositories[count]["dist"] = dist | 193 | self.repositories[count]["dist"] = dist |
121 | 186 | self.repositories[count]["section"] = section | 194 | self.repositories[count]["section"] = section |
123 | 187 | self.repositories[count]["url"] = url_join(url, url_join("dists", url_join(dist, section))) | 195 | self.repositories[count]["url"] = url_join(url, "dists", dist, section) |
124 | 188 | 196 | ||
125 | 189 | count += 1 | 197 | count += 1 |
126 | 190 | 198 | ||
127 | @@ -328,7 +336,6 @@ | |||
128 | 328 | installed statuses. | 336 | installed statuses. |
129 | 329 | """ | 337 | """ |
130 | 330 | 338 | ||
131 | 331 | |||
132 | 332 | f = open(status, "rb") | 339 | f = open(status, "rb") |
133 | 333 | 340 | ||
134 | 334 | self.status = {} | 341 | self.status = {} |
135 | @@ -420,7 +427,7 @@ | |||
136 | 420 | return self.packages[package] | 427 | return self.packages[package] |
137 | 421 | 428 | ||
138 | 422 | 429 | ||
140 | 423 | def on_mark_package(self, metadata): | 430 | def on_mark_package(self, metadata, dependency=False): |
141 | 424 | """ | 431 | """ |
142 | 425 | Get a list of dependencies based on package metadata | 432 | Get a list of dependencies based on package metadata |
143 | 426 | """ | 433 | """ |
144 | @@ -436,7 +443,8 @@ | |||
145 | 436 | self.status[metadata["Package"]]["Status"] | 443 | self.status[metadata["Package"]]["Status"] |
146 | 437 | 444 | ||
147 | 438 | # Mark the package itself | 445 | # Mark the package itself |
149 | 439 | metadata["Status"] = "to be downloaded" | 446 | if not dependency: metadata["Status"] = "to be downloaded" |
150 | 447 | else: metadata["Status"] = "dependency to be downloaded" | ||
151 | 440 | self.status[metadata["Package"]] = metadata | 448 | self.status[metadata["Package"]] = metadata |
152 | 441 | 449 | ||
153 | 442 | logging.info("Finding dependencies for %s..." % metadata["Package"]) | 450 | logging.info("Finding dependencies for %s..." % metadata["Package"]) |
154 | @@ -492,7 +500,7 @@ | |||
155 | 492 | 500 | ||
156 | 493 | # Mark sub-dependencies as well | 501 | # Mark sub-dependencies as well |
157 | 494 | if pkg: | 502 | if pkg: |
159 | 495 | self.on_mark_package(pkg) | 503 | self.on_mark_package(pkg, dependency=True) |
160 | 496 | 504 | ||
161 | 497 | 505 | ||
162 | 498 | def on_apply_changes(self): | 506 | def on_apply_changes(self): |
163 | @@ -500,7 +508,7 @@ | |||
164 | 500 | directory = os.path.join(self.download_directory, "packages") | 508 | directory = os.path.join(self.download_directory, "packages") |
165 | 501 | 509 | ||
166 | 502 | # Build the list of package urls to download | 510 | # Build the list of package urls to download |
168 | 503 | downloads = [(key, value["Repository"]["url"].split("dists")[0] + value["Filename"]) for key, value in self.status.items() if value["Status"] == "to be downloaded"] | 511 | 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 | 504 | 512 | ||
170 | 505 | #downloads = [] | 513 | #downloads = [] |
171 | 506 | #for key, value in self.status.items(): | 514 | #for key, value in self.status.items(): |
172 | @@ -517,12 +525,16 @@ | |||
173 | 517 | for key, url in downloads: | 525 | for key, url in downloads: |
174 | 518 | download_url(url, "%s/%s" % (directory, url.rsplit("/", 1)[1]), proxy=self.proxy["proxy"], username=self.proxy["user"], password=self.proxy["pass"]) | 526 | download_url(url, "%s/%s" % (directory, url.rsplit("/", 1)[1]), proxy=self.proxy["proxy"], username=self.proxy["user"], password=self.proxy["pass"]) |
175 | 519 | # Once it's downloaded, mark this package status to "to be installed" | 527 | # Once it's downloaded, mark this package status to "to be installed" |
177 | 520 | self.status[key]["Status"] = "to be installed" | 528 | # or "dependency to be installed", depending on what it is now. |
178 | 529 | if self.status[key]["Status"] == "to be downloaded": | ||
179 | 530 | self.status[key]["Status"] = "to be installed" | ||
180 | 531 | elif self.status[key]["Status"] == "dependency to be downloaded": | ||
181 | 532 | self.status[key]["Status"] = "dependency to be installed" | ||
182 | 521 | 533 | ||
183 | 522 | 534 | ||
184 | 523 | def on_save_changes(self, status): | 535 | def on_save_changes(self, status): |
185 | 524 | 536 | ||
187 | 525 | # This will NOT create a staus file to override /var/lib/dpkg/status | 537 | # This will NOT create a status file to override /var/lib/dpkg/status |
188 | 526 | # so DO NOT try to replace the system status file. | 538 | # so DO NOT try to replace the system status file. |
189 | 527 | # YOU HAVE BEEN WARNED | 539 | # YOU HAVE BEEN WARNED |
190 | 528 | 540 | ||
191 | @@ -541,22 +553,19 @@ | |||
192 | 541 | 553 | ||
193 | 542 | 554 | ||
194 | 543 | def on_cancel_changes(self, downloads, installs): | 555 | def on_cancel_changes(self, downloads, installs): |
195 | 544 | |||
196 | 545 | cancellations = [] | ||
197 | 546 | 556 | ||
198 | 547 | for key, value in self.status.items(): | 557 | for key, value in self.status.items(): |
205 | 548 | if downloads and value["Status"] == "to be downloaded" or \ | 558 | if downloads and value["Status"] in \ |
206 | 549 | installs and value["Status"] == "to be installed": | 559 | ["to be downloaded", "dependency to be downloaded"] or \ |
207 | 550 | cancellations.append(key) | 560 | installs and value["Status"] in \ |
208 | 551 | 561 | ["to be installed", "dependency to be installed"]: | |
209 | 552 | for key in cancellations: | 562 | del self.status[key] |
204 | 553 | del self.status[key] | ||
210 | 554 | 563 | ||
211 | 555 | 564 | ||
212 | 556 | def on_get_changes_size(self): | 565 | def on_get_changes_size(self): |
213 | 557 | 566 | ||
214 | 558 | # Build list of packages to be downloaded | 567 | # Build list of packages to be downloaded |
216 | 559 | packages = [(value["Package"], value["Version"]) for key, value in self.status.items() if value["Status"] == "to be downloaded"] | 568 | packages = [(value["Package"], value["Version"]) for key, value in self.status.items() if value["Status"] in ["to be downloaded", "dependency to be downloaded"]] |
217 | 560 | 569 | ||
218 | 561 | count = 0 | 570 | count = 0 |
219 | 562 | total = 0 | 571 | total = 0 |
220 | @@ -582,36 +591,36 @@ | |||
221 | 582 | We will take the approach of installing by copying the lists to | 591 | We will take the approach of installing by copying the lists to |
222 | 583 | /var/lib/apt/lists and the packages to /var/cache/apt/archives and | 592 | /var/lib/apt/lists and the packages to /var/cache/apt/archives and |
223 | 584 | calling apt-get update and then apt-get install on the packages | 593 | calling apt-get update and then apt-get install on the packages |
225 | 585 | which have the stats of "to be installed". This prevents tampering | 594 | which have the status of "to be installed". This prevents tampering |
226 | 586 | with sources.list and works more or less the exact same if we made | 595 | with sources.list and works more or less the exact same if we made |
227 | 587 | a local repository. | 596 | a local repository. |
228 | 588 | """ | 597 | """ |
229 | 589 | 598 | ||
231 | 590 | if not os.geteuid()==0: | 599 | if not os.geteuid() == 0: |
232 | 591 | raise PermissionsError, "You may only install as root" | 600 | raise PermissionsError, "You may only install as root" |
233 | 592 | 601 | ||
234 | 593 | # Copy lists over | 602 | # Copy lists over |
239 | 594 | try: | 603 | for repo in self.__iter_repositories(): |
240 | 595 | for repo in self.__iter_repositories(): | 604 | url = to_url(repo, self.architecture, "Packages") |
241 | 596 | url = to_url(repo, self.architecture, "Packages") | 605 | filename = to_filename(os.path.join(self.download_directory, "lists"), url) |
238 | 597 | filename = to_filename(os.path.join(self.download_directory, "lists"), url) | ||
242 | 598 | 606 | ||
243 | 607 | try: | ||
244 | 599 | # Extract the gz | 608 | # Extract the gz |
245 | 600 | g = gzip.open("%s.gz" % filename, "rb") | 609 | g = gzip.open("%s.gz" % filename, "rb") |
246 | 601 | f = open(os.path.join("/var/lib/apt/lists", os.path.basename(filename)), "wb") | 610 | f = open(os.path.join("/var/lib/apt/lists", os.path.basename(filename)), "wb") |
247 | 602 | f.write(g.read()) | 611 | f.write(g.read()) |
248 | 603 | f.close() | 612 | f.close() |
249 | 604 | g.close() | 613 | g.close() |
253 | 605 | except IOError, e: | 614 | except IOError, e: |
254 | 606 | # We will just ignore this, it only trip out if the user did download=False on update() | 615 | # We will just ignore this, it only trip out if the user did download=False on update() |
255 | 607 | pass | 616 | pass |
256 | 608 | 617 | ||
257 | 609 | 618 | ||
258 | 610 | # Copy packages over | 619 | # Copy packages over |
259 | 611 | for key, value in self.status.items(): | 620 | for key, value in self.status.items(): |
261 | 612 | if value["Status"] == "to be installed": | 621 | if value["Status"] in ["to be installed", "dependency to be installed"]: |
262 | 613 | pkg_filename = self.get_binary_version(value["Package"], value["Version"])["Filename"].rsplit("/", 1)[1] | 622 | pkg_filename = self.get_binary_version(value["Package"], value["Version"])["Filename"].rsplit("/", 1)[1] |
264 | 614 | filename = os.path.join(self.download_directory, os.path.join("packages", pkg_filename)) | 623 | filename = os.path.join(self.download_directory, "packages", pkg_filename) |
265 | 615 | dest = os.path.join("/var/cache/apt/archives", os.path.basename(filename)) | 624 | dest = os.path.join("/var/cache/apt/archives", os.path.basename(filename)) |
266 | 616 | shutil.copyfile(filename, dest) | 625 | shutil.copyfile(filename, dest) |
267 | 617 | 626 | ||
268 | @@ -619,6 +628,8 @@ | |||
269 | 619 | # Call apt-get install with the packages | 628 | # Call apt-get install with the packages |
270 | 620 | packages = [value["Package"] for key, value in self.status.items() if value["Status"] == "to be installed"] | 629 | packages = [value["Package"] for key, value in self.status.items() if value["Status"] == "to be installed"] |
271 | 621 | 630 | ||
272 | 631 | #FIXME: apt-get update will fail when installing on an offline machine. | ||
273 | 632 | # `apt-cache gencaches` should be used. | ||
274 | 622 | subprocess.call("apt-get update", shell=True) | 633 | subprocess.call("apt-get update", shell=True) |
275 | 623 | subprocess.call("apt-get -y install %s" % " ".join(packages), shell=True) | 634 | subprocess.call("apt-get -y install %s" % " ".join(packages), shell=True) |
276 | 624 | 635 | ||
277 | @@ -642,5 +653,3 @@ | |||
278 | 642 | 653 | ||
279 | 643 | 654 | ||
280 | 644 | return upgrades | 655 | return upgrades |
281 | 645 | |||
282 | 646 |
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?