From 8013ade2dec560173c9fb3197506087fe8e7411d Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 31 Aug 2011 19:52:05 +0000 Subject: [PATCH 01/46] MigrationItem: new class representing a single package being migrated. Signed-off-by: Adam D. Barratt --- migrationitem.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 migrationitem.py diff --git a/migrationitem.py b/migrationitem.py new file mode 100644 index 0000000..dcc7e4d --- /dev/null +++ b/migrationitem.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011 Adam D. Barratt + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +class MigrationItem: + def __init__(self, name = None): + self._name = None + self._version = None + self._architecture = None + self._suite = None + + if name: + self._set_name(name) + + def _get_name(self): + return self._name + + def _set_name(self, value): + self._version = None + self._name = value + if value.startswith('-'): + value = value[1:] + parts = value.split('/', 3) + package = parts[0] + if '_' in package: + self._package, self._suite = package.split('_', 2) + else: + self._package, self._suite = (package, 'unstable') + if len(parts) == 2: + self._architecture = parts[1] + else: + self._architecture = 'source' + + if '_' in self._architecture: + self_architecture, self._suite = \ + self._architecture.split('_', 2) + + if self.is_removal: + self._suite = 'testing' + + name = property(_get_name, _set_name) + + @property + def is_removal(self): + return self._name.startswith('-') + + @property + def architecture(self): + return self._architecture + + @property + def package(self): + return self._package + + @property + def suite(self): + return self._suite + + @property + def version(self): + return self._version From 3dcb9464cfcf195bb62b037d123a9935c87d24e7 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 31 Aug 2011 19:55:22 +0000 Subject: [PATCH 02/46] Migrate most code parsing package names to use MigrationItems. The use of MigrationItem allows us to centralise the parsing and splitting of package names and architectures, avoiding duplication and simplifying a number of conditions. Signed-off-by: Adam D. Barratt --- britney.py | 123 +++++++++++++++++++++-------------------------------- 1 file changed, 49 insertions(+), 74 deletions(-) diff --git a/britney.py b/britney.py index 2f7bd33..345ba43 100755 --- a/britney.py +++ b/britney.py @@ -193,6 +193,7 @@ import urllib import apt_pkg from excuse import Excuse +from migrationitem import MigrationItem from britney import buildSystem __author__ = 'Fabio Tranchitella and the Debian Release Team' @@ -2033,13 +2034,13 @@ class Britney: # check the package at the top of the tree return satisfy(pkg) - def doop_source(self, pkg, hint_undo=[]): + def doop_source(self, item, hint_undo=[]): """Apply a change to the testing distribution as requested by `pkg` An optional list of undo actions related to packages processed earlier in a hint may be passed in `hint_undo`. - This method applies the changes required by the action `pkg` tracking + This method applies the changes required by the action `item` tracking them so it will be possible to revert them. The method returns a list of the package name, the suite where the @@ -2049,49 +2050,23 @@ class Britney: undo = {'binaries': {}, 'sources': {}, 'virtual': {}, 'nvirtual': []} affected = [] - arch = None # local copies for better performances sources = self.sources binaries = self.binaries['testing'] - - # removal of single-arch binary package = "-/" - if pkg[0] == "-" and "/" in pkg: - pkg_name, arch = pkg.split("/") - pkg_name = pkg_name[1:] - suite = "testing" - # arch = "/", - elif "/" in pkg: - pkg_name, arch = pkg.split("/") - if arch.endswith("_tpu") or arch.endswith("_pu"): - arch, suite = arch.split("_") - else: suite = "unstable" - # removal of source packages = "-", - elif pkg[0] == "-": - pkg_name = pkg[1:] - suite = "testing" - # testing-proposed-updates = "_tpu" - # proposed-updates = "_pu" - elif pkg.endswith("_tpu") or pkg.endswith("_pu"): - pkg_name, suite = pkg.rsplit("_") - # normal update of source packages = "" - else: - pkg_name = pkg - suite = "unstable" - # remove all binary packages (if the source already exists) - if not (arch and pkg[0] == '-'): - if pkg_name in sources['testing']: - source = sources['testing'][pkg_name] + if item.architecture == 'source' or item.is_removal: + if item.package in sources['testing']: + source = sources['testing'][item.package] # remove all the binaries for p in source[BINARIES]: binary, parch = p.split("/") - if arch and parch != arch: continue + if item.architecture != 'source' and parch != item.architecture: continue # do not remove binaries which have been hijacked by other sources - if binaries[parch][0][binary][SOURCE] != pkg_name: continue + if binaries[parch][0][binary][SOURCE] != item.package: continue # if a smooth update is possible for the package, skip it - if not self.options.compatible and suite == 'unstable' and \ - binary not in self.binaries[suite][parch][0] and \ + if not self.options.compatible and item.suite == 'unstable' and \ + binary not in self.binaries[item.suite][parch][0] and \ ('ALL' in self.options.smooth_updates or \ binaries[parch][0][binary][SECTION] in self.options.smooth_updates): continue @@ -2113,28 +2088,28 @@ class Britney: del binaries[parch][0][binary] self.systems[parch].remove_binary(binary) # remove the source package - if not arch: - undo['sources'][pkg_name] = source - del sources['testing'][pkg_name] + if item.architecture == 'source': + undo['sources'][item.package] = source + del sources['testing'][item.package] else: # the package didn't exist, so we mark it as to-be-removed in case of undo - undo['sources']['-' + pkg_name] = True + undo['sources']['-' + item.package] = True # single binary removal - elif pkg_name in binaries[arch][0]: - undo['binaries'][pkg_name + "/" + arch] = binaries[arch][0][pkg_name] - affected.extend( [ (x, arch) for x in \ - self.get_reverse_tree(pkg_name, arch, 'testing') ] ) + elif item.package in binaries[item.architecture][0]: + undo['binaries'][item.package + "/" + item.architecture] = binaries[item.architecture][0][item.package] + affected.extend( [ (x, item.architecture) for x in \ + self.get_reverse_tree(item.package, item.architecture, 'testing') ] ) affected = list(set(affected)) - del binaries[arch][0][pkg_name] - self.systems[arch].remove_binary(pkg_name) + del binaries[item.architecture][0][item.package] + self.systems[item.architecture].remove_binary(item.package) # add the new binary packages (if we are not removing) - if pkg[0] != "-": - source = sources[suite][pkg_name] + if not item.is_removal: + source = sources[item.suite][item.package] for p in source[BINARIES]: binary, parch = p.split("/") - if arch and parch != arch: continue + if item.architecture not in ['source', parch]: continue key = (binary, parch) # obviously, added/modified packages are affected if key not in affected: affected.append(key) @@ -2162,7 +2137,7 @@ class Britney: # ignored as their reverse trees are already handled # by this function # XXX: and the reverse conflict tree? - for (tundo, tpkg, tpkg_name, tsuite) in hint_undo: + for (tundo, tpkg) in hint_undo: if p in tundo['binaries']: for rdep in tundo['binaries'][p][RDEPENDS]: if rdep in binaries[parch][0] and rdep not in source[BINARIES]: @@ -2171,7 +2146,7 @@ class Britney: self.get_reverse_tree(rdep, parch, 'testing') ] ) affected = list(set(affected)) # add/update the binary package - binaries[parch][0][binary] = self.binaries[suite][parch][0][binary] + binaries[parch][0][binary] = self.binaries[item.suite][parch][0][binary] self.systems[parch].add_binary(binary, binaries[parch][0][binary][:PROVIDES] + \ [", ".join(binaries[parch][0][binary][PROVIDES]) or None]) # register new provided packages @@ -2191,15 +2166,15 @@ class Britney: # register reverse dependencies and conflicts for the new binary packages for p in source[BINARIES]: binary, parch = p.split("/") - if arch and parch != arch: continue + if item.architecture not in ['source', parch]: continue self.register_reverses(binary, binaries[parch][0] , binaries[parch][1]) # add/update the source package - if not arch: - sources['testing'][pkg_name] = sources[suite][pkg_name] + if item.architecture == 'source': + sources['testing'][item.package] = sources[item.suite][item.package] # return the package name, the suite, the list of affected packages and the undo dictionary - return (pkg_name, suite, affected, undo) + return (item, affected, undo) def get_reverse_tree(self, pkg, arch, suite): packages = [] @@ -2276,9 +2251,9 @@ class Britney: # pre-process a hint batch pre_process = {} if selected and hint: - for pkg in selected: - pkg_name, suite, affected, undo = self.doop_source(pkg) - pre_process[pkg] = (pkg_name, suite, affected, undo) + for package in selected: + pkg, affected, undo = self.doop_source(MigrationItem(package)) + pre_process[package] = (pkg, affected, undo) lundo = [] if not hint: @@ -2314,14 +2289,14 @@ class Britney: # apply the changes if pkg in pre_process: - pkg_name, suite, affected, undo = pre_process[pkg] + item, affected, undo = pre_process[pkg] else: - pkg_name, suite, affected, undo = self.doop_source(pkg, lundo) + item, affected, undo = self.doop_source(MigrationItem(pkg), lundo) if hint: - lundo.append((undo, pkg, pkg_name, suite)) + lundo.append((undo, item)) # check the affected packages on all the architectures - for arch in ("/" in pkg and (pkg.split("/")[1].split("_")[0],) or architectures): + for arch in (item.architecture == 'source' and architectures or (item.architecture,)): if arch not in nobreakall_arches: skip_archall = True else: skip_archall = False @@ -2390,17 +2365,17 @@ class Britney: continue # if the uninstallability counter is worse than before, break the loop - if (("/" in pkg and arch not in new_arches) or \ + if ((item.architecture != 'source' and arch not in new_arches) or \ (arch not in break_arches)) and len(nuninst[arch]) > len(nuninst_comp[arch]): better = False break # if we are processing hints or the package is already accepted, go ahead - if hint or pkg in selected: continue + if hint or item.name in selected: continue # check if the action improved the uninstallability counters if better: - lundo.append((undo, pkg, pkg_name, suite)) + lundo.append((undo, item)) selected.append(pkg) packages.extend(extra) extra = [] @@ -2430,10 +2405,10 @@ class Britney: else: sources['testing'][k] = undo['sources'][k] # undo the changes (new binaries) - if pkg[0] != '-' and pkg_name in sources[suite]: - for p in sources[suite][pkg_name][BINARIES]: + if not item.is_removal and item.package in sources[item.suite]: + for p in sources[item.suite][item.package][BINARIES]: binary, arch = p.split("/") - if '/' not in pkg or pkg.endswith("/%s" % (arch)) or pkg.endswith("/%s_tpu" % (arch)) or pkg.endswith("/%s_pu" % (arch)): + if item.architecture in ['source', arch]: del binaries[arch][0][binary] self.systems[arch].remove_binary(binary) @@ -2558,23 +2533,23 @@ class Britney: if not undo: return # undo all the changes - for (undo, pkg, pkg_name, suite) in lundo: + for (undo, item) in lundo: # undo the changes (source) for k in undo['sources'].keys(): if k[0] == '-': del self.sources['testing'][k[1:]] else: self.sources['testing'][k] = undo['sources'][k] - for (undo, pkg, pkg_name, suite) in lundo: + for (undo, item) in lundo: # undo the changes (new binaries) - if pkg[0] != '-' and pkg_name in self.sources[suite]: - for p in self.sources[suite][pkg_name][BINARIES]: + if not item.is_removal and item.package in self.sources[item.suite]: + for p in self.sources[item.suite][item.package][BINARIES]: binary, arch = p.split("/") - if '/' not in pkg or pkg.endswith("/%s" % (arch)) or pkg.endswith("/%s_tpu" % (arch)) or pkg.endswith("/%s_pu" % (arch)): + if item.architecture in ['source', arch]: del self.binaries['testing'][arch][0][binary] self.systems[arch].remove_binary(binary) - for (undo, pkg, pkg_name, suite) in lundo: + for (undo, item) in lundo: # undo the changes (binaries) for p in undo['binaries'].keys(): binary, arch = p.split("/") @@ -2588,7 +2563,7 @@ class Britney: self.systems[arch].add_binary(binary, binaries[binary][:PROVIDES] + \ [", ".join(binaries[binary][PROVIDES]) or None]) - for (undo, pkg, pkg_name, suite) in lundo: + for (undo, item) in lundo: # undo the changes (virtual packages) for p in undo['nvirtual']: j, arch = p.split("/") From a03e72ffe0ce03d07c4d8a3d3fb09e2b297e5d60 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 31 Aug 2011 20:07:35 +0000 Subject: [PATCH 03/46] migrationitem: allow for the specification of version information. The new convenience HintItem class automatically sets the "this item contains version information" flag when creating a new item. Signed-off-by: Adam D. Barratt --- migrationitem.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/migrationitem.py b/migrationitem.py index dcc7e4d..2474681 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -13,11 +13,12 @@ # GNU General Public License for more details. class MigrationItem: - def __init__(self, name = None): + def __init__(self, name = None, versionned = False): self._name = None self._version = None self._architecture = None self._suite = None + self._versionned = versionned if name: self._set_name(name) @@ -36,10 +37,17 @@ class MigrationItem: self._package, self._suite = package.split('_', 2) else: self._package, self._suite = (package, 'unstable') - if len(parts) == 2: - self._architecture = parts[1] + if self._versionned: + self._version = parts[1] + if len(parts) == 3: + self._architecture = parts[2] + else: + self._architecture = 'source' else: - self._architecture = 'source' + if len(parts) == 2: + self._architecture = parts[1] + else: + self._architecture = 'source' if '_' in self._architecture: self_architecture, self._suite = \ @@ -69,3 +77,7 @@ class MigrationItem: @property def version(self): return self._version + +class HintItem(MigrationItem): + def __init__(self, name = None): + MigrationItem.__init__(self, name = name, versionned = True) From 0c2c672bb25463df4cb45edfdda50e260a86b67a Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Thu, 1 Sep 2011 18:15:34 +0000 Subject: [PATCH 04/46] Add initial version of Hint/HintCollection classes. These classes encapsulate information about individual hints, with HintCollection providing a convenient wrapper around a set of hints. Signed-off-by: Adam D. Barratt --- hints.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 hints.py diff --git a/hints.py b/hints.py new file mode 100644 index 0000000..d052ae2 --- /dev/null +++ b/hints.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011 Adam D. Barratt + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from migrationitem import HintItem + +class HintCollection: + def __init__(self): + self._hints = [] + + def __getitem__(self, type=None, onlyactive=True): + return self.hints(type, onlyactive) + + def hints(self, type=None, onlyactive=True): + if type: + return [ hint for hint in self._hints if hint.type == type and (hint.active or onlyactive)] + else: + return self._hints[:] + + def add_hint(self, hint, user): + self._hints.append(Hint(hint, user)) + +class Hint: + def __init__(self, hint, user): + self._user = user + self._active = True + self._days = None + if isinstance(hint, list): + self._type = hint[0] + self._packages = hint[1:] + else: + self._type, self._packages = hint.split(' ', 2) + + if self._type == 'age-days': + if isinstance(hint, list): + self._days = self._packages[0] + self._packages = self._packages[1:] + else: + self._days, self._packages = self._packages.split(' ', 2) + + self._packages = [HintItem(x) for x in self._packages] + + def set_active(self, active): + self._active = active + + @property + def type(self): + return self._type + + @property + def packages(self): + return self._packages + + @property + def active(self): + return self._active From 02a9aa14ef2d87d7f9dbb3a7eb70fcbd85981794 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:15:03 +0000 Subject: [PATCH 05/46] migrationitem: add an "unversioned name" property This can be used to retrieve the version information from a versionned hint; for unversionned hints, it is effectively a synonym for "name". Signed-off-by: Adam D. Barratt --- migrationitem.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index 2474681..24df4ea 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -56,6 +56,15 @@ class MigrationItem: if self.is_removal: self._suite = 'testing' + if self._versionned: + parts = self._name.split('/', 3) + if len(parts) == 1 or self._architecture == 'source': + self._uvname = parts[0] + else: + self._uvname = "%s/%s" % (parts[0], parts[1]) + else: + self._uvname = self._name + name = property(_get_name, _set_name) @property @@ -78,6 +87,10 @@ class MigrationItem: def version(self): return self._version + @property + def uvname(self): + return self._uvname + class HintItem(MigrationItem): def __init__(self, name = None): MigrationItem.__init__(self, name = name, versionned = True) From 41c72a3644c5d5e2b1e1009a59373a09532e4344 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:17:39 +0000 Subject: [PATCH 06/46] migrationitem: apply some fixes to the version-derivation code The major change is reversing the order of three-part item names (i.e. binNMUs with version information included) to use the more traditional ordering of //. Even if an instance is marked as versionned, passing a non-versionned source package name should not cause a traceback. Signed-off-by: Adam D. Barratt --- migrationitem.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/migrationitem.py b/migrationitem.py index 24df4ea..4405cac 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -37,12 +37,13 @@ class MigrationItem: self._package, self._suite = package.split('_', 2) else: self._package, self._suite = (package, 'unstable') - if self._versionned: - self._version = parts[1] + if self._versionned and len(parts) > 1: if len(parts) == 3: - self._architecture = parts[2] + self._architecture = parts[1] + self._version = parts[2] else: self._architecture = 'source' + self._version = parts[1] else: if len(parts) == 2: self._architecture = parts[1] From 5a7a0cbe0a21ba013df8320756d29a43f922f9e2 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:21:10 +0000 Subject: [PATCH 07/46] hints: export the user associated with a hint via a property Signed-off-by: Adam D. Barratt --- hints.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hints.py b/hints.py index d052ae2..c5a3fd6 100644 --- a/hints.py +++ b/hints.py @@ -64,3 +64,7 @@ class Hint: @property def active(self): return self._active + + @property + def user(self): + return self._user From 7700e229cdcdcabfecd8be781ecd2507a701ac62 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:23:06 +0000 Subject: [PATCH 08/46] hints: allow more properties to be used for filtering the hint list Signed-off-by: Adam D. Barratt --- hints.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hints.py b/hints.py index c5a3fd6..bc2b87e 100644 --- a/hints.py +++ b/hints.py @@ -18,14 +18,19 @@ class HintCollection: def __init__(self): self._hints = [] - def __getitem__(self, type=None, onlyactive=True): - return self.hints(type, onlyactive) - - def hints(self, type=None, onlyactive=True): - if type: - return [ hint for hint in self._hints if hint.type == type and (hint.active or onlyactive)] - else: - return self._hints[:] + def __getitem__(self, type=None): + return self.hints(type) + + def hints(self, type=None, onlyactive=True, package=None, \ + version=None, days=None, removal=None): + + return [ hint for hint in self._hints if + (type is None or type == hint.type) and + (onlyactive or hint.active) and + (package is None or package == hint.packages[0].package) and + (version is None or version == hint.packages[0].version) and + (removal is None or removal == hint.packages[0].is_removal) + ] def add_hint(self, hint, user): self._hints.append(Hint(hint, user)) From 8665dba10067d78900faa7682cdee2df32b91829 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:34:58 +0000 Subject: [PATCH 09/46] migrationitem: allow instances to be stringified Signed-off-by: Adam D. Barratt --- migrationitem.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index 4405cac..67e5efd 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -23,6 +23,12 @@ class MigrationItem: if name: self._set_name(name) + def __str__(self): + if self._versionned and not self.version is None: + return self.name + else: + return self.uvname + def _get_name(self): return self._name From 870c939e3fd17b4895f0374d29a7982031a5ff5d Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:36:40 +0000 Subject: [PATCH 10/46] migrationitem: add support for using items as indexes in to lists In order to provide the support, items may now be tested for equality and hashed. Two items are considered equal if they have the same unversioned name (and version, if appropriate); hashing is based on a tuple hash of the name and version. Signed-off-by: Adam D. Barratt --- migrationitem.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index 67e5efd..baa606e 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -29,6 +29,19 @@ class MigrationItem: else: return self.uvname + def __eq__(self, other): + isequal = False + if self.uvname == other.uvname: + if self.version is None or other.version is None: + isequal = True + else: + isequal = self.version == other.version + + return isequal + + def __hash__(self): + return hash((self.uvname, self.version)) + def _get_name(self): return self._name From 25a3dd851e94509cb237f825f0e68f078fc28a65 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 16:41:33 +0000 Subject: [PATCH 11/46] Move hint-using code to using HintCollection and HintItem In order to make a number of the changes required for the migration simpler, we also complete the previous migration to using {Hint,Migration}Item rather than passing around strings representing packages and converting between the two forms in several places. Signed-off-by: Adam D. Barratt --- britney.py | 194 +++++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/britney.py b/britney.py index 345ba43..5e916cd 100755 --- a/britney.py +++ b/britney.py @@ -193,7 +193,8 @@ import urllib import apt_pkg from excuse import Excuse -from migrationitem import MigrationItem +from migrationitem import MigrationItem, HintItem +from hints import Hint, HintCollection from britney import buildSystem __author__ = 'Fabio Tranchitella and the Debian Release Team' @@ -773,7 +774,7 @@ class Britney: The method returns a dictionary where the key is the command, and the value is the list of affected packages. """ - hints = dict([(k,[]) for k in self.HINTS_ALL]) + hints = HintCollection() for who in self.HINTS.keys(): if who == 'command-line': @@ -793,35 +794,37 @@ class Britney: break elif l[0] not in self.HINTS[who]: continue - elif l[0] in ["easy", "hint", "force-hint"]: - hints[l[0]].append((who, [k.rsplit("/", 1) for k in l if "/" in k])) - elif l[0] in ["block-all"]: - hints[l[0]].extend([(y, who) for y in l[1:]]) - elif l[0] in ["block", "block-udeb"]: - hints[l[0]].extend([(y, who) for y in l[1:]]) - elif l[0] in ["age-days"] and len(l) >= 3 and l[1].isdigit(): - days = l[1] - tmp = [tuple([who] + k.rsplit("/", 1)) for k in l[2:] if "/" in k] - hints[l[0]].extend([(p, (v, h, days)) for h, p, v in tmp]) - elif l[0] in ["remove", "approve", "unblock", "unblock-udeb", "force", "urgent"]: - hints[l[0]].extend([(k.rsplit("/", 1)[0], (k.rsplit("/", 1)[1], who)) for k in l if "/" in k]) + elif l[0] in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove"]: + for package in l[1:]: + hints.add_hint('%s %s"' % (l[0], package), who) + elif l[0] in ["age-days"]: + for package in l[2:]: + hints.add_hint('%s %s %s' % (l[0], l[1], package), who) + else: + hints.add_hint(l, who) for x in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove", "age-days"]: z = {} - for a, b in hints[x]: - if z.has_key(a) and z[a] != b: + for hint in hints[x]: + item = hint.packages[0] + package = item.package + if z.has_key(package) and z[package] != item.version: if x in ['unblock', 'unblock-udeb']: - if apt_pkg.VersionCompare(z[a][0], b[0]) < 0: + if apt_pkg.VersionCompare(z[package], item.version) < 0: # This hint is for a newer version, so discard the old one - self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W") + self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], item.version), type="W") + for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: + other.set_active(False) else: # This hint is for an older version, so ignore it in favour of the new one - self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, a, b, z[a]), type="W") - continue + self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, package, item.version, z[package]), type="W") + hint.set_active(False) else: - self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W") - z[a] = b - hints[x] = z + self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], item.version), type="W") + for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: + other.set_active(False) + + z[package] = item.version # Sanity check the hints hash if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0: @@ -1082,9 +1085,9 @@ class Britney: src[SECTION] and excuse.set_section(src[SECTION].strip()) # if the package is blocked, skip it - if self.hints['block'].has_key('-' + pkg): + for hint in self.hints.hints('block', package=pkg, removal=True): excuse.addhtml("Not touching package, as requested by %s (contact debian-release " - "if update is needed)" % self.hints['block']['-' + pkg]) + "if update is needed)" % hint.user) excuse.addhtml("Not considered") self.excuses.append(excuse) return False @@ -1119,9 +1122,8 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False - if src in self.hints["remove"] and \ - self.same_source(source_t[VERSION], self.hints["remove"][src][0]): - excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1])) + for hint in [ x for x in self.hints.hints('remove', package=src) if self.same_source(source_t[VERSION], x.packages[0].version) ]: + excuse.addhtml("Removal request by %s" % (hint.user)) excuse.addhtml("Trying to remove package, not update it") excuse.addhtml("Not considered") self.excuses.append(excuse) @@ -1258,38 +1260,38 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False - if src in self.hints["remove"]: - if source_t and self.same_source(source_t[VERSION], self.hints['remove'][src][0]) or \ - self.same_source(source_u[VERSION], self.hints['remove'][src][0]): - excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1])) + for item in self.hints.hints('remove', package=src): + package = item.packages[0] + if source_t and self.same_source(source_t[VERSION], package.version) or \ + self.same_source(source_u[VERSION], package.version): + excuse.addhtml("Removal request by %s" % (item.user)) excuse.addhtml("Trying to remove package, not update it") update_candidate = False # check if there is a `block' or `block-udeb' hint for this package, or a `block-all source' hint blocked = {} - if src in self.hints["block"]: - blocked["block"] = self.hints["block"][src] - elif 'source' in self.hints["block-all"]: - blocked["block"] = self.hints["block-all"]["source"] - - if src in self.hints["block-udeb"]: - blocked["block-udeb"] = self.hints["block-udeb"][src] + for hint in self.hints.hints(package=src): + if hint.type == 'block' or (hint.type == 'block-all' and hint.packages[0] == 'source' and hint not in blocked['block']): + blocked['block'] = hint + if hint.type == 'block-udeb': + blocked['block-udeb'] = hint # if the source is blocked, then look for an `unblock' hint; the unblock request # is processed only if the specified version is correct. If a package is blocked # by `block-udeb', then `unblock-udeb' must be present to cancel it. for block_cmd in blocked: unblock_cmd = "un" + block_cmd - unblock = self.hints[unblock_cmd].get(src,(None,None)) - if unblock[0] != None and self.same_source(unblock[0], source_u[VERSION]): + unblocks = self.hints.hints(unblock_cmd, package=src) + + if unblocks and self.same_source(unblocks[0].version, source_u[VERSION]): excuse.addhtml("Ignoring %s request by %s, due to %s request by %s" % - (block_cmd, blocked[block_cmd], unblock_cmd, self.hints[unblock_cmd][src][1])) + (block_cmd, blocked[block_cmd].user, unblock_cmd, unblocks[0].user)) else: - if unblock[0] != None: + if unblocks: excuse.addhtml("%s request by %s ignored due to version mismatch: %s" % - (unblock_cmd.capitalize(), self.hints[unblock_cmd][src][1], self.hints[unblock_cmd][src][0])) + (unblock_cmd.capitalize(), blocked[block_cmd].user, unblocks[0].version)) excuse.addhtml("Not touching package due to %s request by %s (contact debian-release if update is needed)" % - (block_cmd, blocked[block_cmd])) + (block_cmd, blocked[block_cmd].user)) update_candidate = False # if the suite is unstable, then we have to check the urgency and the minimum days of @@ -1305,17 +1307,18 @@ class Britney: days_old = self.date_now - self.dates[src][1] min_days = self.MINDAYS[urgency] - age_days_hint = self.hints["age-days"].get(src) - if age_days_hint is not None and (age_days_hint[0] == "-" or \ - self.same_source(source_u[VERSION], age_days_hint[0])): + for age_days_hint in [ x for x in self.hints.hints('age-days', package=src) if \ + self.same_source(source_u[VERSION], x.packages[0].version) ]: excuse.addhtml("Overriding age needed from %d days to %d by %s" % (min_days, - int(self.hints["age-days"][src][2]), self.hints["age-days"][src][1])) - min_days = int(self.hints["age-days"][src][2]) + int(age_days_hint.days), age_days_hint.user)) + min_days = int(age_days_hint.days) excuse.setdaysold(days_old, min_days) if days_old < min_days: - if src in self.hints["urgent"] and self.same_source(source_u[VERSION], self.hints["urgent"][src][0]): - excuse.addhtml("Too young, but urgency pushed by %s" % (self.hints["urgent"][src][1])) + urgent_hints = [ x for x in self.hints.hints('urgent', package=src) if \ + self.same_source(source_u[VERSION], x.packages[0].version) ] + if urgent_hints: + excuse.addhtml("Too young, but urgency pushed by %s" % (urgent_hints[0].user)) else: update_candidate = False @@ -1436,19 +1439,19 @@ class Britney: "though it fixes more than it introduces, whine at debian-release)" % pkg) # check if there is a `force' hint for this package, which allows it to go in even if it is not updateable - if src in self.hints["force"] and self.same_source(source_u[VERSION], self.hints["force"][src][0]): + forces = [ x for x in self.hints.hints('force', package=src) if self.same_source(source_u[VERSION], x.packages[0].version) ] + if forces: excuse.dontinvalidate = 1 - if not update_candidate and src in self.hints["force"] and \ - self.same_source(source_u[VERSION], self.hints["force"][src][0]): - excuse.addhtml("Should ignore, but forced by %s" % (self.hints["force"][src][1])) + if not update_candidate and forces: + excuse.addhtml("Should ignore, but forced by %s" % (forces[0].user)) update_candidate = True # if the suite is *-proposed-updates, the package needs an explicit approval in order to go in if suite in ['tpu', 'pu']: key = "%s_%s" % (src, source_u[VERSION]) - if src in self.hints["approve"] and \ - self.same_source(source_u[VERSION], self.hints["approve"][src][0]): - excuse.addhtml("Approved by %s" % self.hints["approve"][src][1]) + approves = [ x for x in self.hints.hints('approve', package=src) if self.same_source(source_u[VERSION], x.packages[0].version) ] + if approves: + excuse.addhtml("Approved by %s" % approves[0].user) else: excuse.addhtml("NEEDS APPROVAL BY RM") update_candidate = False @@ -1573,20 +1576,21 @@ class Britney: upgrade_me.append("%s_%s" % (pkg, suite)) # process the `remove' hints, if the given package is not yet in upgrade_me - for src in self.hints["remove"].keys(): + for item in self.hints['remove']: + src = item.packages[0].package if src in upgrade_me: continue if ("-"+src) in upgrade_me: continue if src not in sources['testing']: continue # check if the version specified in the hint is the same as the considered package tsrcv = sources['testing'][src][VERSION] - if not self.same_source(tsrcv, self.hints["remove"][src][0]): continue + if not self.same_source(tsrcv, item.packages[0].version): continue # add the removal of the package to upgrade_me and build a new excuse upgrade_me.append("-%s" % (src)) excuse = Excuse("-%s" % (src)) excuse.set_vers(tsrcv, None) - excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1])) + excuse.addhtml("Removal request by %s" % (item.packages[0].user)) excuse.addhtml("Package is broken, will try to remove") self.excuses.append(excuse) @@ -1631,7 +1635,7 @@ class Britney: self.invalidate_excuses(upgrade_me, unconsidered) # sort the list of candidates - self.upgrade_me = sorted(upgrade_me) + self.upgrade_me = sorted([ MigrationItem(x) for x in upgrade_me ]) # write excuses to the output file if not self.options.dry_run: @@ -2252,7 +2256,7 @@ class Britney: pre_process = {} if selected and hint: for package in selected: - pkg, affected, undo = self.doop_source(MigrationItem(package)) + pkg, affected, undo = self.doop_source(package) pre_process[package] = (pkg, affected, undo) lundo = [] @@ -2291,7 +2295,7 @@ class Britney: if pkg in pre_process: item, affected, undo = pre_process[pkg] else: - item, affected, undo = self.doop_source(MigrationItem(pkg), lundo) + item, affected, undo = self.doop_source(pkg, lundo) if hint: lundo.append((undo, item)) @@ -2371,7 +2375,7 @@ class Britney: break # if we are processing hints or the package is already accepted, go ahead - if hint or item.name in selected: continue + if hint or item in selected: continue # check if the action improved the uninstallability counters if better: @@ -2384,14 +2388,14 @@ class Britney: self.output_write(" pre: %s\n" % (self.eval_nuninst(nuninst_comp))) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst, nuninst_comp))) if len(selected) <= 20: - self.output_write(" all: %s\n" % (" ".join(selected))) + self.output_write(" all: %s\n" % (" ".join([ str(x) for x in selected ]))) else: - self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join(selected[-20:]))) + self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join([str(x) for x in selected][-20:]))) for k in nuninst: nuninst_comp[k] = nuninst[k] else: self.output_write("skipped: %s (%d <- %d)\n" % (pkg, len(extra), len(packages))) - self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst, "/" in pkg and nuninst_comp or None))) + self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst, pkg.architecture != 'source' and nuninst_comp or None))) self.output_write(" * %s: %s\n" % (arch, ", ".join(sorted([b for b in nuninst[arch] if b not in nuninst_comp[arch]])))) extra.append(pkg) @@ -2438,7 +2442,7 @@ class Britney: if hint: return (nuninst_comp, [], lundo) - self.output_write(" finish: [%s]\n" % ",".join(selected)) + self.output_write(" finish: [%s]\n" % ",".join([ str(x) for x in selected ])) self.output_write("endloop: %s\n" % (self.eval_nuninst(self.nuninst_orig))) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_comp))) self.output_write(self.eval_uninst(self.newlyuninst(self.nuninst_orig, nuninst_comp))) @@ -2471,10 +2475,10 @@ class Britney: # if we have a list of initial packages, check them if init: - self.output_write("leading: %s\n" % (",".join(init))) + self.output_write("leading: %s\n" % (",".join([ str(x) for x in init ]))) for x in init: if x not in upgrade_me: - self.output_write("failed: %s\n" % (x)) + self.output_write("failed: %s\n" % (x.uvname)) return None selected.append(x) upgrade_me.remove(x) @@ -2509,7 +2513,7 @@ class Britney: if nuninst_end: if not force and not earlyabort: self.output_write("Apparently successful\n") - self.output_write("final: %s\n" % ",".join(sorted(selected))) + self.output_write("final: %s\n" % ",".join(sorted([ str(x) for x in selected ]))) self.output_write("start: %s\n" % self.eval_nuninst(nuninst_start)) if not force: self.output_write(" orig: %s\n" % self.eval_nuninst(self.nuninst_orig)) @@ -2594,11 +2598,11 @@ class Britney: if not self.options.actions: # process `easy' hints for x in self.hints['easy']: - self.do_hint("easy", x[0], x[1]) + self.do_hint("easy", x.user, x.packages) # process `force-hint' hints for x in self.hints["force-hint"]: - self.do_hint("force-hint", x[0], x[1]) + self.do_hint("force-hint", x.user, x.packages) # run the first round of the upgrade self.__log("> First loop on the packages with depth = 0", type="I") @@ -2634,7 +2638,7 @@ class Britney: if hintcnt > 50: self.output_write("Skipping remaining hints...") break - if self.do_hint("hint", x[0], x[1]): + if self.do_hint("hint", x.user, x.packages): hintcnt += 1 # run the auto hinter @@ -2729,38 +2733,38 @@ class Britney: "hint": 0, "force-hint": -1,} + if isinstance(pkgvers[0], tuple): + _pkgvers = [ HintItem('%s/%s' % (p, v)) for (p,v) in pkgvers ] + else: + _pkgvers = pkgvers + self.__log("> Processing '%s' hint from %s" % (type, who), type="I") - self.output_write("Trying %s from %s: %s\n" % (type, who, " ".join( ["%s/%s" % (p,v) for (p,v) in pkgvers]))) + self.output_write("Trying %s from %s: %s\n" % (type, who, " ".join( ["%s/%s" % (x.uvname, x.version) for x in _pkgvers]))) ok = True # loop on the requested packages and versions - for pkg, v in pkgvers: - # remove architecture - if "/" in pkg: - pkg = pkg[:pkg.find("/")] - + for pkg in _pkgvers: # skip removal requests - if pkg[0] == "-": + if pkg.is_removal: continue # handle *-proposed-updates - elif pkg.endswith("_tpu") or pkg.endswith("_pu"): - pkg, suite = pkg.rsplit("_") - if pkg not in self.sources[suite]: continue - if apt_pkg.VersionCompare(self.sources[suite][pkg][VERSION], v) != 0: - self.output_write(" Version mismatch, %s %s != %s\n" % (pkg, v, self.sources[suite][pkg][VERSION])) + elif pkg.suite in ['pu', 'tpu']: + if pkg.package not in self.sources[pkg.suite]: continue + if apt_pkg.VersionCompare(self.sources[pkg.suite][pkg.package][VERSION], pkg.version) != 0: + self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources[suite][pkg.package][VERSION])) ok = False # does the package exist in unstable? - elif pkg not in self.sources['unstable']: - self.output_write(" Source %s has no version in unstable\n" % pkg) + elif pkg.package not in self.sources['unstable']: + self.output_write(" Source %s has no version in unstable\n" % pkg.package) ok = False - elif apt_pkg.VersionCompare(self.sources['unstable'][pkg][VERSION], v) != 0: - self.output_write(" Version mismatch, %s %s != %s\n" % (pkg, v, self.sources['unstable'][pkg][VERSION])) + elif apt_pkg.VersionCompare(self.sources['unstable'][pkg.package][VERSION], pkg.version) != 0: + self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources['unstable'][pkg.package][VERSION])) ok = False if not ok: self.output_write("Not using hint\n") return False - self.do_all(hintinfo[type], map(operator.itemgetter(0), pkgvers)) + self.do_all(hintinfo[type], _pkgvers) return True def sort_actions(self): @@ -2814,7 +2818,7 @@ class Britney: self.__log("> Processing hints from the auto hinter", type="I") # consider only excuses which are valid candidates - excuses = dict([(x.name, x) for x in self.excuses if x.name in self.upgrade_me]) + excuses = dict([(x.name, x) for x in self.excuses if x.name in [y.uvname for y in self.upgrade_me]]) def find_related(e, hint, circular_first=False): if e not in excuses: @@ -2866,7 +2870,7 @@ class Britney: to_skip.append(i) for i in range(len(candidates)): if i not in to_skip: - self.do_hint("easy", "autohinter", candidates[i]) + self.do_hint("easy", "autohinter", [ HintItem("%s/%s" % (x[0], x[1])) for x in candidates[i] ]) def old_libraries(self): """Detect old libraries left in testing for smooth transitions From 2401e90bceccf413a6d163023d2f86f99ec28215 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 17:05:35 +0000 Subject: [PATCH 12/46] hints: fix up hint parsing code for age-days string.split()'s second argument specifies the maximum number of times the string should be split, not the maximum number of elements in the result. Signed-off-by: Adam D. Barratt --- hints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hints.py b/hints.py index bc2b87e..2629ef6 100644 --- a/hints.py +++ b/hints.py @@ -44,14 +44,14 @@ class Hint: self._type = hint[0] self._packages = hint[1:] else: - self._type, self._packages = hint.split(' ', 2) + self._type, self._packages = hint.split(' ', 1) if self._type == 'age-days': if isinstance(hint, list): self._days = self._packages[0] self._packages = self._packages[1:] else: - self._days, self._packages = self._packages.split(' ', 2) + self._days, self._packages = self._packages.split(' ', 1) self._packages = [HintItem(x) for x in self._packages] From ef2fc7750f49a726454a533c38b146c8dfc30583 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 18:24:26 +0000 Subject: [PATCH 13/46] hints: allow hints to be stringified Signed-off-by: Adam D. Barratt --- hints.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hints.py b/hints.py index 2629ef6..e893ce2 100644 --- a/hints.py +++ b/hints.py @@ -37,6 +37,7 @@ class HintCollection: class Hint: def __init__(self, hint, user): + self._hint = hint self._user = user self._active = True self._days = None @@ -58,6 +59,9 @@ class Hint: def set_active(self, active): self._active = active + def __str__(self): + return self._hint + @property def type(self): return self._type From 76a83ae527db061423bdbb349227fef16d7ce8dc Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 18:24:54 +0000 Subject: [PATCH 14/46] Fix some brown paper bag bugs in the hint parsing code. Signed-off-by: Adam D. Barratt --- britney.py | 4 ++-- hints.py | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/britney.py b/britney.py index 5e916cd..2f144dd 100755 --- a/britney.py +++ b/britney.py @@ -796,7 +796,7 @@ class Britney: continue elif l[0] in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove"]: for package in l[1:]: - hints.add_hint('%s %s"' % (l[0], package), who) + hints.add_hint('%s %s' % (l[0], package), who) elif l[0] in ["age-days"]: for package in l[2:]: hints.add_hint('%s %s %s' % (l[0], l[1], package), who) @@ -1590,7 +1590,7 @@ class Britney: upgrade_me.append("-%s" % (src)) excuse = Excuse("-%s" % (src)) excuse.set_vers(tsrcv, None) - excuse.addhtml("Removal request by %s" % (item.packages[0].user)) + excuse.addhtml("Removal request by %s" % (item.user)) excuse.addhtml("Package is broken, will try to remove") self.excuses.append(excuse) diff --git a/hints.py b/hints.py index e893ce2..de03786 100644 --- a/hints.py +++ b/hints.py @@ -54,6 +54,9 @@ class Hint: else: self._days, self._packages = self._packages.split(' ', 1) + if isinstance(self._packages, str): + self._packages = self._packages.split(' ') + self._packages = [HintItem(x) for x in self._packages] def set_active(self, active): @@ -77,3 +80,7 @@ class Hint: @property def user(self): return self._user + + @property + def days(self): + return self._days From a5e448fd277a70ee88f9b71de2387398062f0c9f Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 18:58:14 +0000 Subject: [PATCH 15/46] hints: support package and version properties. Each property returns the value of the corresponding property for the first entry in the package list; this is a handy short-cut for hints where there will only ever be one package in the list (e.g. "age-days", "unblock"). Signed-off-by: Adam D. Barratt --- hints.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hints.py b/hints.py index de03786..0ce0993 100644 --- a/hints.py +++ b/hints.py @@ -84,3 +84,18 @@ class Hint: @property def days(self): return self._days + + @property + def package(self): + if self.packages: + return self.packages[0].package + else: + return None + + @property + def version(self): + if self.packages: + return self.packages[0].version + else: + return None + From ffccdae624864543ad83e6a1d433b38adf57c769 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 19:00:59 +0000 Subject: [PATCH 16/46] A failed unblock hint should list that hint's user, not the block's Signed-off-by: Adam D. Barratt --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index 2f144dd..d452ea1 100755 --- a/britney.py +++ b/britney.py @@ -1289,7 +1289,7 @@ class Britney: else: if unblocks: excuse.addhtml("%s request by %s ignored due to version mismatch: %s" % - (unblock_cmd.capitalize(), blocked[block_cmd].user, unblocks[0].version)) + (unblock_cmd.capitalize(), unblocks[0].user, unblocks[0].version)) excuse.addhtml("Not touching package due to %s request by %s (contact debian-release if update is needed)" % (block_cmd, blocked[block_cmd].user)) update_candidate = False From 3731b6295831808cfef7c31cd97f29cba37637ad Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 19:46:38 +0000 Subject: [PATCH 17/46] Migrate "packages[0]"-style code to the package and version properties. Signed-off-by: Adam D. Barratt --- britney.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/britney.py b/britney.py index d452ea1..afe2ab2 100755 --- a/britney.py +++ b/britney.py @@ -806,25 +806,24 @@ class Britney: for x in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove", "age-days"]: z = {} for hint in hints[x]: - item = hint.packages[0] - package = item.package - if z.has_key(package) and z[package] != item.version: + package = hint.package + if z.has_key(package) and z[package] != hint.version: if x in ['unblock', 'unblock-udeb']: - if apt_pkg.VersionCompare(z[package], item.version) < 0: + if apt_pkg.VersionCompare(z[package], hint.version) < 0: # This hint is for a newer version, so discard the old one - self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], item.version), type="W") + self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], hint.version), type="W") for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: other.set_active(False) else: # This hint is for an older version, so ignore it in favour of the new one - self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, package, item.version, z[package]), type="W") + self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, package, hint.version, z[package]), type="W") hint.set_active(False) else: - self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], item.version), type="W") + self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], hint.version), type="W") for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: other.set_active(False) - z[package] = item.version + z[package] = hint.version # Sanity check the hints hash if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0: @@ -1122,7 +1121,7 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False - for hint in [ x for x in self.hints.hints('remove', package=src) if self.same_source(source_t[VERSION], x.packages[0].version) ]: + for hint in [ x for x in self.hints.hints('remove', package=src) if self.same_source(source_t[VERSION], x.version) ]: excuse.addhtml("Removal request by %s" % (hint.user)) excuse.addhtml("Trying to remove package, not update it") excuse.addhtml("Not considered") @@ -1261,9 +1260,8 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False for item in self.hints.hints('remove', package=src): - package = item.packages[0] - if source_t and self.same_source(source_t[VERSION], package.version) or \ - self.same_source(source_u[VERSION], package.version): + if source_t and self.same_source(source_t[VERSION], item.version) or \ + self.same_source(source_u[VERSION], item.version): excuse.addhtml("Removal request by %s" % (item.user)) excuse.addhtml("Trying to remove package, not update it") update_candidate = False @@ -1271,7 +1269,7 @@ class Britney: # check if there is a `block' or `block-udeb' hint for this package, or a `block-all source' hint blocked = {} for hint in self.hints.hints(package=src): - if hint.type == 'block' or (hint.type == 'block-all' and hint.packages[0] == 'source' and hint not in blocked['block']): + if hint.type == 'block' or (hint.type == 'block-all' and hint.package == 'source' and hint not in blocked['block']): blocked['block'] = hint if hint.type == 'block-udeb': blocked['block-udeb'] = hint @@ -1308,7 +1306,7 @@ class Britney: min_days = self.MINDAYS[urgency] for age_days_hint in [ x for x in self.hints.hints('age-days', package=src) if \ - self.same_source(source_u[VERSION], x.packages[0].version) ]: + self.same_source(source_u[VERSION], x.version) ]: excuse.addhtml("Overriding age needed from %d days to %d by %s" % (min_days, int(age_days_hint.days), age_days_hint.user)) min_days = int(age_days_hint.days) @@ -1316,7 +1314,7 @@ class Britney: excuse.setdaysold(days_old, min_days) if days_old < min_days: urgent_hints = [ x for x in self.hints.hints('urgent', package=src) if \ - self.same_source(source_u[VERSION], x.packages[0].version) ] + self.same_source(source_u[VERSION], x.version) ] if urgent_hints: excuse.addhtml("Too young, but urgency pushed by %s" % (urgent_hints[0].user)) else: @@ -1439,7 +1437,7 @@ class Britney: "though it fixes more than it introduces, whine at debian-release)" % pkg) # check if there is a `force' hint for this package, which allows it to go in even if it is not updateable - forces = [ x for x in self.hints.hints('force', package=src) if self.same_source(source_u[VERSION], x.packages[0].version) ] + forces = [ x for x in self.hints.hints('force', package=src) if self.same_source(source_u[VERSION], x.version) ] if forces: excuse.dontinvalidate = 1 if not update_candidate and forces: @@ -1449,7 +1447,7 @@ class Britney: # if the suite is *-proposed-updates, the package needs an explicit approval in order to go in if suite in ['tpu', 'pu']: key = "%s_%s" % (src, source_u[VERSION]) - approves = [ x for x in self.hints.hints('approve', package=src) if self.same_source(source_u[VERSION], x.packages[0].version) ] + approves = [ x for x in self.hints.hints('approve', package=src) if self.same_source(source_u[VERSION], x.version) ] if approves: excuse.addhtml("Approved by %s" % approves[0].user) else: @@ -1577,14 +1575,14 @@ class Britney: # process the `remove' hints, if the given package is not yet in upgrade_me for item in self.hints['remove']: - src = item.packages[0].package + src = item.package if src in upgrade_me: continue if ("-"+src) in upgrade_me: continue if src not in sources['testing']: continue # check if the version specified in the hint is the same as the considered package tsrcv = sources['testing'][src][VERSION] - if not self.same_source(tsrcv, item.packages[0].version): continue + if not self.same_source(tsrcv, item.version): continue # add the removal of the package to upgrade_me and build a new excuse upgrade_me.append("-%s" % (src)) From 02ab64272c424e4bd3870d6b12ded00ba637645c Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sun, 4 Sep 2011 20:26:36 +0000 Subject: [PATCH 18/46] hints: rename the "hints" method to the more helpful "search". Signed-off-by: Adam D. Barratt --- britney.py | 18 +++++++++--------- hints.py | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/britney.py b/britney.py index afe2ab2..2f6e6c2 100755 --- a/britney.py +++ b/britney.py @@ -1084,7 +1084,7 @@ class Britney: src[SECTION] and excuse.set_section(src[SECTION].strip()) # if the package is blocked, skip it - for hint in self.hints.hints('block', package=pkg, removal=True): + for hint in self.hints.search('block', package=pkg, removal=True): excuse.addhtml("Not touching package, as requested by %s (contact debian-release " "if update is needed)" % hint.user) excuse.addhtml("Not considered") @@ -1121,7 +1121,7 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False - for hint in [ x for x in self.hints.hints('remove', package=src) if self.same_source(source_t[VERSION], x.version) ]: + for hint in [ x for x in self.hints.search('remove', package=src) if self.same_source(source_t[VERSION], x.version) ]: excuse.addhtml("Removal request by %s" % (hint.user)) excuse.addhtml("Trying to remove package, not update it") excuse.addhtml("Not considered") @@ -1259,7 +1259,7 @@ class Britney: # if there is a `remove' hint and the requested version is the same as the # version in testing, then stop here and return False - for item in self.hints.hints('remove', package=src): + for item in self.hints.search('remove', package=src): if source_t and self.same_source(source_t[VERSION], item.version) or \ self.same_source(source_u[VERSION], item.version): excuse.addhtml("Removal request by %s" % (item.user)) @@ -1268,7 +1268,7 @@ class Britney: # check if there is a `block' or `block-udeb' hint for this package, or a `block-all source' hint blocked = {} - for hint in self.hints.hints(package=src): + for hint in self.hints.search(package=src): if hint.type == 'block' or (hint.type == 'block-all' and hint.package == 'source' and hint not in blocked['block']): blocked['block'] = hint if hint.type == 'block-udeb': @@ -1279,7 +1279,7 @@ class Britney: # by `block-udeb', then `unblock-udeb' must be present to cancel it. for block_cmd in blocked: unblock_cmd = "un" + block_cmd - unblocks = self.hints.hints(unblock_cmd, package=src) + unblocks = self.hints.search(unblock_cmd, package=src) if unblocks and self.same_source(unblocks[0].version, source_u[VERSION]): excuse.addhtml("Ignoring %s request by %s, due to %s request by %s" % @@ -1305,7 +1305,7 @@ class Britney: days_old = self.date_now - self.dates[src][1] min_days = self.MINDAYS[urgency] - for age_days_hint in [ x for x in self.hints.hints('age-days', package=src) if \ + for age_days_hint in [ x for x in self.hints.search('age-days', package=src) if \ self.same_source(source_u[VERSION], x.version) ]: excuse.addhtml("Overriding age needed from %d days to %d by %s" % (min_days, int(age_days_hint.days), age_days_hint.user)) @@ -1313,7 +1313,7 @@ class Britney: excuse.setdaysold(days_old, min_days) if days_old < min_days: - urgent_hints = [ x for x in self.hints.hints('urgent', package=src) if \ + urgent_hints = [ x for x in self.hints.search('urgent', package=src) if \ self.same_source(source_u[VERSION], x.version) ] if urgent_hints: excuse.addhtml("Too young, but urgency pushed by %s" % (urgent_hints[0].user)) @@ -1437,7 +1437,7 @@ class Britney: "though it fixes more than it introduces, whine at debian-release)" % pkg) # check if there is a `force' hint for this package, which allows it to go in even if it is not updateable - forces = [ x for x in self.hints.hints('force', package=src) if self.same_source(source_u[VERSION], x.version) ] + forces = [ x for x in self.hints.search('force', package=src) if self.same_source(source_u[VERSION], x.version) ] if forces: excuse.dontinvalidate = 1 if not update_candidate and forces: @@ -1447,7 +1447,7 @@ class Britney: # if the suite is *-proposed-updates, the package needs an explicit approval in order to go in if suite in ['tpu', 'pu']: key = "%s_%s" % (src, source_u[VERSION]) - approves = [ x for x in self.hints.hints('approve', package=src) if self.same_source(source_u[VERSION], x.version) ] + approves = [ x for x in self.hints.search('approve', package=src) if self.same_source(source_u[VERSION], x.version) ] if approves: excuse.addhtml("Approved by %s" % approves[0].user) else: diff --git a/hints.py b/hints.py index 0ce0993..1f72143 100644 --- a/hints.py +++ b/hints.py @@ -19,9 +19,9 @@ class HintCollection: self._hints = [] def __getitem__(self, type=None): - return self.hints(type) + return self.search(type) - def hints(self, type=None, onlyactive=True, package=None, \ + def search(self, type=None, onlyactive=True, package=None, \ version=None, days=None, removal=None): return [ hint for hint in self._hints if From 4ddb92db214e3a4510f8970783ca6a58a6b5fb84 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 14:47:00 +0000 Subject: [PATCH 19/46] Fix up the addition of "unblock" hints for t-p-u approvals. Thanks to Niels Thykier for the original idea. Signed-off-by: Adam D. Barratt --- britney.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/britney.py b/britney.py index 2f6e6c2..7b9b8e8 100755 --- a/britney.py +++ b/britney.py @@ -831,7 +831,9 @@ class Britney: # A (t-)p-u approval overrides an unstable block for p in hints["approve"]: - hints["unblock"][p] = hints["approve"][p] + for o in hints.search('unblock', package=p.package): + o.set_active(False) + hints.add_hint('unblock %s/%s' % (p.package, p.version), p.user) return hints From 0fa6e4c3fbd122de50a03101c691ec061290af29 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 19:25:43 +0000 Subject: [PATCH 20/46] Make sort_actions support MigrationItems Signed-off-by: Adam D. Barratt --- britney.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/britney.py b/britney.py index 7b9b8e8..b9f0282 100755 --- a/britney.py +++ b/britney.py @@ -2775,7 +2775,7 @@ class Britney: so the ones with most reverse dependencies are at the end of the loop. If an action depends on another one, it is put after it. """ - upgrade_me = [x.name for x in self.excuses if x.name in self.upgrade_me] + upgrade_me = [x.name for x in self.excuses if x.name in [y.uvname for y in self.upgrade_me]] for e in self.excuses: if e.name not in upgrade_me: continue # try removes at the end of the loop @@ -2797,7 +2797,7 @@ class Britney: self.dependencies[e.name] = e.deps # replace the list of actions with the new one - self.upgrade_me = upgrade_me + self.upgrade_me = [ MigrationItem(x) for x in upgrade_me ] def auto_hinter(self): """Auto-generate "easy" hints. From d62f8f6bccead55186bbf07870d56808b96f593b Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 19:26:57 +0000 Subject: [PATCH 21/46] smooth updates: pass a list of MigrationItems to remove, not strings Signed-off-by: Adam D. Barratt --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index b9f0282..2eea78f 100755 --- a/britney.py +++ b/britney.py @@ -2652,7 +2652,7 @@ class Britney: if len(removals) > 0: self.output_write("Removing packages left in testing for smooth updates (%d):\n%s" % \ (len(removals), self.old_libraries_format(removals))) - self.do_all(actions=removals) + self.do_all(actions=[ MigrationItem(x) for x in removals ]) removals = self.old_libraries() else: removals = () From 0e50a0ef442394be24b03922de73329913478537 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 19:28:09 +0000 Subject: [PATCH 22/46] Fix up broken logic so that binary removals work again Signed-off-by: Adam D. Barratt --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index 2eea78f..d8b03a8 100755 --- a/britney.py +++ b/britney.py @@ -2059,7 +2059,7 @@ class Britney: sources = self.sources binaries = self.binaries['testing'] # remove all binary packages (if the source already exists) - if item.architecture == 'source' or item.is_removal: + if item.architecture == 'source' or not item.is_removal: if item.package in sources['testing']: source = sources['testing'][item.package] # remove all the binaries From 504a7c2d61e61f3aecc24289fb0048acad38de16 Mon Sep 17 00:00:00 2001 From: Niels Thykier Date: Sat, 22 Oct 2011 16:58:02 +0200 Subject: [PATCH 23/46] Allow a list as well as a tuple in Britney.do_hint --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index d8b03a8..5c4ddf8 100755 --- a/britney.py +++ b/britney.py @@ -2733,7 +2733,7 @@ class Britney: "hint": 0, "force-hint": -1,} - if isinstance(pkgvers[0], tuple): + if isinstance(pkgvers[0], tuple) or isinstance(pkgvers[0], list): _pkgvers = [ HintItem('%s/%s' % (p, v)) for (p,v) in pkgvers ] else: _pkgvers = pkgvers From 91baa5a4d67d6f8ee55be1a5c5578a59ff5ac80d Mon Sep 17 00:00:00 2001 From: Niels Thykier Date: Wed, 19 Oct 2011 11:30:45 +0200 Subject: [PATCH 24/46] Removed two unused methods, check_installable and check_conflicts --- britney.py | 265 ----------------------------------------------------- 1 file changed, 265 deletions(-) diff --git a/britney.py b/britney.py index 5c4ddf8..363fb0e 100755 --- a/britney.py +++ b/britney.py @@ -1772,271 +1772,6 @@ class Britney: diff = diff + (len(new[arch]) - len(old[arch])) return diff <= 0 - def check_installable(self, pkg, arch, suite, excluded=[], conflicts=False): - """Check if a package is installable - - This method analyzes the dependencies of the binary package specified - by the parameter `pkg' for the architecture `arch' within the suite - `suite'. If the dependency can be satisfied in the given `suite` and - `conflicts` parameter is True, then the co-installability with - conflicts handling is checked. - - The dependency fields checked are Pre-Depends and Depends. - - The method returns a boolean which is True if the given package is - installable. - - NOTE: this method has been deprecated, actually we use is_installable - from the britney c extension called within a testing system. See - self.build_systems for more information. - """ - self.__log("WARNING: method check_installable is deprecated: use is_installable instead!", type="E") - - # retrieve the binary package from the specified suite and arch - binary_u = self.binaries[suite][arch][0][pkg] - - # local copies for better performances - parse_depends = apt_pkg.ParseDepends - get_dependency_solvers = self.get_dependency_solvers - - # analyze the dependency fields (if present) - for type in (PREDEPENDS, DEPENDS): - if not binary_u[type]: - continue - - # for every block of dependency (which is formed as conjunction of disconjunction) - for block in parse_depends(binary_u[type]): - # if the block is not satisfied, return False - solved, packages = get_dependency_solvers(block, arch, 'testing', excluded, strict=True) - if not solved: - return False - - # otherwise, the package is installable (not considering conflicts) - # if the conflicts handling is enabled, then check conflicts before - # saying that the package is really installable - if conflicts: - return self.check_conflicts(pkg, arch, excluded, {}, {}) - - return True - - def check_conflicts(self, pkg, arch, broken, system, conflicts): - """Check if a package can be installed satisfying the conflicts - - This method checks if the `pkg` package from the `arch` architecture - can be installed (excluding `broken` packages) within the system - `system` along with all its dependencies. This means that all the - conflicts relationships are checked in order to achieve the test - co-installability of the package. - - The method returns a boolean which is True if the given package is - co-installable in the given system. - """ - - # local copies for better performances - binaries = self.binaries['testing'][arch] - parse_depends = apt_pkg.ParseDepends - check_depends = apt_pkg.CheckDep - - # unregister conflicts, local method to remove conflicts - # registered from a given package. - def unregister_conflicts(pkg, conflicts): - for c in conflicts.keys(): - i = 0 - while i < len(conflicts[c]): - if conflicts[c][i][3] == pkg: - del conflicts[c][i] - else: i = i + 1 - if len(conflicts[c]) == 0: - del conflicts[c] - - def remove_package(pkg, system, conflicts): - for k in system: - if pkg in system[k][1]: - system[k][1].remove(pkg) - unregister_conflicts(pkg, conflicts) - - # handle a conflict, local method to solve a conflict which happened - # in the system; the behaviour of the conflict-solver is: - # 1. If there are alternatives for the package which must be removed, - # try them, and if one of them resolves the system return True; - # 2. If none of the alternatives can solve the conflict, then call - # itself for the package which depends on the conflicting package. - # 3. If the top of the dependency tree is reached, then the conflict - # can't be solved, so return False. - def handle_conflict(pkg, source, system, conflicts): - # skip packages which don't have reverse dependencies - if source not in system or system[source][1] == []: - remove_package(source, system, conflicts) - return (system, conflicts) - # reached the top of the tree - if not system[source][1][0]: - return False - # remove its conflicts - unregister_conflicts(source, conflicts) - # if there are alternatives, try them - alternatives = system[source][0] - for alt in alternatives: - if satisfy(alt, [x for x in alternatives if x != alt], pkg_from=system[source][1], - system=system, conflicts=conflicts, excluded=[source]): - remove_package(source, system, conflicts) - return (system, conflicts) - # there are no good alternatives, so remove the package which depends on it - for p in system[source][1]: - # the package does not exist, we reached the top of the tree - if not p: return False - # we are providing the package we conflict on (eg. exim4 and mail-transfer-agent), skip it - if p == pkg: continue - output = handle_conflict(pkg, p, system, conflicts) - if output: - system, conflicts = output - else: return False - remove_package(source, system, conflicts) - return (system, conflicts) - - # dependency tree satisfier, local method which tries to satisfy the dependency - # tree for a given package. It calls itself recursively in order to check the - # co-installability of the full tree of dependency of the starting package. - # If a conflict is detected, it tries to handle it calling the handle_conflict - # method; if it can't be resolved, then it returns False. - def satisfy(pkg, pkg_alt=None, pkg_from=None, system=system, conflicts=conflicts, excluded=[]): - # if it is a real package and it is already installed, skip it and return True - if pkg in binaries[0]: - if pkg in system: - if type(pkg_from) == list: - system[pkg][1].extend(pkg_from) - else: - system[pkg][1].append(pkg_from) - system[pkg] = (system[pkg][1], filter(lambda x: x in pkg_alt, system[pkg][0])) - return True - binary_u = binaries[0][pkg] - else: binary_u = None - - # if it is a virtual package - providers = [] - if pkg_from and pkg in binaries[1]: - providers = binaries[1][pkg] - # it is both real and virtual, so the providers are alternatives - if binary_u: - providers = filter(lambda x: (not pkg_alt or x not in pkg_alt) and x != pkg, providers) - if not pkg_alt: - pkg_alt = [] - pkg_alt.extend(providers) - # try all the alternatives and if none of them suits, give up and return False - else: - # if we already have a provider in the system, everything is ok and return True - if len(filter(lambda x: x in providers and x not in excluded, system)) > 0: - return True - for p in providers: - # try to install the providers skipping excluded packages, - # which we already tried but do not work - if p in excluded: continue - elif satisfy(p, [a for a in providers if a != p], pkg_from): - return True - # if none of them suits, return False - return False - - # if the package doesn't exist, return False - if not binary_u: return False - - # it is broken, but we have providers - if pkg in broken and pkg_from: - for p in providers: - # try to install the providers skipping excluded packages, - # which we already tried but do not work - if p in excluded: continue - elif satisfy(p, [a for a in providers if a != p], pkg_from): - return True - return False - - # install the package into the system, recording which package required it - if type(pkg_from) != list: - pkg_from = [pkg_from] - system[pkg] = (pkg_alt or [], pkg_from) - - # register provided packages - if binary_u[PROVIDES]: - for p in binary_u[PROVIDES]: - if p in system: - # do not consider packages providing the one which we are checking - if len(system[p][1]) == 1 and system[p][1][0] == None: continue - system[p][1].append(pkg) - else: - system[p] = ([], [pkg]) - - # check the conflicts - if pkg in conflicts: - for name, version, op, conflicting in conflicts[pkg]: - if conflicting in binary_u[PROVIDES] and system[conflicting][1] == [pkg]: continue - if op == '' and version == '' or check_depends(binary_u[VERSION], op, version): - # if conflict is found, check if it can be solved removing - # already-installed packages without breaking the system; if - # this is not possible, give up and return False - output = handle_conflict(pkg, conflicting, system.copy(), conflicts.copy()) - if output: - system, conflicts = output - else: - del system[pkg] - return False - - # register conflicts from the just-installed package - if binary_u[CONFLICTS]: - for block in map(operator.itemgetter(0), parse_depends(binary_u[CONFLICTS] or [])): - name, version, op = block - # skip conflicts for packages provided by itself - # if the conflicting package is in the system (and it is not a self-conflict) - if not (name in binary_u[PROVIDES] and system[name][1] == [pkg]) and \ - block[0] != pkg and block[0] in system: - if block[0] in binaries[0]: - binary_c = binaries[0][block[0]] - else: binary_c = None - if op == '' and version == '' or binary_c and check_depends(binary_c[VERSION], op, version): - # if conflict is found, check if it can be solved removing - # already-installed packages without breaking the system; if - # this is not possible, give up and return False - output = handle_conflict(pkg, name, system.copy(), conflicts.copy()) - if output: - system, conflicts = output - else: - del system[pkg] - unregister_conflicts(pkg, conflicts) - return False - # register the conflict - if block[0] not in conflicts: - conflicts[block[0]] = [] - conflicts[block[0]].append((name, version, op, pkg)) - - # list all its dependencies ... - dependencies = [] - for key in (PREDEPENDS, DEPENDS): - if not binary_u[key]: continue - dependencies.extend(parse_depends(binary_u[key])) - - # ... and go through them - for block in dependencies: - # list the possible alternatives, in case of a conflict - alternatives = map(operator.itemgetter(0), block) - valid = False - for name, version, op in block: - # otherwise, if it is already installed or it is installable, the block is satisfied - if name in system or satisfy(name, [a for a in alternatives if a != name], pkg): - valid = True - break - # if the block can't be satisfied, the package is not installable so - # we need to remove it, its conflicts and its provided packages and - # return False - if not valid: - del system[pkg] - unregister_conflicts(pkg, conflicts) - for p in providers: - if satisfy(p, [a for a in providers if a != p], pkg_from): - return True - return False - - # if all the blocks have been satisfied, the package is installable - return True - - # check the package at the top of the tree - return satisfy(pkg) def doop_source(self, item, hint_undo=[]): """Apply a change to the testing distribution as requested by `pkg` From aeca75b99da23832be84432ec64a63d2e66cce93 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 19:38:26 +0000 Subject: [PATCH 25/46] Remove an unused variable (part of 22a9427c21b6216e093252ba77e09d5f10e436f7 from master) Signed-off-by: Adam D. Barratt --- britney.py | 1 - 1 file changed, 1 deletion(-) diff --git a/britney.py b/britney.py index 363fb0e..de1d979 100755 --- a/britney.py +++ b/britney.py @@ -1448,7 +1448,6 @@ class Britney: # if the suite is *-proposed-updates, the package needs an explicit approval in order to go in if suite in ['tpu', 'pu']: - key = "%s_%s" % (src, source_u[VERSION]) approves = [ x for x in self.hints.search('approve', package=src) if self.same_source(source_u[VERSION], x.version) ] if approves: excuse.addhtml("Approved by %s" % approves[0].user) From 48866b513ef00853de00806a06095d3c5ca7f001 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Thu, 13 Oct 2011 20:22:33 +0000 Subject: [PATCH 26/46] Don't consider intra-source dependencies for smooth updates Signed-off-by: Adam D. Barratt --- britney.py | 1 + 1 file changed, 1 insertion(+) diff --git a/britney.py b/britney.py index de1d979..ddc4e92 100755 --- a/britney.py +++ b/britney.py @@ -1805,6 +1805,7 @@ class Britney: # if a smooth update is possible for the package, skip it if not self.options.compatible and item.suite == 'unstable' and \ binary not in self.binaries[item.suite][parch][0] and \ + len([x for x in rdeps if x not in [y.split("/")[0] for y in source[BINARIES]]]) > 0 and \ ('ALL' in self.options.smooth_updates or \ binaries[parch][0][binary][SECTION] in self.options.smooth_updates): continue From 120f783dfdff403b9cc9b347f9bc1db2da91bb2c Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 19 Oct 2011 18:23:52 +0000 Subject: [PATCH 27/46] When auto-hinting, also try a "minimal" package set The minimal set is comprised of only the first level of (reverse) dependencies, before any further iterations of packages are added to the set. In some cases, the result of the full iteration will contain packages which cause problems when migrated but the minimal set, although possibly a less optimal solution, may be able to migrate successfully. It is assumed that migrating the larger set of packages will be preferred if possible, so minimal sets are tried later. Signed-off-by: Adam D. Barratt --- britney.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/britney.py b/britney.py index ddc4e92..74ba8b1 100755 --- a/britney.py +++ b/britney.py @@ -2573,6 +2573,7 @@ class Britney: # loop on them candidates = [] + mincands = [] for e in excuses: excuse = excuses[e] if e in self.sources['testing'] and self.sources['testing'][e][VERSION] == excuse.ver[1]: @@ -2583,29 +2584,34 @@ class Britney: candidates.append(hint.items()) else: items = [ (e, excuse.ver[1]) ] + looped = False for item, ver in items: # excuses which depend on "item" or are depended on by it items.extend( [ (x, excuses[x].ver[1]) for x in excuses if \ (item in excuses[x].deps or x in excuses[item].deps) \ and (x, excuses[x].ver[1]) not in items ] ) + if not looped and len(items) > 1: + mincands.append(items[:]) + looped = True if len(items) > 1: candidates.append(items) - to_skip = [] - for i in range(len(candidates)): - for j in range(i+1, len(candidates)): - if i in to_skip or j in to_skip: - # we already know this list isn't interesting - continue - elif frozenset(candidates[i]) >= frozenset(candidates[j]): - # j is a subset of i; ignore it - to_skip.append(j) - elif frozenset(candidates[i]) <= frozenset(candidates[j]): - # i is a subset of j; ignore it - to_skip.append(i) - for i in range(len(candidates)): - if i not in to_skip: - self.do_hint("easy", "autohinter", [ HintItem("%s/%s" % (x[0], x[1])) for x in candidates[i] ]) + for l in [ candidates, mincands ]: + to_skip = [] + for i in range(len(l)): + for j in range(i+1, len(l)): + if i in to_skip or j in to_skip: + # we already know this list isn't interesting + continue + elif frozenset(l[i]) >= frozenset(l[j]): + # j is a subset of i; ignore it + to_skip.append(j) + elif frozenset(l[i]) <= frozenset(l[j]): + # i is a subset of j; ignore it + to_skip.append(i) + for i in range(len(l)): + if i not in to_skip: + self.do_hint("easy", "autohinter", [ HintItem("%s/%s" % (x[0], x[1])) for x in l[i] ]) def old_libraries(self): """Detect old libraries left in testing for smooth transitions From b85db2b308a643bbbe6e7bee643f0b1a14a86ccf Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 22 Oct 2011 19:58:07 +0000 Subject: [PATCH 28/46] Fix-up a broken merge so smooth updates work again Signed-off-by: Adam D. Barratt --- britney.py | 1 + 1 file changed, 1 insertion(+) diff --git a/britney.py b/britney.py index 30ce9f4..3ad946f 100755 --- a/britney.py +++ b/britney.py @@ -1800,6 +1800,7 @@ class Britney: if item.architecture != 'source' and parch != item.architecture: continue # do not remove binaries which have been hijacked by other sources if binaries[parch][0][binary][SOURCE] != item.package: continue + rdeps = binaries[parch][0][binary][RDEPENDS] # if a smooth update is possible for the package, skip it if not self.options.compatible and item.suite == 'unstable' and \ binary not in self.binaries[item.suite][parch][0] and \ From a906f55e0bec91b0ef92e07b6e7f5c260cd5fd99 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 29 Oct 2011 14:30:43 +0000 Subject: [PATCH 29/46] MigrationItem: fix typo in parsing of some *-p-u versions Signed-off-by: Adam D. Barratt --- migrationitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrationitem.py b/migrationitem.py index baa606e..8997e23 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -70,7 +70,7 @@ class MigrationItem: self._architecture = 'source' if '_' in self._architecture: - self_architecture, self._suite = \ + self._architecture, self._suite = \ self._architecture.split('_', 2) if self.is_removal: From edd0302c976ea0051a6009f2c6ab745121a7450d Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 29 Oct 2011 14:31:38 +0000 Subject: [PATCH 30/46] Drop some unused imports Signed-off-by: Adam D. Barratt --- britney.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/britney.py b/britney.py index 2a65771..e97a5f9 100755 --- a/britney.py +++ b/britney.py @@ -186,14 +186,13 @@ import sys import string import time import optparse -import operator import urllib import apt_pkg from excuse import Excuse from migrationitem import MigrationItem, HintItem -from hints import Hint, HintCollection +from hints import HintCollection from britney import buildSystem __author__ = 'Fabio Tranchitella and the Debian Release Team' From 8641af1c24581008fca101b2bcff303d6ee9e164 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 29 Oct 2011 14:32:26 +0000 Subject: [PATCH 31/46] Use the correct (and existing) variable in an error message Signed-off-by: Adam D. Barratt --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index e97a5f9..ba01db3 100755 --- a/britney.py +++ b/britney.py @@ -2540,7 +2540,7 @@ class Britney: elif pkg.suite in ['pu', 'tpu']: if pkg.package not in self.sources[pkg.suite]: continue if apt_pkg.VersionCompare(self.sources[pkg.suite][pkg.package][VERSION], pkg.version) != 0: - self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources[suite][pkg.package][VERSION])) + self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources[pkg.suite][pkg.package][VERSION])) ok = False # does the package exist in unstable? elif pkg.package not in self.sources['unstable']: From e95d127fb1bdcca3a9e7a7ce5db5ed10e12cb89d Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 29 Oct 2011 17:14:00 +0000 Subject: [PATCH 32/46] Fix odd spacing Signed-off-by: Adam D. Barratt --- migrationitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrationitem.py b/migrationitem.py index 8997e23..a889cf0 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -93,7 +93,7 @@ class MigrationItem: @property def architecture(self): - return self._architecture + return self._architecture @property def package(self): From 9c65c3e6b75c5e55d720a5f6051f015a5bdea954 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Sat, 29 Oct 2011 17:17:42 +0000 Subject: [PATCH 33/46] Ensure _package and _uvname attributes are always defined Signed-off-by: Adam D. Barratt --- migrationitem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index a889cf0..56b472c 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -15,6 +15,8 @@ class MigrationItem: def __init__(self, name = None, versionned = False): self._name = None + self._uvname = None + self._package = None self._version = None self._architecture = None self._suite = None From 6a360858ea9a0b83e1df325811baf759a4f140d8 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 9 Nov 2011 22:44:31 +0000 Subject: [PATCH 34/46] "Broken architectures" fix-up Signed-off-by: Adam D. Barratt --- britney.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/britney.py b/britney.py index eeec857..f1b3926 100755 --- a/britney.py +++ b/britney.py @@ -2379,7 +2379,7 @@ class Britney: normpackages = self.upgrade_me[:] archpackages = {} for a in self.options.break_arches.split(): - archpackages[a] = [p for p in normpackages if p.endswith("/" + a) or p.endswith("/" + a + "_tpu") or p.endswith("/" + a + "_pu")] + archpackages[a] = [p for p in normpackages if p.architecture == a] normpackages = [p for p in normpackages if p not in archpackages[a]] self.upgrade_me = normpackages self.output_write("info: main run\n") From e44b038ed76afff4234447e0b950d633c6c43565 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 9 Nov 2011 22:44:55 +0000 Subject: [PATCH 35/46] Correctly parse foo/arch/ver in unversionned mode Signed-off-by: Adam D. Barratt --- migrationitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrationitem.py b/migrationitem.py index 56b472c..3bb1189 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -66,7 +66,7 @@ class MigrationItem: self._architecture = 'source' self._version = parts[1] else: - if len(parts) == 2: + if len(parts) >= 2: self._architecture = parts[1] else: self._architecture = 'source' From debd6a7229647e433451b4a03fba3067415455bb Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 9 Nov 2011 23:24:42 +0000 Subject: [PATCH 36/46] MigrationItem: canonicalise item names on output Although multiple formats of item name are accepted (e.g. $pkg_$suite/$arch and $pkg/$arch_$suite), for compatibility and ease of parsing by clients the output format is standardised. Signed-off-by: Adam D. Barratt --- migrationitem.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/migrationitem.py b/migrationitem.py index 3bb1189..502fe0f 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -78,14 +78,17 @@ class MigrationItem: if self.is_removal: self._suite = 'testing' + parts = self._name.split('/', 3) + if len(parts) == 1 or self._architecture == 'source': + self._uvname = self._package + else: + self._uvname = "%s/%s" % (self._package, self._architecture) + if self._suite not in ('testing', 'unstable'): + self._uvname = '%s_%s' % (self._uvname, self._suite) if self._versionned: - parts = self._name.split('/', 3) - if len(parts) == 1 or self._architecture == 'source': - self._uvname = parts[0] - else: - self._uvname = "%s/%s" % (parts[0], parts[1]) + self._name = '%s/%s' % (self._uvname, self._version) else: - self._uvname = self._name + self._name = self._uvname name = property(_get_name, _set_name) From 3b876e743e92f4a572b53d7b82937408bc6e270e Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Wed, 9 Nov 2011 23:31:56 +0000 Subject: [PATCH 37/46] MigrationItem: fix name output for removal items Signed-off-by: Adam D. Barratt --- migrationitem.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index 502fe0f..272ff6e 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -79,12 +79,15 @@ class MigrationItem: self._suite = 'testing' parts = self._name.split('/', 3) + is_removal = self.is_removal if len(parts) == 1 or self._architecture == 'source': self._uvname = self._package else: self._uvname = "%s/%s" % (self._package, self._architecture) if self._suite not in ('testing', 'unstable'): self._uvname = '%s_%s' % (self._uvname, self._suite) + if is_removal: + self._uvname = '-%s' % (self._uvname) if self._versionned: self._name = '%s/%s' % (self._uvname, self._version) else: From ba0cdde6975d21d02f502413cb76335f31d6c21c Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Thu, 10 Nov 2011 10:30:13 +0000 Subject: [PATCH 38/46] Fix up spacing issues; thanks Niels. Signed-off-by: Adam D. Barratt --- britney.py | 4 ++-- migrationitem.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/britney.py b/britney.py index f1b3926..922962b 100755 --- a/britney.py +++ b/britney.py @@ -818,7 +818,7 @@ class Britney: self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], hint.version), type="W") for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: other.set_active(False) - + z[package] = hint.version # Sanity check the hints hash @@ -1278,7 +1278,7 @@ class Britney: for block_cmd in blocked: unblock_cmd = "un" + block_cmd unblocks = self.hints.search(unblock_cmd, package=src) - + if unblocks and self.same_source(unblocks[0].version, source_u[VERSION]): excuse.addhtml("Ignoring %s request by %s, due to %s request by %s" % (block_cmd, blocked[block_cmd].user, unblock_cmd, unblocks[0].user)) diff --git a/migrationitem.py b/migrationitem.py index 272ff6e..de4d46b 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -39,7 +39,7 @@ class MigrationItem: else: isequal = self.version == other.version - return isequal + return isequal def __hash__(self): return hash((self.uvname, self.version)) @@ -77,7 +77,7 @@ class MigrationItem: if self.is_removal: self._suite = 'testing' - + parts = self._name.split('/', 3) is_removal = self.is_removal if len(parts) == 1 or self._architecture == 'source': From 92eca3958c4369afbb0df993f09cf2c8b7acec83 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Thu, 10 Nov 2011 12:50:01 +0000 Subject: [PATCH 39/46] HintCollection::search(): fix "onlyactive" logic Signed-off-by: Adam D. Barratt --- hints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hints.py b/hints.py index 1f72143..2f0c490 100644 --- a/hints.py +++ b/hints.py @@ -26,7 +26,7 @@ class HintCollection: return [ hint for hint in self._hints if (type is None or type == hint.type) and - (onlyactive or hint.active) and + (hint.active or not onlyactive) and (package is None or package == hint.packages[0].package) and (version is None or version == hint.packages[0].version) and (removal is None or removal == hint.packages[0].is_removal) From 60b682a4c3478a86322b0ace67f516699310a7d2 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Fri, 11 Nov 2011 13:28:04 +0000 Subject: [PATCH 40/46] MigrationItem: support "$pkg/$ver/$arch" syntax This relies on the caller supplying a list of valid architectures, and assumes that none of the list entries are also valid version strings. Signed-off-by: Adam D. Barratt --- migrationitem.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/migrationitem.py b/migrationitem.py index de4d46b..6b68cd7 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -13,6 +13,16 @@ # GNU General Public License for more details. class MigrationItem: + _architectures = [] + + @classmethod + def set_architectures(cls, architectures = None): + cls._architectures = architectures or [] + + @classmethod + def get_architectures(cls): + return cls._architectures + def __init__(self, name = None, versionned = False): self._name = None self._uvname = None @@ -71,6 +81,10 @@ class MigrationItem: else: self._architecture = 'source' + if self._version in self.__class__.get_architectures(): + (self._architecture, self._version) = \ + (self._version, self._architecture) + if '_' in self._architecture: self._architecture, self._suite = \ self._architecture.split('_', 2) From 1107844e8b34f22ee7d9805ebd2bd09bfec716e0 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Fri, 11 Nov 2011 13:30:53 +0000 Subject: [PATCH 41/46] Pass the list of supported architectures to MigrationItem Signed-off-by: Adam D. Barratt --- britney.py | 1 + 1 file changed, 1 insertion(+) diff --git a/britney.py b/britney.py index 922962b..0883aee 100755 --- a/britney.py +++ b/britney.py @@ -243,6 +243,7 @@ class Britney: # parse the command line arguments self.__parse_arguments() + MigrationItem.set_architectures(self.options.architectures) # initialize the apt_pkg back-end apt_pkg.init() From 157b4ee947e861d658e0ba8db84e65f7ad55ad1c Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Mon, 14 Nov 2011 18:28:32 +0000 Subject: [PATCH 42/46] Syntax tweak Signed-off-by: Adam D. Barratt --- migrationitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrationitem.py b/migrationitem.py index 6b68cd7..6dace15 100644 --- a/migrationitem.py +++ b/migrationitem.py @@ -36,7 +36,7 @@ class MigrationItem: self._set_name(name) def __str__(self): - if self._versionned and not self.version is None: + if self._versionned and self.version is not None: return self.name else: return self.uvname From b76d9e189aa0f215ce645020268ad31cfea321a2 Mon Sep 17 00:00:00 2001 From: Julien Cristau Date: Mon, 14 Nov 2011 18:29:43 +0000 Subject: [PATCH 43/46] Ensure package list contains exactly one item in convenience methods Signed-off-by: Adam D. Barratt --- hints.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hints.py b/hints.py index 2f0c490..dfc4539 100644 --- a/hints.py +++ b/hints.py @@ -88,6 +88,7 @@ class Hint: @property def package(self): if self.packages: + assert len(self.packages) == 1, self.packages return self.packages[0].package else: return None @@ -95,6 +96,7 @@ class Hint: @property def version(self): if self.packages: + assert len(self.packages) == 1, self.packages return self.packages[0].version else: return None From f35342993c678fcc60c333e9ea42c25a61f61646 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Mon, 14 Nov 2011 19:13:16 +0000 Subject: [PATCH 44/46] Hint: implement equality based on comparing string representations This will work for "single package" hints, but should be extended to consider "easy foo/1 bar/1" and "easy bar/1 foo/1" to be equivalent. Signed-off-by: Adam D. Barratt --- hints.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hints.py b/hints.py index dfc4539..07a9ffa 100644 --- a/hints.py +++ b/hints.py @@ -65,6 +65,9 @@ class Hint: def __str__(self): return self._hint + def __eq__(self, other): + return str(self) == str(other) + @property def type(self): return self._type From 4f29a4334c19606a92da5fab076a7ed032fb9a6b Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Mon, 14 Nov 2011 19:15:25 +0000 Subject: [PATCH 45/46] Fix-up hint override logic Signed-off-by: Adam D. Barratt --- britney.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/britney.py b/britney.py index 0883aee..e4f7080 100755 --- a/britney.py +++ b/britney.py @@ -804,23 +804,27 @@ class Britney: z = {} for hint in hints[x]: package = hint.package - if z.has_key(package) and z[package] != hint.version: + if z.has_key(package) and z[package] != hint: if x in ['unblock', 'unblock-udeb']: - if apt_pkg.VersionCompare(z[package], hint.version) < 0: + if apt_pkg.VersionCompare(z[package].version, hint.version) < 0: # This hint is for a newer version, so discard the old one - self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], hint.version), type="W") + self.__log("Overriding %s[%s] = ('%s', '%s') with ('%s', '%s')" % + (x, package, z[package].version, z[package].user, hint.version, hint.user), type="W") for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: other.set_active(False) else: # This hint is for an older version, so ignore it in favour of the new one - self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, package, hint.version, z[package]), type="W") + self.__log("Ignoring %s[%s] = ('%s', '%s'), ('%s', '%s') is higher or equal" % + (x, package, hint.version, hint.user, z[package].version, z[package].user), type="W") hint.set_active(False) else: - self.__log("Overriding %s[%s] = %s with %s" % (x, package, z[package], hint.version), type="W") - for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: + self.__log("Overriding %s[%s] = ('%s', '%s', '%s') with ('%s, '%s, '%s')" % + (x, package, z[package].version, z[package].user, z[package].days, + hint.version, hint.user, hint.days), type="W") + for other in [y for y in hints[x] if y.package==package and y.version==z[package].version]: other.set_active(False) - z[package] = hint.version + z[package] = hint # Sanity check the hints hash if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0: From f6ba71d0e971b3820fd772bb32eb5837c2ca44b7 Mon Sep 17 00:00:00 2001 From: "Adam D. Barratt" Date: Mon, 14 Nov 2011 19:52:42 +0000 Subject: [PATCH 46/46] Simplify hint override checking Signed-off-by: Adam D. Barratt --- britney.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/britney.py b/britney.py index e4f7080..b0df2e5 100755 --- a/britney.py +++ b/britney.py @@ -804,27 +804,27 @@ class Britney: z = {} for hint in hints[x]: package = hint.package - if z.has_key(package) and z[package] != hint: + key = (hint, hint.user) + if z.has_key(package) and z[package] != key: + hint2 = z[package][0] if x in ['unblock', 'unblock-udeb']: - if apt_pkg.VersionCompare(z[package].version, hint.version) < 0: + if apt_pkg.VersionCompare(hint2.version, hint.version) < 0: # This hint is for a newer version, so discard the old one self.__log("Overriding %s[%s] = ('%s', '%s') with ('%s', '%s')" % - (x, package, z[package].version, z[package].user, hint.version, hint.user), type="W") - for other in [y for y in hints[x] if y.package==package and y.version==z[package]]: - other.set_active(False) + (x, package, hint2.version, hint2.user, hint.version, hint.user), type="W") + hint2.set_active(False) else: # This hint is for an older version, so ignore it in favour of the new one self.__log("Ignoring %s[%s] = ('%s', '%s'), ('%s', '%s') is higher or equal" % - (x, package, hint.version, hint.user, z[package].version, z[package].user), type="W") + (x, package, hint.version, hint.user, hint2.version, hint2.user), type="W") hint.set_active(False) else: - self.__log("Overriding %s[%s] = ('%s', '%s', '%s') with ('%s, '%s, '%s')" % - (x, package, z[package].version, z[package].user, z[package].days, + self.__log("Overriding %s[%s] = ('%s', '%s', '%s') with ('%s', '%s', '%s')" % + (x, package, hint2.version, hint2.user, hint2.days, hint.version, hint.user, hint.days), type="W") - for other in [y for y in hints[x] if y.package==package and y.version==z[package].version]: - other.set_active(False) + hint2.set_active(False) - z[package] = hint + z[package] = key # Sanity check the hints hash if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0: