home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / pycentral / python-apt / site-packages / apt / cache.py < prev    next >
Encoding:
Python Source  |  2006-07-27  |  11.8 KB  |  372 lines

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