home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / apt / cache.py < prev    next >
Encoding:
Python Source  |  2009-03-30  |  14.5 KB  |  464 lines

  1. # cache.py - apt cache abstraction
  2. #
  3. #  Copyright (c) 2005 Canonical
  4. #
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #
  7. #  This program is free software; you can redistribute it and/or
  8. #  modify it under the terms of the GNU General Public License as
  9. #  published by the Free Software Foundation; either version 2 of the
  10. #  License, or (at your option) any later version.
  11. #
  12. #  This program is distributed in the hope that it will be useful,
  13. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. #  GNU General Public License for more details.
  16. #
  17. #  You should have received a copy of the GNU General Public License
  18. #  along with this program; if not, write to the Free Software
  19. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  20. #  USA
  21.  
  22. import os
  23. import sys
  24.  
  25. import apt_pkg
  26. from apt import Package
  27. import apt.progress
  28.  
  29.  
  30. class FetchCancelledException(IOError):
  31.     """Exception that is thrown when the user cancels a fetch operation."""
  32.  
  33.  
  34. class FetchFailedException(IOError):
  35.     """Exception that is thrown when fetching fails."""
  36.  
  37.  
  38. class LockFailedException(IOError):
  39.     """Exception that is thrown when locking fails."""
  40.  
  41.  
  42. class Cache(object):
  43.     """Dictionary-like package cache.
  44.  
  45.     This class has all the packages that are available in it's
  46.     dictionary
  47.     """
  48.  
  49.     def __init__(self, progress=None, rootdir=None, memonly=False):
  50.         self._callbacks = {}
  51.         if memonly:
  52.             # force apt to build its caches in memory
  53.             apt_pkg.Config.Set("Dir::Cache::pkgcache", "")
  54.         if rootdir:
  55.             if os.path.exists(rootdir+"/etc/apt/apt.conf"):
  56.                 apt_pkg.ReadConfigFile(apt_pkg.Config, rootdir+"/etc/apt/apt.conf")
  57.             if os.path.isdir(rootdir+"/etc/apt/apt.conf.d"):
  58.                 apt_pkg.ReadConfigDir(apt_pkg.Config, rootdir+"/etc/apt/apt.conf.d")
  59.             apt_pkg.Config.Set("Dir", rootdir)
  60.             apt_pkg.Config.Set("Dir::State::status",
  61.                                rootdir + "/var/lib/dpkg/status")
  62.         self.open(progress)
  63.  
  64.     def _runCallbacks(self, name):
  65.         """ internal helper to run a callback """
  66.         if name in self._callbacks:
  67.             for callback in self._callbacks[name]:
  68.                 callback()
  69.  
  70.     def open(self, progress):
  71.         """ Open the package cache, after that it can be used like
  72.             a dictionary
  73.         """
  74.         self._runCallbacks("cache_pre_open")
  75.         self._cache = apt_pkg.GetCache(progress)
  76.         self._depcache = apt_pkg.GetDepCache(self._cache)
  77.         self._records = apt_pkg.GetPkgRecords(self._cache)
  78.         self._list = apt_pkg.GetPkgSourceList()
  79.         self._list.ReadMainList()
  80.         self._dict = {}
  81.  
  82.         # build the packages dict
  83.         if progress is not None:
  84.             progress.Op = "Building data structures"
  85.         i=last=0
  86.         size=len(self._cache.Packages)
  87.         for pkg in self._cache.Packages:
  88.             if progress is not None and last+100 < i:
  89.                 progress.update(i/float(size)*100)
  90.                 last=i
  91.             # drop stuff with no versions (cruft)
  92.             if len(pkg.VersionList) > 0:
  93.                 self._dict[pkg.Name] = Package(self._cache, self._depcache,
  94.                                                self._records, self._list,
  95.                                                self, pkg)
  96.  
  97.             i += 1
  98.         if progress is not None:
  99.             progress.done()
  100.         self._runCallbacks("cache_post_open")
  101.  
  102.     def __getitem__(self, key):
  103.         """ look like a dictionary (get key) """
  104.         return self._dict[key]
  105.  
  106.     def __iter__(self):
  107.         for pkgname in self._dict.keys():
  108.             yield self._dict[pkgname]
  109.         raise StopIteration
  110.  
  111.     def has_key(self, key):
  112.         return (key in self._dict)
  113.  
  114.     def __contains__(self, key):
  115.         return (key in self._dict)
  116.  
  117.     def __len__(self):
  118.         return len(self._dict)
  119.  
  120.     def keys(self):
  121.         return self._dict.keys()
  122.  
  123.     def getChanges(self):
  124.         """ Get the marked changes """
  125.         changes = []
  126.         for name in self._dict.keys():
  127.             p = self._dict[name]
  128.             if p.markedUpgrade or p.markedInstall or p.markedDelete or \
  129.                p.markedDowngrade or p.markedReinstall:
  130.                 changes.append(p)
  131.         return changes
  132.  
  133.     def upgrade(self, distUpgrade=False):
  134.         """ Upgrade the all package, DistUpgrade will also install
  135.             new dependencies
  136.         """
  137.         self.cachePreChange()
  138.         self._depcache.Upgrade(distUpgrade)
  139.         self.cachePostChange()
  140.  
  141.     @property
  142.     def requiredDownload(self):
  143.         """Get the size of the packages that are required to download."""
  144.         pm = apt_pkg.GetPackageManager(self._depcache)
  145.         fetcher = apt_pkg.GetAcquire()
  146.         pm.GetArchives(fetcher, self._list, self._records)
  147.         return fetcher.FetchNeeded
  148.  
  149.     @property
  150.     def additionalRequiredSpace(self):
  151.         """Get the size of the additional required space on the fs."""
  152.         return self._depcache.UsrSize
  153.  
  154.     @property
  155.     def reqReinstallPkgs(self):
  156.         """Return the packages not downloadable packages in reqreinst state."""
  157.         reqreinst = set()
  158.         for pkg in self:
  159.             if (not pkg.candidateDownloadable and
  160.                 (pkg._pkg.InstState == apt_pkg.InstStateReInstReq or
  161.                  pkg._pkg.InstState == apt_pkg.InstStateHoldReInstReq)):
  162.                 reqreinst.add(pkg.name)
  163.         return reqreinst
  164.  
  165.     def _runFetcher(self, fetcher):
  166.         # do the actual fetching
  167.         res = fetcher.Run()
  168.  
  169.         # now check the result (this is the code from apt-get.cc)
  170.         failed = False
  171.         transient = False
  172.         errMsg = ""
  173.         for item in fetcher.Items:
  174.             if item.Status == item.StatDone:
  175.                 continue
  176.             if item.StatIdle:
  177.                 transient = True
  178.                 continue
  179.             errMsg += "Failed to fetch %s %s\n" % (item.DescURI,
  180.                                                    item.ErrorText)
  181.             failed = True
  182.  
  183.         # we raise a exception if the download failed or it was cancelt
  184.         if res == fetcher.ResultCancelled:
  185.             raise FetchCancelledException(errMsg)
  186.         elif failed:
  187.             raise FetchFailedException(errMsg)
  188.         return res
  189.  
  190.     def _fetchArchives(self, fetcher, pm):
  191.         """ fetch the needed archives """
  192.  
  193.         # get lock
  194.         lockfile = apt_pkg.Config.FindDir("Dir::Cache::Archives") + "lock"
  195.         lock = apt_pkg.GetLock(lockfile)
  196.         if lock < 0:
  197.             raise LockFailedException("Failed to lock %s" % lockfile)
  198.  
  199.         try:
  200.             # this may as well throw a SystemError exception
  201.             if not pm.GetArchives(fetcher, self._list, self._records):
  202.                 return False
  203.             # now run the fetcher, throw exception if something fails to be
  204.             # fetched
  205.             return self._runFetcher(fetcher)
  206.         finally:
  207.             os.close(lock)
  208.  
  209.     def isVirtualPackage(self, pkgname):
  210.         """Return whether the package is a virtual package."""
  211.         pkg = self._cache[pkgname]
  212.         return bool(pkg.ProvidesList and not pkg.VersionList)
  213.  
  214.     def getProvidingPackages(self, virtual):
  215.         """
  216.         Return a list of packages which provide the virtual package of the
  217.         specified name
  218.         """
  219.         providers = []
  220.         try:
  221.             vp = self._cache[virtual]
  222.             if len(vp.VersionList) != 0:
  223.                 return providers
  224.         except KeyError:
  225.             return providers
  226.         for pkg in self:
  227.             v = self._depcache.GetCandidateVer(pkg._pkg)
  228.             if v is None:
  229.                 continue
  230.             for p in v.ProvidesList:
  231.                 if virtual == p[0]:
  232.                     # we found a pkg that provides this virtual pkg
  233.                     providers.append(pkg)
  234.         return providers
  235.  
  236.     def update(self, fetchProgress=None):
  237.         " run the equivalent of apt-get update "
  238.         lockfile = apt_pkg.Config.FindDir("Dir::State::Lists") + "lock"
  239.         lock = apt_pkg.GetLock(lockfile)
  240.         if lock < 0:
  241.             raise LockFailedException("Failed to lock %s" % lockfile)
  242.  
  243.         try:
  244.             if fetchProgress is None:
  245.                 fetchProgress = apt.progress.FetchProgress()
  246.             return self._cache.Update(fetchProgress, self._list)
  247.         finally:
  248.             os.close(lock)
  249.  
  250.     def installArchives(self, pm, installProgress):
  251.         installProgress.startUpdate()
  252.         res = installProgress.run(pm)
  253.         installProgress.finishUpdate()
  254.         return res
  255.  
  256.     def commit(self, fetchProgress=None, installProgress=None):
  257.         """ Apply the marked changes to the cache """
  258.         # FIXME:
  259.         # use the new acquire/pkgmanager interface here,
  260.         # raise exceptions when a download or install fails
  261.         # and send proper error strings to the application.
  262.         # Current a failed download will just display "error"
  263.         # which is less than optimal!
  264.  
  265.         if fetchProgress is None:
  266.             fetchProgress = apt.progress.FetchProgress()
  267.         if installProgress is None:
  268.             installProgress = apt.progress.InstallProgress()
  269.  
  270.         pm = apt_pkg.GetPackageManager(self._depcache)
  271.         fetcher = apt_pkg.GetAcquire(fetchProgress)
  272.         while True:
  273.             # fetch archives first
  274.             res = self._fetchArchives(fetcher, pm)
  275.  
  276.             # then install
  277.             res = self.installArchives(pm, installProgress)
  278.             if res == pm.ResultCompleted:
  279.                 break
  280.             if res == pm.ResultFailed:
  281.                 raise SystemError("installArchives() failed")
  282.             # reload the fetcher for media swaping
  283.             fetcher.Shutdown()
  284.         return (res == pm.ResultCompleted)
  285.  
  286.     def clear(self):
  287.         """ Unmark all changes """
  288.         self._depcache.Init()
  289.  
  290.     # cache changes
  291.  
  292.     def cachePostChange(self):
  293.         " called internally if the cache has changed, emit a signal then "
  294.         self._runCallbacks("cache_post_change")
  295.  
  296.     def cachePreChange(self):
  297.         """ called internally if the cache is about to change, emit
  298.             a signal then """
  299.         self._runCallbacks("cache_pre_change")
  300.  
  301.     def connect(self, name, callback):
  302.         """ connect to a signal, currently only used for
  303.             cache_{post,pre}_{changed,open} """
  304.         if not name in self._callbacks:
  305.             self._callbacks[name] = []
  306.         self._callbacks[name].append(callback)
  307.  
  308.  
  309. # ----------------------------- experimental interface
  310.  
  311.  
  312. class Filter(object):
  313.     """ Filter base class """
  314.  
  315.     def apply(self, pkg):
  316.         """ Filter function, return True if the package matchs a
  317.             filter criteria and False otherwise
  318.         """
  319.         return True
  320.  
  321.  
  322. class MarkedChangesFilter(Filter):
  323.     """ Filter that returns all marked changes """
  324.  
  325.     def apply(self, pkg):
  326.         if pkg.markedInstall or pkg.markedDelete or pkg.markedUpgrade:
  327.             return True
  328.         else:
  329.             return False
  330.  
  331.  
  332. class FilteredCache(object):
  333.     """ A package cache that is filtered.
  334.  
  335.         Can work on a existing cache or create a new one
  336.     """
  337.  
  338.     def __init__(self, cache=None, progress=None):
  339.         if cache is None:
  340.             self.cache = Cache(progress)
  341.         else:
  342.             self.cache = cache
  343.         self.cache.connect("cache_post_change", self.filterCachePostChange)
  344.         self.cache.connect("cache_post_open", self.filterCachePostChange)
  345.         self._filtered = {}
  346.         self._filters = []
  347.  
  348.     def __len__(self):
  349.         return len(self._filtered)
  350.  
  351.     def __getitem__(self, key):
  352.         return self.cache._dict[key]
  353.  
  354.     def __iter__(self):
  355.         for pkgname in self._filtered:
  356.             yield self.cache[pkgname]
  357.  
  358.     def keys(self):
  359.         return self._filtered.keys()
  360.  
  361.     def has_key(self, key):
  362.         return (key in self._filtered)
  363.  
  364.     def __contains__(self, key):
  365.         return (key in self._filtered)
  366.  
  367.     def _reapplyFilter(self):
  368.         " internal helper to refilter "
  369.         self._filtered = {}
  370.         for pkg in self.cache._dict.keys():
  371.             for f in self._filters:
  372.                 if f.apply(self.cache._dict[pkg]):
  373.                     self._filtered[pkg] = 1
  374.                     break
  375.  
  376.     def setFilter(self, filter):
  377.         """Set the current active filter."""
  378.         self._filters = []
  379.         self._filters.append(filter)
  380.         #self._reapplyFilter()
  381.         # force a cache-change event that will result in a refiltering
  382.         self.cache.cachePostChange()
  383.  
  384.     def filterCachePostChange(self):
  385.         """Called internally if the cache changes, emit a signal then."""
  386.         #print "filterCachePostChange()"
  387.         self._reapplyFilter()
  388.  
  389. #    def connect(self, name, callback):
  390. #        self.cache.connect(name, callback)
  391.  
  392.     def __getattr__(self, key):
  393.         """we try to look exactly like a real cache."""
  394.         #print "getattr: %s " % key
  395.         return getattr(self.cache, key)
  396.  
  397.  
  398. def cache_pre_changed():
  399.     print "cache pre changed"
  400.  
  401.  
  402. def cache_post_changed():
  403.     print "cache post changed"
  404.  
  405.  
  406. # internal test code
  407. if __name__ == "__main__":
  408.     print "Cache self test"
  409.     apt_pkg.init()
  410.     c = Cache(apt.progress.OpTextProgress())
  411.     c.connect("cache_pre_change", cache_pre_changed)
  412.     c.connect("cache_post_change", cache_post_changed)
  413.     print ("aptitude" in c)
  414.     p = c["aptitude"]
  415.     print p.name
  416.     print len(c)
  417.  
  418.     for pkg in c.keys():
  419.         x= c[pkg].name
  420.  
  421.     c.upgrade()
  422.     changes = c.getChanges()
  423.     print len(changes)
  424.     for p in changes:
  425.         #print p.name
  426.         x = p.name
  427.  
  428.  
  429.     # see if fetching works
  430.     for d in ["/tmp/pytest", "/tmp/pytest/partial"]:
  431.         if not os.path.exists(d):
  432.             os.mkdir(d)
  433.     apt_pkg.Config.Set("Dir::Cache::Archives", "/tmp/pytest")
  434.     pm = apt_pkg.GetPackageManager(c._depcache)
  435.     fetcher = apt_pkg.GetAcquire(apt.progress.TextFetchProgress())
  436.     c._fetchArchives(fetcher, pm)
  437.     #sys.exit(1)
  438.  
  439.     print "Testing filtered cache (argument is old cache)"
  440.     f = FilteredCache(c)
  441.     f.cache.connect("cache_pre_change", cache_pre_changed)
  442.     f.cache.connect("cache_post_change", cache_post_changed)
  443.     f.cache.upgrade()
  444.     f.setFilter(MarkedChangesFilter())
  445.     print len(f)
  446.     for pkg in f.keys():
  447.         #print c[pkg].name
  448.         x = f[pkg].name
  449.  
  450.     print len(f)
  451.  
  452.     print "Testing filtered cache (no argument)"
  453.     f = FilteredCache(progress=OpTextProgress())
  454.     f.cache.connect("cache_pre_change", cache_pre_changed)
  455.     f.cache.connect("cache_post_change", cache_post_changed)
  456.     f.cache.upgrade()
  457.     f.setFilter(MarkedChangesFilter())
  458.     print len(f)
  459.     for pkg in f.keys():
  460.         #print c[pkg].name
  461.         x = f[pkg].name
  462.  
  463.     print len(f)
  464.