ALCSeenDb.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # vim:set fileencoding=utf-8 et ts=4 sts=4 sw=4:
  2. #
  3. # apt-listchanges - Show changelog entries between the installed versions
  4. # of a set of packages and the versions contained in
  5. # corresponding .deb files
  6. #
  7. # Copyright (C) 2000-2006 Matt Zimmerman <mdz@debian.org>
  8. # Copyright (C) 2006 Pierre Habouzit <madcoder@debian.org>
  9. # Copyright (C) 2016 Robert Luberda <robert@debian.org>
  10. #
  11. # This program is free software; you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License as published by
  13. # the Free Software Foundation; either version 2 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public
  22. # License along with this program; if not, write to the Free
  23. # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  24. # MA 02111-1307 USA
  25. #
  26. from dbm import ndbm
  27. from ALChacks import _
  28. import os
  29. import shutil
  30. import sys
  31. class DbError(Exception):
  32. pass
  33. class seendb_dummy(object):
  34. '''Interface for seen database.
  35. Also used when path to the database is not configured'''
  36. def __contains__(self, srcpackage):
  37. return False
  38. def __getitem__(self, srcpackage):
  39. pass
  40. def __setitem__(self, srcpackage, version):
  41. pass
  42. def close_db(self):
  43. pass
  44. def apply_changes(self):
  45. pass
  46. def dump(self):
  47. raise DbError(_("Path to the seen database is unknown.\n"
  48. "Please either specify it with --save-seen option\n"
  49. "or pass --profile=apt to have it read from the configuration file."))
  50. class seendb(seendb_dummy):
  51. '''Class to manage the seen database'''
  52. def __init__(self, path, readOnly = False):
  53. super().__init__()
  54. self._extension = '.db'
  55. if path[-3:] != self._extension:
  56. raise DbError(_("Database %(db)s does not end with %(ext)s")
  57. % {'db': path, 'ext': self._extension})
  58. self._dbpath = path[:-3] # strip the .db suffix
  59. try:
  60. mode = 'r' if readOnly else 'c'
  61. self._seen = ndbm.open(self._dbpath, mode, 0o644)
  62. 'foo%0' in self._seen
  63. except Exception as ex:
  64. raise DbError(_("Database %(db)s failed to load: %(errmsg)s")
  65. % {'db': path, 'errmsg': str(ex)}) from ex
  66. # Will replace seen after changes have actually been seen
  67. self._seen_new = {}
  68. def __contains__(self, srcpackage):
  69. return srcpackage in self._seen
  70. def __getitem__(self, srcpackage):
  71. return self._seen[srcpackage].decode()
  72. def __setitem__(self, srcpackage, version):
  73. if self._seen.get(srcpackage, b'').decode() != version:
  74. # Note: updating _seen_new, not _seen
  75. self._seen_new[srcpackage] = version
  76. def close_db(self):
  77. self._seen.close()
  78. self._seen = None
  79. def apply_changes(self):
  80. if not self._seen_new:
  81. # Nothing to update
  82. return
  83. def mk(arg):
  84. return self._dbpath + arg + self._extension
  85. old, cur, new = (mk('-old'), mk(''), mk('-new'))
  86. # For reliability and to have backup of the database,
  87. # it is updated in the following steps:
  88. # 1. copy current version to new
  89. if os.path.isfile(cur):
  90. shutil.copy(cur, new)
  91. # 2. apply the changes to new
  92. seen = ndbm.open(self._dbpath + '-new', 'c', 0o644)
  93. for (key, value) in self._seen_new.items():
  94. seen[key] = value
  95. seen.close()
  96. # 3. save current as old, and rename new to current
  97. if os.path.isfile(old):
  98. os.unlink(old)
  99. if os.path.isfile(cur):
  100. os.link(cur, old)
  101. os.rename(new, cur)
  102. def dump(self):
  103. for key in sorted(self._seen.keys()):
  104. value = self._seen[key]
  105. print("%s %s" % (key.decode(), value.decode()))
  106. def make_seen_db(config, readOnly = False):
  107. if config.save_seen:
  108. return seendb(config.save_seen, readOnly)
  109. return seendb_dummy()