Package yum :: Module rpmsack
[hide private]
[frames] | no frames]

Source Code for Module yum.rpmsack

   1  #!/usr/bin/python -tt 
   2  # This program is free software; you can redistribute it and/or modify 
   3  # it under the terms of the GNU General Public License as published by 
   4  # the Free Software Foundation; either version 2 of the License, or 
   5  # (at your option) any later version. 
   6  # 
   7  # This program is distributed in the hope that it will be useful, 
   8  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
   9  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  10  # GNU Library General Public License for more details. 
  11  # 
  12  # You should have received a copy of the GNU General Public License 
  13  # along with this program; if not, write to the Free Software 
  14  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  15   
  16  import rpm 
  17  import types 
  18  import warnings 
  19  import glob 
  20  import os 
  21  import os.path 
  22   
  23  from rpmUtils import miscutils 
  24  from rpmUtils import arch 
  25  from rpmUtils.transaction import initReadOnlyTransaction 
  26  import misc 
  27  import Errors 
  28  from packages import YumInstalledPackage, parsePackages 
  29  from packageSack import PackageSackBase, PackageSackVersion 
  30   
  31  # For returnPackages(patterns=) 
  32  import fnmatch 
  33  import re 
  34   
  35  from yum.i18n import to_unicode, _ 
  36  import constants 
  37   
  38  import yum.depsolve 
39 40 -class RPMInstalledPackage(YumInstalledPackage):
41
42 - def __init__(self, rpmhdr, index, rpmdb):
43 YumInstalledPackage.__init__(self, rpmhdr, yumdb=rpmdb.yumdb) 44 # NOTE: We keep summary/description/url because it doesn't add much 45 # and "yum search" uses them all. 46 self.url = rpmhdr['url'] 47 # Also keep sourcerpm for pirut/etc. 48 self.sourcerpm = rpmhdr['sourcerpm'] 49 50 self.idx = index 51 self.rpmdb = rpmdb 52 53 self._has_hdr = False 54 del self.hdr
55
56 - def _get_hdr(self):
57 # Note that we can't use hasattr(self, 'hdr') or we'll recurse 58 if self._has_hdr: 59 return self.hdr 60 61 ts = self.rpmdb.readOnlyTS() 62 mi = ts.dbMatch(0, self.idx) 63 try: 64 return mi.next() 65 except StopIteration: 66 raise Errors.PackageSackError, 'Rpmdb changed underneath us'
67
68 - def __getattr__(self, varname):
69 self.hdr = val = self._get_hdr() 70 self._has_hdr = True 71 # If these existed, then we wouldn't get here ... and nothing in the DB 72 # starts and ends with __'s. So these are missing. 73 if varname.startswith('__') and varname.endswith('__'): 74 raise AttributeError, "%s has no attribute %s" % (self, varname) 75 76 if varname != 'hdr': # This is unusual, for anything that happens 77 val = val[varname] # a lot we should preload at __init__. 78 # Also note that pkg.no_value raises KeyError. 79 80 return val
81
82 83 -class RPMDBProblem:
84 ''' 85 Represents a problem in the rpmdb, from the check_*() functions. 86 '''
87 - def __init__(self, pkg, problem, **kwargs):
88 self.pkg = pkg 89 self.problem = problem 90 for kwarg in kwargs: 91 setattr(self, kwarg, kwargs[kwarg])
92
93 - def __cmp__(self, other):
94 if other is None: 95 return 1 96 return cmp(self.pkg, other.pkg) or cmp(self.problem, other.problem)
97
98 99 -class RPMDBProblemDependency(RPMDBProblem):
100 - def __str__(self):
101 if self.problem == 'requires': 102 return "%s %s %s" % (self.pkg, _('has missing requires of'), 103 self.missing) 104 105 return "%s %s %s: %s" % (self.pkg, _('has installed conflicts'), 106 self.found,', '.join(map(str, self.conflicts)))
107
108 109 -class RPMDBProblemDuplicate(RPMDBProblem):
110 - def __init__(self, pkg, **kwargs):
111 RPMDBProblem.__init__(self, pkg, "duplicate", **kwargs)
112
113 - def __str__(self):
114 return _("%s is a duplicate with %s") % (self.pkg, self.duplicate)
115
116 117 -class RPMDBPackageSack(PackageSackBase):
118 ''' 119 Represent rpmdb as a packagesack 120 ''' 121 122 DEP_TABLE = { 123 'requires' : (rpm.RPMTAG_REQUIRENAME, 124 rpm.RPMTAG_REQUIREVERSION, 125 rpm.RPMTAG_REQUIREFLAGS), 126 'provides' : (rpm.RPMTAG_PROVIDENAME, 127 rpm.RPMTAG_PROVIDEVERSION, 128 rpm.RPMTAG_PROVIDEFLAGS), 129 'conflicts' : (rpm.RPMTAG_CONFLICTNAME, 130 rpm.RPMTAG_CONFLICTVERSION, 131 rpm.RPMTAG_CONFLICTFLAGS), 132 'obsoletes' : (rpm.RPMTAG_OBSOLETENAME, 133 rpm.RPMTAG_OBSOLETEVERSION, 134 rpm.RPMTAG_OBSOLETEFLAGS) 135 } 136 137 # Do we want to cache rpmdb data in a file, for later use? 138 __cache_rpmdb__ = True 139
140 - def __init__(self, root='/', releasever=None, cachedir=None, 141 persistdir='/var/lib/yum'):
142 self.root = root 143 self._idx2pkg = {} 144 self._name2pkg = {} 145 self._tup2pkg = {} 146 self._completely_loaded = False 147 self._simple_pkgtup_list = [] 148 self._get_pro_cache = {} 149 self._get_req_cache = {} 150 self._loaded_gpg_keys = False 151 if cachedir is None: 152 cachedir = misc.getCacheDir() 153 self.setCacheDir(cachedir) 154 self._persistdir = root + '/' + persistdir 155 self._have_cached_rpmdbv_data = None 156 self._cached_conflicts_data = None 157 # Store the result of what happens, if a transaction completes. 158 self._trans_cache_store = {} 159 self.ts = None 160 self.releasever = releasever 161 self.auto_close = False # this forces a self.ts.close() after 162 # most operations so it doesn't leave 163 # any lingering locks. 164 165 self._cache = { 166 'provides' : { }, 167 'requires' : { }, 168 'conflicts' : { }, 169 'obsoletes' : { }, 170 } 171 172 addldb_path = os.path.normpath(self._persistdir + '/yumdb') 173 self.yumdb = RPMDBAdditionalData(db_path=addldb_path)
174
175 - def _get_pkglist(self):
176 '''Getter for the pkglist property. 177 Returns a list of package tuples. 178 ''' 179 if not self._simple_pkgtup_list: 180 for (hdr, mi) in self._all_packages(): 181 self._simple_pkgtup_list.append(self._hdr2pkgTuple(hdr)) 182 183 return self._simple_pkgtup_list
184 185 pkglist = property(_get_pkglist, None) 186
187 - def dropCachedData(self):
188 self._idx2pkg = {} 189 self._name2pkg = {} 190 self._tup2pkg = {} 191 self._completely_loaded = False 192 self._simple_pkgtup_list = [] 193 self._get_pro_cache = {} 194 self._get_req_cache = {} 195 # We can be called on python shutdown (due to yb.__del__), at which 196 # point other modules might not be available. 197 if misc is not None: 198 misc.unshare_data() 199 self._cache = { 200 'provides' : { }, 201 'requires' : { }, 202 'conflicts' : { }, 203 'obsoletes' : { }, 204 } 205 self._have_cached_rpmdbv_data = None 206 self._cached_conflicts_data = None
207
208 - def setCacheDir(self, cachedir):
209 """ Sets the internal cachedir value for the rpmdb, to be the 210 "installed" directory from this parent. """ 211 self._cachedir = self.root + '/' + cachedir + "/installed/"
212
213 - def readOnlyTS(self):
214 if not self.ts: 215 self.ts = initReadOnlyTransaction(root=self.root) 216 if not self.ts.open: 217 self.ts = initReadOnlyTransaction(root=self.root) 218 return self.ts
219
220 - def buildIndexes(self):
221 # Not used here 222 return
223
224 - def _checkIndexes(self, failure='error'):
225 # Not used here 226 return
227
228 - def delPackage(self, obj):
229 # Not supported with this sack type 230 pass
231
232 - def searchAll(self, name, query_type='like'):
233 ts = self.readOnlyTS() 234 result = {} 235 236 # check provides 237 tag = self.DEP_TABLE['provides'][0] 238 mi = ts.dbMatch() 239 mi.pattern(tag, rpm.RPMMIRE_GLOB, name) 240 for hdr in mi: 241 if hdr['name'] == 'gpg-pubkey': 242 continue 243 pkg = self._makePackageObject(hdr, mi.instance()) 244 if not result.has_key(pkg.pkgid): 245 result[pkg.pkgid] = pkg 246 del mi 247 248 fileresults = self.searchFiles(name) 249 for pkg in fileresults: 250 if not result.has_key(pkg.pkgid): 251 result[pkg.pkgid] = pkg 252 253 if self.auto_close: 254 self.ts.close() 255 256 return result.values()
257
258 - def searchFiles(self, name):
259 """search the filelists in the rpms for anything matching name""" 260 261 ts = self.readOnlyTS() 262 result = {} 263 264 mi = ts.dbMatch('basenames', name) 265 for hdr in mi: 266 if hdr['name'] == 'gpg-pubkey': 267 continue 268 pkg = self._makePackageObject(hdr, mi.instance()) 269 if not result.has_key(pkg.pkgid): 270 result[pkg.pkgid] = pkg 271 del mi 272 273 result = result.values() 274 275 if self.auto_close: 276 self.ts.close() 277 278 return result
279
280 - def searchPrco(self, name, prcotype):
281 282 result = self._cache[prcotype].get(name) 283 if result is not None: 284 return result 285 (n,f,(e,v,r)) = misc.string_to_prco_tuple(name) 286 glob = False 287 288 if misc.re_glob(n): 289 glob = True 290 291 ts = self.readOnlyTS() 292 result = {} 293 tag = self.DEP_TABLE[prcotype][0] 294 mi = ts.dbMatch(tag, misc.to_utf8(n)) 295 for hdr in mi: 296 if hdr['name'] == 'gpg-pubkey': 297 continue 298 po = self._makePackageObject(hdr, mi.instance()) 299 if not glob: 300 if po.checkPrco(prcotype, (n, f, (e,v,r))): 301 result[po.pkgid] = po 302 else: 303 result[po.pkgid] = po 304 del mi 305 306 307 # If it's not a provides or filename, we are done 308 if prcotype == 'provides' and name[0] == '/': 309 fileresults = self.searchFiles(name) 310 for pkg in fileresults: 311 result[pkg.pkgid] = pkg 312 313 result = result.values() 314 self._cache[prcotype][name] = result 315 316 if self.auto_close: 317 self.ts.close() 318 319 return result
320
321 - def searchProvides(self, name):
322 return self.searchPrco(name, 'provides')
323
324 - def searchRequires(self, name):
325 return self.searchPrco(name, 'requires')
326
327 - def searchObsoletes(self, name):
328 return self.searchPrco(name, 'obsoletes')
329
330 - def searchConflicts(self, name):
331 return self.searchPrco(name, 'conflicts')
332
333 - def simplePkgList(self):
334 return self.pkglist
335 336 installed = PackageSackBase.contains 337
338 - def returnNewestByNameArch(self, naTup=None, patterns=None):
339 340 #FIXME - should this (or any packagesack) be returning tuples? 341 if not naTup: 342 return 343 344 (name, arch) = naTup 345 346 allpkg = self._search(name=name, arch=arch) 347 348 if not allpkg: 349 raise Errors.PackageSackError, 'No Package Matching %s' % name 350 351 return [ po.pkgtup for po in misc.newestInList(allpkg) ]
352
353 - def returnNewestByName(self, name=None):
354 if not name: 355 return 356 357 allpkgs = self._search(name=name) 358 359 if not allpkgs: 360 raise Errors.PackageSackError, 'No Package Matching %s' % name 361 362 return misc.newestInList(allpkgs)
363 364 @staticmethod
365 - def _compile_patterns(patterns, ignore_case=False):
366 if not patterns or len(patterns) > constants.PATTERNS_MAX: 367 return None 368 ret = [] 369 for pat in patterns: 370 if not pat: 371 continue 372 qpat = pat[0] 373 if qpat in ('?', '*'): 374 qpat = None 375 if ignore_case: 376 if qpat is not None: 377 qpat = qpat.lower() 378 ret.append((qpat, re.compile(fnmatch.translate(pat), re.I))) 379 else: 380 ret.append((qpat, re.compile(fnmatch.translate(pat)))) 381 return ret
382 @staticmethod
383 - def _match_repattern(repatterns, hdr, ignore_case):
384 if repatterns is None: 385 return True 386 387 for qpat, repat in repatterns: 388 epoch = hdr['epoch'] 389 if epoch is None: 390 epoch = '0' 391 else: 392 epoch = str(epoch) 393 qname = hdr['name'][0] 394 if ignore_case: 395 qname = qname.lower() 396 if qpat is not None and qpat != qname and qpat != epoch[0]: 397 continue 398 if repat.match(hdr['name']): 399 return True 400 if repat.match("%(name)s-%(version)s-%(release)s.%(arch)s" % hdr): 401 return True 402 if repat.match("%(name)s.%(arch)s" % hdr): 403 return True 404 if repat.match("%(name)s-%(version)s" % hdr): 405 return True 406 if repat.match("%(name)s-%(version)s-%(release)s" % hdr): 407 return True 408 if repat.match(epoch + ":%(name)s-%(version)s-%(release)s.%(arch)s" 409 % hdr): 410 return True 411 if repat.match("%(name)s-%(epoch)s:%(version)s-%(release)s.%(arch)s" 412 % hdr): 413 return True 414 return False
415
416 - def returnPackages(self, repoid=None, patterns=None, ignore_case=False):
417 """Returns a list of packages. Note that the packages are 418 always filtered to those matching the patterns/case. repoid is 419 ignored, and is just here for compatibility with non-rpmdb sacks. """ 420 if not self._completely_loaded: 421 rpats = self._compile_patterns(patterns, ignore_case) 422 for hdr, idx in self._all_packages(): 423 if self._match_repattern(rpats, hdr, ignore_case): 424 self._makePackageObject(hdr, idx) 425 self._completely_loaded = patterns is None 426 427 pkgobjlist = self._idx2pkg.values() 428 # Remove gpg-pubkeys, as no sane callers expects/likes them... 429 if self._loaded_gpg_keys: 430 pkgobjlist = [pkg for pkg in pkgobjlist if pkg.name != 'gpg-pubkey'] 431 if patterns: 432 pkgobjlist = parsePackages(pkgobjlist, patterns, not ignore_case) 433 pkgobjlist = pkgobjlist[0] + pkgobjlist[1] 434 return pkgobjlist
435
437 if self._cached_conflicts_data is None: 438 ret = [] 439 for pkg in self.returnPackages(): 440 if len(pkg.conflicts): 441 ret.append(pkg) 442 self._cached_conflicts_data = ret 443 return self._cached_conflicts_data
444
445 - def _write_conflicts_new(self, pkgs, rpmdbv):
446 if not os.access(self._cachedir, os.W_OK): 447 return 448 449 conflicts_fname = self._cachedir + '/conflicts' 450 fo = open(conflicts_fname + '.tmp', 'w') 451 fo.write("%s\n" % rpmdbv) 452 fo.write("%u\n" % len(pkgs)) 453 for pkg in sorted(pkgs): 454 for var in pkg.pkgtup: 455 fo.write("%s\n" % var) 456 fo.close() 457 os.rename(conflicts_fname + '.tmp', conflicts_fname)
458
459 - def _write_conflicts(self, pkgs):
460 rpmdbv = self.simpleVersion(main_only=True)[0] 461 self._write_conflicts_new(pkgs, rpmdbv)
462
463 - def _read_conflicts(self):
464 if not self.__cache_rpmdb__: 465 return None 466 467 def _read_str(fo): 468 return fo.readline()[:-1]
469 470 conflict_fname = self._cachedir + '/conflicts' 471 if not os.path.exists(conflict_fname): 472 return None 473 474 fo = open(conflict_fname) 475 frpmdbv = fo.readline() 476 rpmdbv = self.simpleVersion(main_only=True)[0] 477 if not frpmdbv or rpmdbv != frpmdbv[:-1]: 478 return None 479 480 ret = [] 481 try: 482 # Read the conflicts... 483 pkgtups_num = int(_read_str(fo)) 484 while pkgtups_num > 0: 485 pkgtups_num -= 1 486 487 # n, a, e, v, r 488 pkgtup = (_read_str(fo), _read_str(fo), 489 _read_str(fo), _read_str(fo), _read_str(fo)) 490 int(pkgtup[2]) # Check epoch is valid 491 ret.extend(self.searchPkgTuple(pkgtup)) 492 if fo.readline() != '': # Should be EOF 493 return None 494 except ValueError: 495 return None 496 497 self._cached_conflicts_data = ret 498 return self._cached_conflicts_data
499
500 - def transactionCacheConflictPackages(self, pkgs):
501 if self.__cache_rpmdb__: 502 self._trans_cache_store['conflicts'] = pkgs
503
504 - def returnConflictPackages(self):
505 """ Return a list of packages that have conflicts. """ 506 pkgs = self._read_conflicts() 507 if pkgs is None: 508 pkgs = self._uncached_returnConflictPackages() 509 if self.__cache_rpmdb__: 510 self._write_conflicts(pkgs) 511 512 return pkgs
513
514 - def transactionResultVersion(self, rpmdbv):
515 """ We are going to do a transaction, and the parameter will be the 516 rpmdb version when we finish. The idea being we can update all 517 our rpmdb caches for that rpmdb version. """ 518 519 if not self.__cache_rpmdb__: 520 self._trans_cache_store = {} 521 return 522 523 if 'conflicts' in self._trans_cache_store: 524 pkgs = self._trans_cache_store['conflicts'] 525 self._write_conflicts_new(pkgs, rpmdbv) 526 527 if 'file-requires' in self._trans_cache_store: 528 data = self._trans_cache_store['file-requires'] 529 self._write_file_requires(rpmdbv, data) 530 531 if 'yumdb-package-checksums' in self._trans_cache_store: 532 data = self._trans_cache_store['yumdb-package-checksums'] 533 self._write_package_checksums(rpmdbv, data) 534 535 self._trans_cache_store = {}
536
537 - def transactionReset(self):
538 """ We are going to reset the transaction, because the data we've added 539 already might now be invalid (Eg. skip-broken, or splitting a 540 transaction). """ 541 542 self._trans_cache_store = {}
543
544 - def returnGPGPubkeyPackages(self):
545 """ Return packages of the gpg-pubkeys ... hacky. """ 546 ts = self.readOnlyTS() 547 mi = ts.dbMatch('name', 'gpg-pubkey') 548 ret = [] 549 for hdr in mi: 550 self._loaded_gpg_keys = True 551 ret.append(self._makePackageObject(hdr, mi.instance())) 552 return ret
553
554 - def _read_file_requires(self):
555 def _read_str(fo): 556 return fo.readline()[:-1]
557 558 assert self.__cache_rpmdb__ 559 if not os.path.exists(self._cachedir + '/file-requires'): 560 return None, None 561 562 rpmdbv = self.simpleVersion(main_only=True)[0] 563 fo = open(self._cachedir + '/file-requires') 564 frpmdbv = fo.readline() 565 if not frpmdbv or rpmdbv != frpmdbv[:-1]: 566 return None, None 567 568 iFR = {} 569 iFP = {} 570 try: 571 # Read the requires... 572 pkgtups_num = int(_read_str(fo)) 573 while pkgtups_num > 0: 574 pkgtups_num -= 1 575 576 # n, a, e, v, r 577 pkgtup = (_read_str(fo), _read_str(fo), 578 _read_str(fo), _read_str(fo), _read_str(fo)) 579 int(pkgtup[2]) # Check epoch is valid 580 581 files_num = int(_read_str(fo)) 582 while files_num > 0: 583 files_num -= 1 584 585 fname = _read_str(fo) 586 587 iFR.setdefault(pkgtup, []).append(fname) 588 589 # Read the provides... 590 files_num = int(_read_str(fo)) 591 while files_num > 0: 592 files_num -= 1 593 fname = _read_str(fo) 594 pkgtups_num = int(_read_str(fo)) 595 while pkgtups_num > 0: 596 pkgtups_num -= 1 597 598 # n, a, e, v, r 599 pkgtup = (_read_str(fo), _read_str(fo), 600 _read_str(fo), _read_str(fo), _read_str(fo)) 601 int(pkgtup[2]) # Check epoch is valid 602 603 iFP.setdefault(fname, []).append(pkgtup) 604 605 if fo.readline() != '': # Should be EOF 606 return None, None 607 except ValueError: 608 return None, None 609 610 return iFR, iFP 611
612 - def fileRequiresData(self):
613 """ Get a cached copy of the fileRequiresData for 614 depsolving/checkFileRequires, note the giant comment in that 615 function about how we don't keep this perfect for the providers of 616 the requires. """ 617 if self.__cache_rpmdb__: 618 iFR, iFP = self._read_file_requires() 619 if iFR is not None: 620 return iFR, set(), iFP 621 622 installedFileRequires = {} 623 installedUnresolvedFileRequires = set() 624 resolved = set() 625 for pkg in self.returnPackages(): 626 for name, flag, evr in pkg.requires: 627 if not name.startswith('/'): 628 continue 629 installedFileRequires.setdefault(pkg.pkgtup, []).append(name) 630 if name not in resolved: 631 dep = self.getProvides(name, flag, evr) 632 resolved.add(name) 633 if not dep: 634 installedUnresolvedFileRequires.add(name) 635 636 fileRequires = set() 637 for fnames in installedFileRequires.itervalues(): 638 fileRequires.update(fnames) 639 installedFileProviders = {} 640 for fname in fileRequires: 641 pkgtups = [pkg.pkgtup for pkg in self.getProvides(fname)] 642 installedFileProviders[fname] = pkgtups 643 644 ret = (installedFileRequires, installedUnresolvedFileRequires, 645 installedFileProviders) 646 if self.__cache_rpmdb__: 647 rpmdbv = self.simpleVersion(main_only=True)[0] 648 self._write_file_requires(rpmdbv, ret) 649 650 return ret
651
652 - def transactionCacheFileRequires(self, installedFileRequires, 653 installedUnresolvedFileRequires, 654 installedFileProvides, 655 problems):
656 if not self.__cache_rpmdb__: 657 return 658 659 if installedUnresolvedFileRequires or problems: 660 return 661 662 data = (installedFileRequires, 663 installedUnresolvedFileRequires, 664 installedFileProvides) 665 666 self._trans_cache_store['file-requires'] = data
667
668 - def _write_file_requires(self, rpmdbversion, data):
669 if not os.access(self._cachedir, os.W_OK): 670 return 671 672 (installedFileRequires, 673 installedUnresolvedFileRequires, 674 installedFileProvides) = data 675 676 # Have to do this here, as well as in transactionCacheFileRequires, 677 # because fileRequiresData() calls us directly. 678 if installedUnresolvedFileRequires: 679 return 680 681 fo = open(self._cachedir + '/file-requires.tmp', 'w') 682 fo.write("%s\n" % rpmdbversion) 683 684 fo.write("%u\n" % len(installedFileRequires)) 685 for pkgtup in sorted(installedFileRequires): 686 for var in pkgtup: 687 fo.write("%s\n" % var) 688 filenames = set(installedFileRequires[pkgtup]) 689 fo.write("%u\n" % len(filenames)) 690 for fname in sorted(filenames): 691 fo.write("%s\n" % fname) 692 693 fo.write("%u\n" % len(installedFileProvides)) 694 for fname in sorted(installedFileProvides): 695 fo.write("%s\n" % fname) 696 697 pkgtups = set(installedFileProvides[fname]) 698 fo.write("%u\n" % len(pkgtups)) 699 for pkgtup in sorted(pkgtups): 700 for var in pkgtup: 701 fo.write("%s\n" % var) 702 fo.close() 703 os.rename(self._cachedir + '/file-requires.tmp', 704 self._cachedir + '/file-requires')
705
706 - def preloadPackageChecksums(self):
707 """ As simpleVersion() et. al. requires it, we "cache" this yumdb data 708 as part of our rpmdb cache. We cache it with rpmdb data, even 709 though someone _could_ use yumdb to alter it without changing the 710 rpmdb ... don't do that. """ 711 if not self.__cache_rpmdb__: 712 return 713 714 if not os.path.exists(self._cachedir + '/yumdb-package-checksums'): 715 return 716 717 def _read_str(fo): 718 return fo.readline()[:-1]
719 720 rpmdbv = self.simpleVersion(main_only=True)[0] 721 fo = open(self._cachedir + '/yumdb-package-checksums') 722 frpmdbv = fo.readline() 723 if not frpmdbv or rpmdbv != frpmdbv[:-1]: 724 return 725 726 checksum_data = {} 727 try: 728 # Read the checksums... 729 pkgtups_num = int(_read_str(fo)) 730 while pkgtups_num > 0: 731 pkgtups_num -= 1 732 733 # n, a, e, v, r 734 pkgtup = (_read_str(fo), _read_str(fo), 735 _read_str(fo), _read_str(fo), _read_str(fo)) 736 int(pkgtup[2]) # Check epoch is valid 737 738 T = _read_str(fo) 739 D = _read_str(fo) 740 checksum_data[pkgtup] = (T, D) 741 742 if fo.readline() != '': # Should be EOF 743 return 744 except ValueError: 745 return 746 747 for pkgtup in checksum_data: 748 (n, a, e, v, r) = pkgtup 749 pkg = self.searchNevra(n, e, v, r, a)[0] 750 (T, D) = checksum_data[pkgtup] 751 if ('checksum_type' in pkg.yumdb_info._read_cached_data or 752 'checksum_data' in pkg.yumdb_info._read_cached_data): 753 continue 754 pkg.yumdb_info._read_cached_data['checksum_type'] = T 755 pkg.yumdb_info._read_cached_data['checksum_data'] = D 756
757 - def transactionCachePackageChecksums(self, pkg_checksum_tups):
758 if not self.__cache_rpmdb__: 759 return 760 761 self._trans_cache_store['yumdb-package-checksums'] = pkg_checksum_tups
762
763 - def _write_package_checksums(self, rpmdbversion, data):
764 if not os.access(self._cachedir, os.W_OK): 765 return 766 767 pkg_checksum_tups = data 768 fo = open(self._cachedir + '/yumdb-package-checksums.tmp', 'w') 769 fo.write("%s\n" % rpmdbversion) 770 fo.write("%u\n" % len(pkg_checksum_tups)) 771 for pkgtup, TD in sorted(pkg_checksum_tups): 772 for var in pkgtup: 773 fo.write("%s\n" % var) 774 for var in TD: 775 fo.write("%s\n" % var) 776 fo.close() 777 os.rename(self._cachedir + '/yumdb-package-checksums.tmp', 778 self._cachedir + '/yumdb-package-checksums')
779
780 - def _get_cached_simpleVersion_main(self):
781 """ Return the cached string of the main rpmdbv. """ 782 if self._have_cached_rpmdbv_data is not None: 783 return self._have_cached_rpmdbv_data 784 785 if not self.__cache_rpmdb__: 786 return None 787 788 # This test is "obvious" and the only thing to come out of: 789 # http://lists.rpm.org/pipermail/rpm-maint/2007-November/001719.html 790 # ...if anything gets implemented, we should change. 791 rpmdbvfname = self._cachedir + "/version" 792 rpmdbfname = self.root + "/var/lib/rpm/Packages" 793 794 if os.path.exists(rpmdbvfname) and os.path.exists(rpmdbfname): 795 # See if rpmdb has "changed" ... 796 nmtime = os.path.getmtime(rpmdbvfname) 797 omtime = os.path.getmtime(rpmdbfname) 798 if omtime <= nmtime: 799 rpmdbv = open(rpmdbvfname).readline()[:-1] 800 self._have_cached_rpmdbv_data = rpmdbv 801 return self._have_cached_rpmdbv_data
802
803 - def _put_cached_simpleVersion_main(self, rpmdbv):
804 self._have_cached_rpmdbv_data = str(rpmdbv) 805 806 if not self.__cache_rpmdb__: 807 return 808 809 rpmdbvfname = self._cachedir + "/version" 810 if not os.access(self._cachedir, os.W_OK): 811 if os.path.exists(self._cachedir): 812 return 813 814 try: 815 os.makedirs(self._cachedir) 816 except (IOError, OSError), e: 817 return 818 819 fo = open(rpmdbvfname + ".tmp", "w") 820 fo.write(self._have_cached_rpmdbv_data) 821 fo.write('\n') 822 fo.close() 823 os.rename(rpmdbvfname + ".tmp", rpmdbvfname)
824
825 - def simpleVersion(self, main_only=False, groups={}):
826 """ Return a simple version for all installed packages. """ 827 def _up_revs(irepos, repoid, rev, pkg, csum): 828 irevs = irepos.setdefault(repoid, {}) 829 rpsv = irevs.setdefault(None, PackageSackVersion()) 830 rpsv.update(pkg, csum) 831 if rev is not None: 832 rpsv = irevs.setdefault(rev, PackageSackVersion()) 833 rpsv.update(pkg, csum)
834 835 if main_only and not groups: 836 rpmdbv = self._get_cached_simpleVersion_main() 837 if rpmdbv is not None: 838 return [rpmdbv, {}] 839 840 main = PackageSackVersion() 841 irepos = {} 842 main_grps = {} 843 irepos_grps = {} 844 for pkg in sorted(self.returnPackages()): 845 ydbi = pkg.yumdb_info 846 csum = None 847 if 'checksum_type' in ydbi and 'checksum_data' in ydbi: 848 csum = (ydbi.checksum_type, ydbi.checksum_data) 849 main.update(pkg, csum) 850 851 for group in groups: 852 if pkg.name in groups[group]: 853 if group not in main_grps: 854 main_grps[group] = PackageSackVersion() 855 irepos_grps[group] = {} 856 main_grps[group].update(pkg, csum) 857 858 if main_only: 859 continue 860 861 repoid = 'installed' 862 rev = None 863 if 'from_repo' in pkg.yumdb_info: 864 repoid = '@' + pkg.yumdb_info.from_repo 865 if 'from_repo_revision' in pkg.yumdb_info: 866 rev = pkg.yumdb_info.from_repo_revision 867 868 _up_revs(irepos, repoid, rev, pkg, csum) 869 for group in groups: 870 if pkg.name in groups[group]: 871 _up_revs(irepos_grps[group], repoid, rev, pkg, csum) 872 873 if self._have_cached_rpmdbv_data is None: 874 self._put_cached_simpleVersion_main(main) 875 876 if groups: 877 return [main, irepos, main_grps, irepos_grps] 878 return [main, irepos] 879 880 @staticmethod
881 - def _find_search_fields(fields, searchstrings, hdr):
882 count = 0 883 for s in searchstrings: 884 for field in fields: 885 value = to_unicode(hdr[field]) 886 if value and value.lower().find(s) != -1: 887 count += 1 888 break 889 return count
890
891 - def searchPrimaryFieldsMultipleStrings(self, fields, searchstrings, 892 lowered=False):
893 if not lowered: 894 searchstrings = map(lambda x: x.lower(), searchstrings) 895 ret = [] 896 for hdr, idx in self._all_packages(): 897 n = self._find_search_fields(fields, searchstrings, hdr) 898 if n > 0: 899 ret.append((self._makePackageObject(hdr, idx), n)) 900 return ret
901 - def searchNames(self, names=[]):
902 returnList = [] 903 for name in names: 904 returnList.extend(self._search(name=name)) 905 return returnList
906
907 - def searchNevra(self, name=None, epoch=None, ver=None, rel=None, arch=None):
908 return self._search(name, epoch, ver, rel, arch)
909
910 - def excludeArchs(self, archlist):
911 pass
912
913 - def returnLeafNodes(self, repoid=None):
914 ts = self.readOnlyTS() 915 return [ self._makePackageObject(h, mi) for (h, mi) in ts.returnLeafNodes(headers=True) ]
916 917 # Helper functions
918 - def _all_packages(self):
919 '''Generator that yield (header, index) for all packages 920 ''' 921 ts = self.readOnlyTS() 922 mi = ts.dbMatch() 923 924 for hdr in mi: 925 if hdr['name'] != 'gpg-pubkey': 926 yield (hdr, mi.instance()) 927 del mi 928 if self.auto_close: 929 self.ts.close()
930 931
932 - def _header_from_index(self, idx):
933 """returns a package header having been given an index""" 934 warnings.warn('_header_from_index() will go away in a future version of Yum.\n', 935 Errors.YumFutureDeprecationWarning, stacklevel=2) 936 937 ts = self.readOnlyTS() 938 try: 939 mi = ts.dbMatch(0, idx) 940 except (TypeError, StopIteration), e: 941 #FIXME: raise some kind of error here 942 print 'No index matching %s found in rpmdb, this is bad' % idx 943 yield None # it should REALLY not be returning none - this needs to be right 944 else: 945 hdr = mi.next() 946 yield hdr 947 del hdr 948 949 del mi 950 if self.auto_close: 951 self.ts.close()
952 953
954 - def _search(self, name=None, epoch=None, ver=None, rel=None, arch=None):
955 '''List of matching packages, to zero or more of NEVRA.''' 956 pkgtup = (name, arch, epoch, ver, rel) 957 if pkgtup in self._tup2pkg: 958 return [self._tup2pkg[pkgtup]] 959 960 loc = locals() 961 ret = [] 962 963 if self._completely_loaded: 964 if name is not None: 965 pkgs = self._name2pkg.get(name, []) 966 else: 967 pkgs = self.returnPkgs() 968 for po in pkgs: 969 for tag in ('arch', 'rel', 'ver', 'epoch'): 970 if loc[tag] is not None and loc[tag] != getattr(po, tag): 971 break 972 else: 973 ret.append(po) 974 return ret 975 976 ts = self.readOnlyTS() 977 if name is not None: 978 mi = ts.dbMatch('name', name) 979 elif arch is not None: 980 mi = ts.dbMatch('arch', arch) 981 else: 982 mi = ts.dbMatch() 983 self._completely_loaded = True 984 985 for hdr in mi: 986 if hdr['name'] == 'gpg-pubkey': 987 continue 988 po = self._makePackageObject(hdr, mi.instance()) 989 for tag in ('arch', 'rel', 'ver', 'epoch'): 990 if loc[tag] is not None and loc[tag] != getattr(po, tag): 991 break 992 else: 993 ret.append(po) 994 995 if self.auto_close: 996 self.ts.close() 997 998 return ret
999
1000 - def _makePackageObject(self, hdr, index):
1001 if index in self._idx2pkg: 1002 return self._idx2pkg[index] 1003 po = RPMInstalledPackage(hdr, index, self) 1004 self._idx2pkg[index] = po 1005 self._name2pkg.setdefault(po.name, []).append(po) 1006 self._tup2pkg[po.pkgtup] = po 1007 return po
1008
1009 - def _hdr2pkgTuple(self, hdr):
1010 name = misc.share_data(hdr['name']) 1011 arch = misc.share_data(hdr['arch']) 1012 # convert these to strings to be sure 1013 ver = misc.share_data(str(hdr['version'])) 1014 rel = misc.share_data(str(hdr['release'])) 1015 epoch = hdr['epoch'] 1016 if epoch is None: 1017 epoch = '0' 1018 else: 1019 epoch = str(epoch) 1020 epoch = misc.share_data(epoch) 1021 return misc.share_data((name, arch, epoch, ver, rel))
1022 1023 # deprecated options for compat only - remove once rpmdb is converted:
1024 - def getPkgList(self):
1025 warnings.warn('getPkgList() will go away in a future version of Yum.\n' 1026 'Please access this via the pkglist attribute.', 1027 DeprecationWarning, stacklevel=2) 1028 1029 return self.pkglist
1030
1031 - def getHdrList(self):
1032 warnings.warn('getHdrList() will go away in a future version of Yum.\n', 1033 DeprecationWarning, stacklevel=2) 1034 return [ hdr for hdr, idx in self._all_packages() ]
1035
1036 - def getNameArchPkgList(self):
1037 warnings.warn('getNameArchPkgList() will go away in a future version of Yum.\n', 1038 DeprecationWarning, stacklevel=2) 1039 1040 lst = [] 1041 for (name, arch, epoch, ver, rel) in self.pkglist: 1042 lst.append((name, arch)) 1043 1044 return miscutils.unique(lst)
1045
1046 - def getNamePkgList(self):
1047 warnings.warn('getNamePkgList() will go away in a future version of Yum.\n', 1048 DeprecationWarning, stacklevel=2) 1049 1050 lst = [] 1051 for (name, arch, epoch, ver, rel) in self.pkglist: 1052 lst.append(name) 1053 1054 return miscutils.unique(lst)
1055
1056 - def returnTupleByKeyword(self, name=None, arch=None, epoch=None, ver=None, rel=None):
1057 warnings.warn('returnTuplebyKeyword() will go away in a future version of Yum.\n', 1058 DeprecationWarning, stacklevel=2) 1059 return [po.pkgtup for po in self._search(name=name, arch=arch, epoch=epoch, ver=ver, rel=rel)]
1060
1061 - def returnHeaderByTuple(self, pkgtuple):
1062 warnings.warn('returnHeaderByTuple() will go away in a future version of Yum.\n', 1063 DeprecationWarning, stacklevel=2) 1064 """returns a list of header(s) based on the pkgtuple provided""" 1065 1066 (n, a, e, v, r) = pkgtuple 1067 1068 lst = self.searchNevra(name=n, arch=a, epoch=e, ver=v, rel=r) 1069 if len(lst) > 0: 1070 item = lst[0] 1071 return [item.hdr] 1072 else: 1073 return []
1074
1075 - def returnIndexByTuple(self, pkgtuple):
1076 """returns a list of header indexes based on the pkgtuple provided""" 1077 1078 warnings.warn('returnIndexbyTuple() will go away in a future version of Yum.\n', 1079 DeprecationWarning, stacklevel=2) 1080 1081 name, arch, epoch, version, release = pkgtuple 1082 1083 # Normalise epoch 1084 if epoch in (None, 0, '(none)', ''): 1085 epoch = '0' 1086 1087 return [po.idx for po in self._search(name, epoch, version, release, arch)]
1088
1089 - def addDB(self, ts):
1090 # Can't support this now 1091 raise NotImplementedError
1092 1093 @staticmethod
1094 - def _genDeptup(name, flags, version):
1095 """ Given random stuff, generate a usable dep tuple. """ 1096 1097 if flags == 0: 1098 flags = None 1099 1100 if type(version) is types.StringType: 1101 (r_e, r_v, r_r) = miscutils.stringToVersion(version) 1102 # would this ever be a ListType? 1103 elif type(version) in (types.TupleType, types.ListType): 1104 (r_e, r_v, r_r) = version 1105 else: 1106 # FIXME: This isn't always type(version) is types.NoneType: 1107 # ...not sure what it is though, come back to this 1108 r_e = r_v = r_r = None 1109 1110 deptup = (name, misc.share_data(flags), 1111 (misc.share_data(r_e), misc.share_data(r_v), 1112 misc.share_data(r_r))) 1113 return misc.share_data(deptup)
1114
1115 - def getProvides(self, name, flags=None, version=(None, None, None)):
1116 """searches the rpmdb for what provides the arguments 1117 returns a list of pkg objects of providing packages, possibly empty""" 1118 1119 name = misc.share_data(name) 1120 deptup = self._genDeptup(name, flags, version) 1121 if deptup in self._get_pro_cache: 1122 return self._get_pro_cache[deptup] 1123 r_v = deptup[2][1] 1124 1125 pkgs = self.searchProvides(name) 1126 1127 result = { } 1128 1129 for po in pkgs: 1130 if name[0] == '/' and r_v is None: 1131 result[po] = [(name, None, (None, None, None))] 1132 continue 1133 hits = po.matchingPrcos('provides', deptup) 1134 if hits: 1135 result[po] = hits 1136 self._get_pro_cache[deptup] = result 1137 return result
1138
1139 - def whatProvides(self, name, flags, version):
1140 # XXX deprecate? 1141 return [po.pkgtup for po in self.getProvides(name, flags, version)]
1142
1143 - def getRequires(self, name, flags=None, version=(None, None, None)):
1144 """searches the rpmdb for what provides the arguments 1145 returns a list of pkgtuples of providing packages, possibly empty""" 1146 1147 name = misc.share_data(name) 1148 deptup = self._genDeptup(name, flags, version) 1149 if deptup in self._get_req_cache: 1150 return self._get_req_cache[deptup] 1151 r_v = deptup[2][1] 1152 1153 pkgs = self.searchRequires(name) 1154 1155 result = { } 1156 1157 for po in pkgs: 1158 if name[0] == '/' and r_v is None: 1159 # file dep add all matches to the defSack 1160 result[po] = [(name, None, (None, None, None))] 1161 continue 1162 hits = po.matchingPrcos('requires', deptup) 1163 if hits: 1164 result[po] = hits 1165 self._get_req_cache[deptup] = result 1166 return result
1167
1168 - def whatRequires(self, name, flags, version):
1169 # XXX deprecate? 1170 return [po.pkgtup for po in self.getRequires(name, flags, version)]
1171
1172 - def return_running_packages(self):
1173 """returns a list of yum installed package objects which own a file 1174 that are currently running or in use.""" 1175 pkgs = {} 1176 for pid in misc.return_running_pids(): 1177 for fn in misc.get_open_files(pid): 1178 for pkg in self.searchFiles(fn): 1179 pkgs[pkg] = 1 1180 1181 return sorted(pkgs.keys())
1182
1183 - def check_dependencies(self, pkgs=None):
1184 """ Checks for any missing dependencies. """ 1185 1186 if pkgs is None: 1187 pkgs = self.returnPackages() 1188 1189 providers = set() # Speedup, as usual :) 1190 problems = [] 1191 for pkg in sorted(pkgs): # The sort here is mainly for "UI" 1192 for rreq in pkg.requires: 1193 if rreq[0].startswith('rpmlib'): continue 1194 if rreq in providers: continue 1195 1196 (req, flags, ver) = rreq 1197 if self.getProvides(req, flags, ver): 1198 providers.add(rreq) 1199 continue 1200 flags = yum.depsolve.flags.get(flags, flags) 1201 missing = miscutils.formatRequire(req, ver, flags) 1202 prob = RPMDBProblemDependency(pkg, "requires", missing=missing) 1203 problems.append(prob) 1204 1205 for creq in pkg.conflicts: 1206 if creq[0].startswith('rpmlib'): continue 1207 1208 (req, flags, ver) = creq 1209 res = self.getProvides(req, flags, ver) 1210 if not res: 1211 continue 1212 flags = yum.depsolve.flags.get(flags, flags) 1213 found = miscutils.formatRequire(req, ver, flags) 1214 prob = RPMDBProblemDependency(pkg, "conflicts", found=found, 1215 conflicts=res) 1216 problems.append(prob) 1217 return problems
1218
1219 - def _iter_two_pkgs(self, ignore_provides):
1220 last = None 1221 for pkg in sorted(self.returnPackages()): 1222 if pkg.name in ignore_provides: 1223 continue 1224 if ignore_provides.intersection(set(pkg.provides_names)): 1225 continue 1226 1227 if last is None: 1228 last = pkg 1229 continue 1230 yield last, pkg 1231 last = pkg
1232
1233 - def check_duplicates(self, ignore_provides=[]):
1234 """ Checks for any "duplicate packages" (those with multiple versions 1235 installed), we ignore any packages with a provide in the passed 1236 provide list (this is how installonlyworks, so we do the same). """ 1237 ignore_provides = set(ignore_provides) 1238 problems = [] 1239 for last, pkg in self._iter_two_pkgs(ignore_provides): 1240 if pkg.name != last.name: 1241 continue 1242 if pkg.verEQ(last) and pkg != last: 1243 if arch.isMultiLibArch(pkg.arch) and last.arch != 'noarch': 1244 continue 1245 if arch.isMultiLibArch(last.arch) and pkg.arch != 'noarch': 1246 continue 1247 1248 # More than one pkg, they aren't version equal, or aren't multiarch 1249 problems.append(RPMDBProblemDuplicate(pkg, duplicate=last)) 1250 return problems
1251
1252 1253 -def _sanitize(path):
1254 return path.replace('/', '').replace('~', '')
1255
1256 1257 -class RPMDBAdditionalData(object):
1258 """class for access to the additional data not able to be stored in the 1259 rpmdb""" 1260 # dir: /var/lib/yum/yumdb/ 1261 # pkgs stored in name[0]/name[1]/pkgid-name-ver-rel-arch dirs 1262 # dirs have files per piece of info we're keeping 1263 # repoid, install reason, status, blah, (group installed for?), notes? 1264
1265 - def __init__(self, db_path='/var/lib/yum/yumdb'):
1266 self.conf = misc.GenericHolder() 1267 self.conf.db_path = db_path 1268 self.conf.writable = False 1269 1270 self._packages = {} # pkgid = dir 1271 if not os.path.exists(self.conf.db_path): 1272 try: 1273 os.makedirs(self.conf.db_path) 1274 except (IOError, OSError), e: 1275 # some sort of useful thing here? A warning? 1276 return 1277 self.conf.writable = True 1278 else: 1279 if os.access(self.conf.db_path, os.W_OK): 1280 self.conf.writable = True
1281 # Don't call _load_all_package_paths to preload, as it's expensive 1282 # if the dirs. aren't in cache. 1283
1284 - def _load_all_package_paths(self):
1285 # glob the path and get a dict of pkgs to their subdir 1286 glb = '%s/*/*/' % self.conf.db_path 1287 pkgdirs = glob.glob(glb) 1288 for d in pkgdirs: 1289 pkgid = os.path.basename(d).split('-')[0] 1290 self._packages[pkgid] = d
1291
1292 - def _get_dir_name(self, pkgtup, pkgid):
1293 if pkgid in self._packages: 1294 return self._packages[pkgid] 1295 (n, a, e, v,r) = pkgtup 1296 n = _sanitize(n) # Please die in a fire rpmbuild 1297 thisdir = '%s/%s/%s-%s-%s-%s-%s' % (self.conf.db_path, 1298 n[0], pkgid, n, v, r, a) 1299 self._packages[pkgid] = thisdir 1300 return thisdir
1301
1302 - def get_package(self, po=None, pkgtup=None, pkgid=None):
1303 """Return an RPMDBAdditionalDataPackage Object for this package""" 1304 if po: 1305 thisdir = self._get_dir_name(po.pkgtup, po.pkgid) 1306 elif pkgtup and pkgid: 1307 thisdir = self._get_dir_name(pkgtup, pkgid) 1308 else: 1309 raise ValueError,"Pass something to RPMDBAdditionalData.get_package" 1310 1311 return RPMDBAdditionalDataPackage(self.conf, thisdir)
1312
1313 - def sync_with_rpmdb(self, rpmdbobj):
1314 """populate out the dirs and remove all the items no longer in the rpmd 1315 and/or populate various bits to the currently installed version""" 1316 # TODO: 1317 # get list of all items in the yumdb 1318 # remove any no longer in the rpmdb/andor migrate them up to the currently 1319 # installed version 1320 # add entries for items in the rpmdb if they don't exist in the yumdb 1321 1322 pass
1323
1324 -class RPMDBAdditionalDataPackage(object):
1325 - def __init__(self, conf, pkgdir):
1326 self._conf = conf 1327 self._mydir = pkgdir 1328 # FIXME needs some intelligent caching beyond the FS cache 1329 self._read_cached_data = {}
1330
1331 - def _write(self, attr, value):
1332 # check for self._conf.writable before going on? 1333 if not os.path.exists(self._mydir): 1334 os.makedirs(self._mydir) 1335 1336 attr = _sanitize(attr) 1337 if attr in self._read_cached_data: 1338 del self._read_cached_data[attr] 1339 fn = self._mydir + '/' + attr 1340 fn = os.path.normpath(fn) 1341 fo = open(fn + '.tmp', 'w') 1342 try: 1343 fo.write(value) 1344 except (OSError, IOError), e: 1345 raise AttributeError, "Cannot set attribute %s on %s" % (attr, self) 1346 1347 fo.flush() 1348 fo.close() 1349 del fo 1350 os.rename(fn + '.tmp', fn) # even works on ext4 now!:o 1351 self._read_cached_data[attr] = value
1352
1353 - def _read(self, attr):
1354 attr = _sanitize(attr) 1355 1356 if attr.endswith('.tmp'): 1357 raise AttributeError, "%s has no attribute %s" % (self, attr) 1358 1359 if attr in self._read_cached_data: 1360 return self._read_cached_data[attr] 1361 1362 fn = self._mydir + '/' + attr 1363 if not os.path.exists(fn): 1364 raise AttributeError, "%s has no attribute %s" % (self, attr) 1365 1366 fo = open(fn, 'r') 1367 self._read_cached_data[attr] = fo.read() 1368 fo.close() 1369 del fo 1370 return self._read_cached_data[attr]
1371
1372 - def _delete(self, attr):
1373 """remove the attribute file""" 1374 1375 attr = _sanitize(attr) 1376 fn = self._mydir + '/' + attr 1377 if attr in self._read_cached_data: 1378 del self._read_cached_data[attr] 1379 if os.path.exists(fn): 1380 try: 1381 os.unlink(fn) 1382 except (IOError, OSError): 1383 raise AttributeError, "Cannot delete attribute %s on " % (attr, self)
1384
1385 - def __getattr__(self, attr):
1386 return self._read(attr)
1387
1388 - def __setattr__(self, attr, value):
1389 if not attr.startswith('_'): 1390 self._write(attr, value) 1391 else: 1392 object.__setattr__(self, attr, value)
1393
1394 - def __delattr__(self, attr):
1395 if not attr.startswith('_'): 1396 self._delete(attr) 1397 else: 1398 object.__delattr__(self, attr)
1399
1400 - def __iter__(self, show_hidden=False):
1401 for item in self._read_cached_data: 1402 yield item 1403 for item in glob.glob(self._mydir + '/*'): 1404 item = item[(len(self._mydir) + 1):] 1405 if item in self._read_cached_data: 1406 continue 1407 if not show_hidden and item.endswith('.tmp'): 1408 continue 1409 yield item
1410
1411 - def clean(self):
1412 # purge out everything 1413 for item in self.__iter__(show_hidden=True): 1414 self._delete(item) 1415 try: 1416 os.rmdir(self._mydir) 1417 except OSError: 1418 pass
1419 1420 # def __dir__(self): # for 2.6 and beyond, apparently 1421 # return list(self.__iter__()) + self.__dict__.keys() 1422
1423 - def get(self, attr, default=None):
1424 """retrieve an add'l data obj""" 1425 1426 try: 1427 res = self._read(attr) 1428 except AttributeError: 1429 return default 1430 return res
1431
1432 1433 -def main():
1434 sack = RPMDBPackageSack('/') 1435 for p in sack.simplePkgList(): 1436 print p
1437 1438 if __name__ == '__main__': 1439 main() 1440