Package _emerge :: Module BlockerCache
[hide private]

Source Code for Module _emerge.BlockerCache

  1  # Copyright 1999-2013 Gentoo Foundation 
  2  # Distributed under the terms of the GNU General Public License v2 
  3   
  4  import errno 
  5  import sys 
  6  from portage.util import writemsg 
  7  from portage.data import secpass 
  8  import portage 
  9  from portage import os 
 10   
 11  try: 
 12          import cPickle as pickle 
 13  except ImportError: 
 14          import pickle 
 15   
 16  if sys.hexversion >= 0x3000000: 
 17          basestring = str 
 18          long = int 
 19          _unicode = str 
 20  else: 
 21          _unicode = unicode 
 22   
23 -class BlockerCache(portage.cache.mappings.MutableMapping):
24 """This caches blockers of installed packages so that dep_check does not 25 have to be done for every single installed package on every invocation of 26 emerge. The cache is invalidated whenever it is detected that something 27 has changed that might alter the results of dep_check() calls: 28 1) the set of installed packages (including COUNTER) has changed 29 """ 30 31 # Number of uncached packages to trigger cache update, since 32 # it's wasteful to update it for every vdb change. 33 _cache_threshold = 5 34
35 - class BlockerData(object):
36 37 __slots__ = ("__weakref__", "atoms", "counter") 38
39 - def __init__(self, counter, atoms):
40 self.counter = counter 41 self.atoms = atoms
42
43 - def __init__(self, myroot, vardb):
44 """ myroot is ignored in favour of EROOT """ 45 self._vardb = vardb 46 self._cache_filename = os.path.join(vardb.settings['EROOT'], 47 portage.CACHE_PATH, "vdb_blockers.pickle") 48 self._cache_version = "1" 49 self._cache_data = None 50 self._modified = set() 51 self._load()
52
53 - def _load(self):
54 try: 55 f = open(self._cache_filename, mode='rb') 56 mypickle = pickle.Unpickler(f) 57 try: 58 mypickle.find_global = None 59 except AttributeError: 60 # TODO: If py3k, override Unpickler.find_class(). 61 pass 62 self._cache_data = mypickle.load() 63 f.close() 64 del f 65 except (SystemExit, KeyboardInterrupt): 66 raise 67 except Exception as e: 68 if isinstance(e, EnvironmentError) and \ 69 getattr(e, 'errno', None) in (errno.ENOENT, errno.EACCES): 70 pass 71 else: 72 writemsg("!!! Error loading '%s': %s\n" % \ 73 (self._cache_filename, str(e)), noiselevel=-1) 74 del e 75 76 cache_valid = self._cache_data and \ 77 isinstance(self._cache_data, dict) and \ 78 self._cache_data.get("version") == self._cache_version and \ 79 isinstance(self._cache_data.get("blockers"), dict) 80 if cache_valid: 81 # Validate all the atoms and counters so that 82 # corruption is detected as soon as possible. 83 invalid_items = set() 84 for k, v in self._cache_data["blockers"].items(): 85 if not isinstance(k, basestring): 86 invalid_items.add(k) 87 continue 88 try: 89 if portage.catpkgsplit(k) is None: 90 invalid_items.add(k) 91 continue 92 except portage.exception.InvalidData: 93 invalid_items.add(k) 94 continue 95 if not isinstance(v, tuple) or \ 96 len(v) != 2: 97 invalid_items.add(k) 98 continue 99 counter, atoms = v 100 if not isinstance(counter, (int, long)): 101 invalid_items.add(k) 102 continue 103 if not isinstance(atoms, (list, tuple)): 104 invalid_items.add(k) 105 continue 106 invalid_atom = False 107 for atom in atoms: 108 if not isinstance(atom, basestring): 109 invalid_atom = True 110 break 111 if atom[:1] != "!" or \ 112 not portage.isvalidatom( 113 atom, allow_blockers=True): 114 invalid_atom = True 115 break 116 if invalid_atom: 117 invalid_items.add(k) 118 continue 119 120 for k in invalid_items: 121 del self._cache_data["blockers"][k] 122 if not self._cache_data["blockers"]: 123 cache_valid = False 124 125 if not cache_valid: 126 self._cache_data = {"version":self._cache_version} 127 self._cache_data["blockers"] = {} 128 self._modified.clear()
129
130 - def flush(self):
131 """If the current user has permission and the internal blocker cache has 132 been updated, save it to disk and mark it unmodified. This is called 133 by emerge after it has processed blockers for all installed packages. 134 Currently, the cache is only written if the user has superuser 135 privileges (since that's required to obtain a lock), but all users 136 have read access and benefit from faster blocker lookups (as long as 137 the entire cache is still valid). The cache is stored as a pickled 138 dict object with the following format: 139 140 { 141 version : "1", 142 "blockers" : {cpv1:(counter,(atom1, atom2...)), cpv2...}, 143 } 144 """ 145 if len(self._modified) >= self._cache_threshold and \ 146 secpass >= 2: 147 try: 148 f = portage.util.atomic_ofstream(self._cache_filename, mode='wb') 149 pickle.dump(self._cache_data, f, protocol=2) 150 f.close() 151 portage.util.apply_secpass_permissions( 152 self._cache_filename, gid=portage.portage_gid, mode=0o644) 153 except (IOError, OSError): 154 pass 155 self._modified.clear()
156
157 - def __setitem__(self, cpv, blocker_data):
158 """ 159 Update the cache and mark it as modified for a future call to 160 self.flush(). 161 162 @param cpv: Package for which to cache blockers. 163 @type cpv: String 164 @param blocker_data: An object with counter and atoms attributes. 165 @type blocker_data: BlockerData 166 """ 167 self._cache_data["blockers"][_unicode(cpv)] = (blocker_data.counter, 168 tuple(_unicode(x) for x in blocker_data.atoms)) 169 self._modified.add(cpv)
170
171 - def __iter__(self):
172 if self._cache_data is None: 173 # triggered by python-trace 174 return iter([]) 175 return iter(self._cache_data["blockers"])
176
177 - def __len__(self):
178 """This needs to be implemented in order to avoid 179 infinite recursion in some cases.""" 180 return len(self._cache_data["blockers"])
181
182 - def __delitem__(self, cpv):
183 del self._cache_data["blockers"][cpv]
184
185 - def __getitem__(self, cpv):
186 """ 187 @rtype: BlockerData 188 @return: An object with counter and atoms attributes. 189 """ 190 return self.BlockerData(*self._cache_data["blockers"][cpv])
191