1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
From ac3e8f392ad61d8a3d455776a3df05e08e8d993c Mon Sep 17 00:00:00 2001
From: Brian Harring <ferringb@gmail.com>
Date: Wed, 11 Apr 2012 12:17:55 -0700
Subject: [PATCH] Fix rollback support when reverting a replace affected by a
blocker.
Specifically, if the state is blocker !a:1 w/ a replace operation of
a-1:1 -> a-2:2, that replace is fine. Reverting the replace however
would fail w/ an assertion error due to !a:1 catching a-1:1 during
revert.
Thus track the state, and only through the error if the blocker state
for that package has somehow changed since we last looked (unlikely).
---
pkgcore/resolver/pigeonholes.py | 11 ++++++++---
pkgcore/resolver/state.py | 14 ++++++++++----
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/pkgcore/resolver/pigeonholes.py b/pkgcore/resolver/pigeonholes.py
index 112d53b..fc010e9 100644
--- a/pkgcore/resolver/pigeonholes.py
+++ b/pkgcore/resolver/pigeonholes.py
@@ -22,9 +22,10 @@ class PigeonHoledSlots(object):
:return: any conflicting objs (empty list if inserted successfully).
"""
- key = obj.key
- l = [x for x in self.limiters.get(key, ()) if x.match(obj)]
+ l = self.check_limiters(obj)
+
+ key = obj.key
dslot = obj.slot
l.extend(x for x in self.slot_dict.get(key, ()) if x.slot == dslot)
@@ -32,7 +33,6 @@ class PigeonHoledSlots(object):
self.slot_dict.setdefault(key, []).append(obj)
return l
-
def get_conflicting_slot(self, pkg):
for x in self.slot_dict.get(pkg.key, ()):
if pkg.slot == x.slot:
@@ -56,6 +56,11 @@ class PigeonHoledSlots(object):
self.limiters.setdefault(key, []).append(atom)
return self.find_atom_matches(atom, key=key)
+ def check_limiters(self, obj):
+ """return any limiters conflicting w/ the psased in obj"""
+ key = obj.key
+ return [x for x in self.limiters.get(key, ()) if x.match(obj)]
+
def remove_slotting(self, obj):
key = obj.key
# let the key error be thrown if they screwed up.
diff --git a/pkgcore/resolver/state.py b/pkgcore/resolver/state.py
index f4ad3c4..c14d9f9 100644
--- a/pkgcore/resolver/state.py
+++ b/pkgcore/resolver/state.py
@@ -198,24 +198,26 @@ class remove_op(base_op_state):
plan.vdb_filter.add(self.pkg)
def revert(self, plan):
- plan.state.fill_slotting(self.pkg, force=self.force)
+ plan.state.fill_slotting(self.pkg, force=True)
plan.pkg_choices[self.pkg] = self.choices
plan.vdb_filter.remove(self.pkg)
class replace_op(base_op_state):
- __slots__ = ("old_pkg", "old_choices")
+ __slots__ = ("old_pkg", "old_choices", "force_old")
desc = "replace"
def __init__(self, *args, **kwds):
base_op_state.__init__(self, *args, **kwds)
self.old_pkg, self.old_choices = None, None
+ self.force_old = False
def apply(self, plan):
revert_point = plan.current_state
old = plan.state.get_conflicting_slot(self.pkg)
# probably should just convert to an add...
+ force_old = bool(plan.state.check_limiters(old))
assert old is not None
plan.state.remove_slotting(old)
old_choices = plan.pkg_choices[old]
@@ -233,6 +235,7 @@ class replace_op(base_op_state):
# wipe olds blockers.
self.old_pkg = old
+ self.force_old = force_old
self.old_choices = old_choices
del plan.pkg_choices[old]
plan.pkg_choices[self.pkg] = self.choices
@@ -243,8 +246,11 @@ class replace_op(base_op_state):
# far simpler, since the apply op generates multiple ops on it's own.
# all we have to care about is swap.
plan.state.remove_slotting(self.pkg)
- l = plan.state.fill_slotting(self.old_pkg, force=self.force)
- assert not l, "reverting a replace op %r, got %r from slotting" % (self, l)
+ l = plan.state.fill_slotting(self.old_pkg, force=self.force_old)
+ if bool(l) != self.force_old:
+ raise AssertionError(
+ "Internal error detected, unable to revert %s; got %s, "
+ "force_old=%s " % (self, l, self.force_old))
del plan.pkg_choices[self.pkg]
plan.pkg_choices[self.old_pkg] = self.old_choices
plan.vdb_filter.remove(self.old_pkg)
--
1.7.8.5
|