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
=== modified file 'example.py'
--- example.py 2010-08-01 19:34:08 +0000
+++ example.py 2010-08-11 19:11:23 +0000
@@ -51,7 +51,10 @@
5151
52print "%i available packages" % len(apt.get_available_package_names())52print "%i available packages" % len(apt.get_available_package_names())
5353
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
55# to individually specify the download directories.
56#apt.set_download_directory("/var/lib/apt")
57#apt.update(download=False)
5558
56apt.update()59apt.update()
5760
5861
=== modified file 'unwrapt/DefinitionBase.py'
--- unwrapt/DefinitionBase.py 2010-08-01 19:34:08 +0000
+++ unwrapt/DefinitionBase.py 2010-08-11 19:11:23 +0000
@@ -90,6 +90,7 @@
90 90
91 - directory is the location of the 91 - directory is the location of the
92 """92 """
93 #FIXME: that docstring.
93 94
94 self.download_directory = os.path.abspath(os.path.expanduser(directory))95 self.download_directory = os.path.abspath(os.path.expanduser(directory))
9596
@@ -163,6 +164,7 @@
163 164
164 pass165 pass
165166
167
166 @callback 168 @callback
167 def update(self, reporthook=None, directory=None, download=True):169 def update(self, reporthook=None, directory=None, download=True):
168 """170 """
@@ -202,6 +204,7 @@
202 204
203 pass205 pass
204 206
207
205 @callback208 @callback
206 def get_latest_binary(self, package):209 def get_latest_binary(self, package):
207 """210 """
@@ -268,7 +271,8 @@
268 271
269 For example:272 For example:
270 273
271 client.mark_package("firefox")274 package = client.get_latest_binary("firefox")
275 client.mark_package(package)
272 """276 """
273 277
274 pass278 pass
@@ -289,7 +293,6 @@
289 For example:293 For example:
290 294
291 client.apply_changes()295 client.apply_changes()
292
293 """296 """
294 297
295 pass298 pass
296299
=== modified file 'unwrapt/definitions/aptdef/__init__.py'
--- unwrapt/definitions/aptdef/__init__.py 2010-08-01 19:34:08 +0000
+++ unwrapt/definitions/aptdef/__init__.py 2010-08-11 19:11:23 +0000
@@ -51,14 +51,12 @@
5151
5252
53#TODO: Move this code to proper library location53#TODO: Move this code to proper library location
54def url_join(first, last):54def url_join(*args):
55 """ Returns full URL """55 """ Returns full URL """
56 if first.endswith('/'):56 # Strip any leading or trailing slashes from the parts.
57 if last.startswith('/'): return first + last[1:]57 args = [x.strip("/") for x in args]
58 else: return first + last58
59 else:59 return "/".join(args)
60 if last.startswith('/'): return first + last
61 else: return first + '/' + last
6260
6361
64#class Repository(Base):62#class Repository(Base):
@@ -108,10 +106,15 @@
108106
109107
110def to_url(repository, architecture, format):108def to_url(repository, architecture, format):
111 return url_join(repository["url"], url_join(architecture, format))109 return url_join(repository["url"], architecture, format)
112110
113111
114def to_filename(directory, url):112def to_filename(directory, url):
113 """
114 Forms a full filename from a directory and url.
115 i.e. Strips the url of the protocol prefix, replacse all slashes with
116 underscores, and appends it to directory.
117 """
115 return os.path.join(directory, url.split("//")[1].replace("/", "_"))118 return os.path.join(directory, url.split("//")[1].replace("/", "_"))
116119
117 120
@@ -147,8 +150,13 @@
147 supported = ["amd64", "armel", "i386", "ia64", "powerpc", "sparc"]150 supported = ["amd64", "armel", "i386", "ia64", "powerpc", "sparc"]
148 status_properties = ["Package", "Version", "Status", "Provides"]151 status_properties = ["Package", "Version", "Status", "Provides"]
149 binary_dependencies = ["Pre-Depends", "Depends", "Recommends"]152 binary_dependencies = ["Pre-Depends", "Depends", "Recommends"]
150 supported_statuses = ["install ok installed", "to be installed", "to be downloaded"]153 supported_statuses = ["install ok installed",
154 "to be downloaded",
155 "dependency to be downloaded",
156 "to be installed",
157 "dependency to be installed"]
151158
159 #FIXME: This seems redundant. Could it be moved to DefinitionBase?
152 def on_set_proxy(self, proxy, username=None, password=None):160 def on_set_proxy(self, proxy, username=None, password=None):
153 self.proxy = {"proxy": proxy,161 self.proxy = {"proxy": proxy,
154 "user": username,162 "user": username,
@@ -184,7 +192,7 @@
184 self.repositories[count]["url"] = url192 self.repositories[count]["url"] = url
185 self.repositories[count]["dist"] = dist193 self.repositories[count]["dist"] = dist
186 self.repositories[count]["section"] = section194 self.repositories[count]["section"] = section
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)
188196
189 count += 1197 count += 1
190198
@@ -328,7 +336,6 @@
328 installed statuses.336 installed statuses.
329 """337 """
330338
331
332 f = open(status, "rb")339 f = open(status, "rb")
333 340
334 self.status = {}341 self.status = {}
@@ -420,7 +427,7 @@
420 return self.packages[package]427 return self.packages[package]
421428
422429
423 def on_mark_package(self, metadata):430 def on_mark_package(self, metadata, dependency=False):
424 """431 """
425 Get a list of dependencies based on package metadata432 Get a list of dependencies based on package metadata
426 """433 """
@@ -436,7 +443,8 @@
436 self.status[metadata["Package"]]["Status"]443 self.status[metadata["Package"]]["Status"]
437 444
438 # Mark the package itself445 # Mark the package itself
439 metadata["Status"] = "to be downloaded"446 if not dependency: metadata["Status"] = "to be downloaded"
447 else: metadata["Status"] = "dependency to be downloaded"
440 self.status[metadata["Package"]] = metadata448 self.status[metadata["Package"]] = metadata
441449
442 logging.info("Finding dependencies for %s..." % metadata["Package"])450 logging.info("Finding dependencies for %s..." % metadata["Package"])
@@ -492,7 +500,7 @@
492 500
493 # Mark sub-dependencies as well501 # Mark sub-dependencies as well
494 if pkg:502 if pkg:
495 self.on_mark_package(pkg)503 self.on_mark_package(pkg, dependency=True)
496 504
497 505
498 def on_apply_changes(self):506 def on_apply_changes(self):
@@ -500,7 +508,7 @@
500 directory = os.path.join(self.download_directory, "packages")508 directory = os.path.join(self.download_directory, "packages")
501 509
502 # Build the list of package urls to download510 # Build the list of package urls to download
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"]]
504 512
505 #downloads = []513 #downloads = []
506 #for key, value in self.status.items():514 #for key, value in self.status.items():
@@ -517,12 +525,16 @@
517 for key, url in downloads:525 for key, url in downloads:
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"])
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"
520 self.status[key]["Status"] = "to be installed"528 # or "dependency to be installed", depending on what it is now.
529 if self.status[key]["Status"] == "to be downloaded":
530 self.status[key]["Status"] = "to be installed"
531 elif self.status[key]["Status"] == "dependency to be downloaded":
532 self.status[key]["Status"] = "dependency to be installed"
521 533
522 534
523 def on_save_changes(self, status):535 def on_save_changes(self, status):
524 536
525 # This will NOT create a staus file to override /var/lib/dpkg/status537 # This will NOT create a status file to override /var/lib/dpkg/status
526 # so DO NOT try to replace the system status file.538 # so DO NOT try to replace the system status file.
527 # YOU HAVE BEEN WARNED539 # YOU HAVE BEEN WARNED
528 540
@@ -541,22 +553,19 @@
541 553
542 554
543 def on_cancel_changes(self, downloads, installs):555 def on_cancel_changes(self, downloads, installs):
544
545 cancellations = []
546 556
547 for key, value in self.status.items():557 for key, value in self.status.items():
548 if downloads and value["Status"] == "to be downloaded" or \558 if downloads and value["Status"] in \
549 installs and value["Status"] == "to be installed":559 ["to be downloaded", "dependency to be downloaded"] or \
550 cancellations.append(key)560 installs and value["Status"] in \
551 561 ["to be installed", "dependency to be installed"]:
552 for key in cancellations:562 del self.status[key]
553 del self.status[key]
554 563
555 564
556 def on_get_changes_size(self):565 def on_get_changes_size(self):
557 566
558 # Build list of packages to be downloaded567 # Build list of packages to be downloaded
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"]]
560569
561 count = 0570 count = 0
562 total = 0 571 total = 0
@@ -582,36 +591,36 @@
582 We will take the approach of installing by copying the lists to591 We will take the approach of installing by copying the lists to
583 /var/lib/apt/lists and the packages to /var/cache/apt/archives and592 /var/lib/apt/lists and the packages to /var/cache/apt/archives and
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
585 which have the stats of "to be installed". This prevents tampering594 which have the status of "to be installed". This prevents tampering
586 with sources.list and works more or less the exact same if we made595 with sources.list and works more or less the exact same if we made
587 a local repository.596 a local repository.
588 """597 """
589 598
590 if not os.geteuid()==0: 599 if not os.geteuid() == 0:
591 raise PermissionsError, "You may only install as root"600 raise PermissionsError, "You may only install as root"
592 601
593 # Copy lists over602 # Copy lists over
594 try:603 for repo in self.__iter_repositories():
595 for repo in self.__iter_repositories():604 url = to_url(repo, self.architecture, "Packages")
596 url = to_url(repo, self.architecture, "Packages")605 filename = to_filename(os.path.join(self.download_directory, "lists"), url)
597 filename = to_filename(os.path.join(self.download_directory, "lists"), url)
598606
607 try:
599 # Extract the gz608 # Extract the gz
600 g = gzip.open("%s.gz" % filename, "rb")609 g = gzip.open("%s.gz" % filename, "rb")
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")
602 f.write(g.read())611 f.write(g.read())
603 f.close()612 f.close()
604 g.close()613 g.close()
605 except IOError, e:614 except IOError, e:
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()
607 pass616 pass
608617
609 618
610 # Copy packages over619 # Copy packages over
611 for key, value in self.status.items():620 for key, value in self.status.items():
612 if value["Status"] == "to be installed":621 if value["Status"] in ["to be installed", "dependency to be installed"]:
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]
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)
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))
616 shutil.copyfile(filename, dest)625 shutil.copyfile(filename, dest)
617626
@@ -619,6 +628,8 @@
619 # Call apt-get install with the packages628 # Call apt-get install with the packages
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"]
621 630
631 #FIXME: apt-get update will fail when installing on an offline machine.
632 # `apt-cache gencaches` should be used.
622 subprocess.call("apt-get update", shell=True)633 subprocess.call("apt-get update", shell=True)
623 subprocess.call("apt-get -y install %s" % " ".join(packages), shell=True)634 subprocess.call("apt-get -y install %s" % " ".join(packages), shell=True)
624 635
@@ -642,5 +653,3 @@
642653
643 654
644 return upgrades655 return upgrades
645
646

Subscribers

People subscribed via source and target branches