summaryrefslogtreecommitdiff
path: root/apt
diff options
context:
space:
mode:
Diffstat (limited to 'apt')
-rw-r--r--apt/cache.py65
-rw-r--r--apt/package.py16
-rw-r--r--apt/progress/__init__.py42
3 files changed, 98 insertions, 25 deletions
diff --git a/apt/cache.py b/apt/cache.py
index 94a77fd8..c1e2428c 100644
--- a/apt/cache.py
+++ b/apt/cache.py
@@ -20,6 +20,7 @@
# USA
import os
+import weakref
import apt_pkg
from apt import Package
@@ -47,6 +48,8 @@ class Cache(object):
def __init__(self, progress=None, rootdir=None, memonly=False):
self._callbacks = {}
+ self._weakref = weakref.WeakValueDictionary()
+ self._set = set()
if memonly:
# force apt to build its caches in memory
apt_pkg.Config.Set("Dir::Cache::pkgcache", "")
@@ -63,6 +66,9 @@ class Cache(object):
# create required dirs/files when run with special rootdir
# automatically
self._check_and_create_required_dirs(rootdir)
+ # Call InitSystem so the change to Dir::State::Status is actually
+ # recognized (LP: #320665)
+ apt_pkg.InitSystem()
self.open(progress)
def _check_and_create_required_dirs(self, rootdir):
@@ -93,7 +99,7 @@ class Cache(object):
for callback in self._callbacks[name]:
callback()
- def open(self, progress):
+ def open(self, progress=None):
""" Open the package cache, after that it can be used like
a dictionary
"""
@@ -105,7 +111,8 @@ class Cache(object):
self._records = apt_pkg.GetPkgRecords(self._cache)
self._list = apt_pkg.GetPkgSourceList()
self._list.ReadMainList()
- self._dict = {}
+ self._set.clear()
+ self._weakref.clear()
progress.Op = "Building data structures"
i=last=0
@@ -116,7 +123,7 @@ class Cache(object):
last=i
# drop stuff with no versions (cruft)
if len(pkg.VersionList) > 0:
- self._dict[pkg.Name] = Package(self, pkg)
+ self._set.add(pkg.Name)
i += 1
@@ -125,30 +132,36 @@ class Cache(object):
def __getitem__(self, key):
""" look like a dictionary (get key) """
- return self._dict[key]
+ try:
+ return self._weakref[key]
+ except KeyError:
+ if key in self._set:
+ pkg = self._weakref[key] = Package(self, self._cache[key])
+ return pkg
+ else:
+ raise KeyError('The cache has no package named %r' % key)
def __iter__(self):
- for pkgname in self._dict.keys():
- yield self._dict[pkgname]
+ for pkgname in self._set:
+ yield self[pkgname]
raise StopIteration
def has_key(self, key):
- return (key in self._dict)
+ return (key in self._set)
def __contains__(self, key):
- return (key in self._dict)
+ return (key in self._set)
def __len__(self):
- return len(self._dict)
+ return len(self._set)
def keys(self):
- return self._dict.keys()
+ return list(self._set)
def getChanges(self):
""" Get the marked changes """
changes = []
- for name in self._dict.keys():
- p = self._dict[name]
+ for p in self:
if p.markedUpgrade or p.markedInstall or p.markedDelete or \
p.markedDowngrade or p.markedReinstall:
changes.append(p)
@@ -329,6 +342,26 @@ class Cache(object):
self._callbacks[name] = []
self._callbacks[name].append(callback)
+ @property
+ def broken_count(self):
+ """Return the number of packages with broken dependencies."""
+ return self._depcache.broken_count
+
+ @property
+ def delete_count(self):
+ """Return the number of packages marked for deletion."""
+ return self._depcache.del_count
+
+ @property
+ def install_count(self):
+ """Return the number of packages marked for installation."""
+ return self._depcache.inst_count
+
+ @property
+ def keep_count(self):
+ """Return the number of packages marked as keep."""
+ return self._depcache.keep_count
+
# ----------------------------- experimental interface
@@ -373,7 +406,7 @@ class FilteredCache(object):
return len(self._filtered)
def __getitem__(self, key):
- return self.cache._dict[key]
+ return self.cache[key]
def __iter__(self):
for pkgname in self._filtered:
@@ -391,10 +424,10 @@ class FilteredCache(object):
def _reapplyFilter(self):
" internal helper to refilter "
self._filtered = {}
- for pkg in self.cache._dict.keys():
+ for pkg in self.cache:
for f in self._filters:
- if f.apply(self.cache._dict[pkg]):
- self._filtered[pkg] = 1
+ if f.apply(pkg):
+ self._filtered[pkg.name] = 1
break
def setFilter(self, filter):
diff --git a/apt/package.py b/apt/package.py
index ec88a456..f5bdc47d 100644
--- a/apt/package.py
+++ b/apt/package.py
@@ -486,15 +486,25 @@ class Package(object):
def __repr__(self):
return '<Package: name:%r id:%r>' % (self._pkg.Name, self._pkg.ID)
- @property
def candidate(self):
"""Return the candidate version of the package.
-
- :since: 0.7.9"""
+
+ This property is writeable to allow you to set the candidate version
+ of the package. Just assign a Version() object, and it will be set as
+ the candidate version.
+ """
cand = self._pcache._depcache.GetCandidateVer(self._pkg)
if cand is not None:
return Version(self, cand)
+ def __set_candidate(self, version):
+ """Set the candidate version of the package."""
+ self._pcache.cachePreChange()
+ self._pcache._depcache.SetCandidateVer(self._pkg, version._cand)
+ self._pcache.cachePostChange()
+
+ candidate = property(candidate, __set_candidate)
+
@property
def installed(self):
"""Return the currently installed version of the package.
diff --git a/apt/progress/__init__.py b/apt/progress/__init__.py
index 769942ce..b9288c2c 100644
--- a/apt/progress/__init__.py
+++ b/apt/progress/__init__.py
@@ -112,6 +112,13 @@ class FetchProgress(object):
This happens eg. when the downloads fails or is completed.
"""
+ def update_status_full(self, uri, descr, short_descr, status, file_size,
+ partial_size):
+ """Called when the status of an item changes.
+
+ This happens eg. when the downloads fails or is completed. This
+ version include information on current filesize and partial size
+ """
def pulse(self):
"""Called periodically to update the user interface.
@@ -125,6 +132,19 @@ class FetchProgress(object):
float(self.currentCPS))
return True
+ def pulse_items(self, items):
+ """Called periodically to update the user interface.
+ This function includes details about the items being fetched
+ Return True to continue or False to cancel.
+
+ """
+ self.percent = (((self.currentBytes + self.currentItems) * 100.0) /
+ float(self.totalBytes + self.totalItems))
+ if self.currentCPS > 0:
+ self.eta = ((self.totalBytes - self.currentBytes) /
+ float(self.currentCPS))
+ return True
+
def mediaChange(self, medium, drive):
"""react to media change events."""
@@ -266,11 +286,20 @@ class InstallProgress(DumbInstallProgress):
def waitChild(self):
"""Wait for child progress to exit."""
while True:
- select.select([self.statusfd], [], [], self.selectTimeout)
- self.updateInterface()
- (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
- if pid == self.child_pid:
+ try:
+ select.select([self.statusfd], [], [], self.selectTimeout)
+ except select.error, (errno_, errstr):
+ if errno_ != errno.EINTR:
+ raise
break
+ self.updateInterface()
+ try:
+ (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
+ if pid == self.child_pid:
+ break
+ except OSError, (errno_, errstr):
+ if errno_ != errno.EINTR:
+ raise
return res
def run(self, pm):
@@ -315,7 +344,7 @@ class DpkgInstallProgress(InstallProgress):
if pid == 0:
# child
res = os.system("/usr/bin/dpkg --status-fd %s -i %s" % \
- (self.writefd, self.debfile))
+ (self.writefd, debfile))
os._exit(os.WEXITSTATUS(res))
self.child_pid = pid
res = self.waitChild()
@@ -341,10 +370,11 @@ class DpkgInstallProgress(InstallProgress):
print "got garbage from dpkg: '%s'" % self.read
self.read = ""
break
+ pkg_name = statusl[1].strip()
status = statusl[2].strip()
#print status
if status == "error":
- self.error(self.debname, status)
+ self.error(pkg_name, status)
elif status == "conffile-prompt":
# we get a string like this:
# 'current-conffile' 'new-conffile' useredited distedited