Coverage for britney2/transaction.py: 96%
75 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-04-18 20:48 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-04-18 20:48 +0000
1class MigrationTransactionState(object):
3 def __init__(self, suite_info, all_binaries, parent=None):
4 self._suite_info = suite_info
5 self._all_binaries = all_binaries
6 self.parent_transaction = parent
7 self._is_rolled_back = False
8 self._is_committed = False
9 self._undo_items = []
10 self._pending_child = False
11 if self.parent_transaction:
12 # Transactions can only support one child transaction at a time
13 assert not self.parent_transaction._pending_child
14 self.parent_transaction._pending_child = True
16 def add_undo_item(self, undo, updated_binaries):
17 # We do not accept any changes to this transaction while it has a child transaction
18 # (the undo code does not handle that case correctly)
19 assert not self._pending_child
20 self._assert_open_transaction()
21 self._undo_items.append((undo, updated_binaries))
23 def _assert_open_transaction(self):
24 assert not self._is_rolled_back and not self._is_committed
25 p = self.parent_transaction
26 if p:
27 p._assert_open_transaction()
29 @property
30 def undo_items(self):
31 """Only needed by a _apply_item_to_target_suite for the "hint"-hint case"""
32 yield from self._undo_items
34 def commit(self):
35 """Commit the transaction
37 After this call, it is not possible to roll these changes
38 back (except if there is a parent transaction, which can
39 still be rolled back).
40 """
41 self._assert_open_transaction()
42 self._is_committed = True
43 if self.parent_transaction:
44 self.parent_transaction._pending_child = False
45 for undo_item in self._undo_items:
46 self.parent_transaction.add_undo_item(*undo_item)
48 def rollback(self):
49 """Rollback all recorded changes by this transaction
51 The parent transaction (if any) will remain unchanged
52 """
54 self._assert_open_transaction()
55 self._is_rolled_back = True
56 lundo = self._undo_items
57 lundo.reverse()
59 all_binary_packages = self._all_binaries
60 target_suite = self._suite_info.target_suite
61 sources_t = target_suite.sources
62 binaries_t = target_suite.binaries
63 provides_t = target_suite.provides_table
65 # Historically, we have done the undo process in "4 steps"
66 # with the rule that each step must be fully completed for
67 # each undo-item before starting on the next.
68 #
69 # see commit:ef71f0e33a7c3d8ef223ec9ad5e9843777e68133 and
70 # #624716 for the issues we had when we did not do this.
71 #
72 # Today, only STEP 2 and STEP 3 are known to potentially
73 # clash. If there is a point in merging the loops/steps,
74 # then it is now feasible.
76 # STEP 1
77 # undo all the changes for sources
78 for (undo, updated_binaries) in lundo:
79 for (k, v) in undo['sources'].items():
80 if v is None:
81 del sources_t[k]
82 else:
83 sources_t[k] = v
85 # STEP 2
86 # undo all new/updated binaries
87 # Note this must be completed fully before starting STEP 3
88 # as it potentially breaks STEP 3 if the two are interleaved.
89 for (_, updated_binaries) in lundo:
90 for pkg_id in updated_binaries:
91 pkg_name, _, pkg_arch = pkg_id
92 try:
93 del binaries_t[pkg_arch][pkg_name]
94 except KeyError:
95 continue
97 target_suite.remove_binary(pkg_id)
99 # STEP 3
100 # undo all other binary package changes (except virtual packages)
101 for (undo, updated_binaries) in lundo:
102 for p in undo['binaries']:
103 binary, arch = p
104 binaries_t_a = binaries_t[arch]
105 pkgdata = all_binary_packages[undo['binaries'][p]]
106 binaries_t_a[binary] = pkgdata
107 target_suite.add_binary(pkgdata.pkg_id)
109 # STEP 4
110 # undo all changes to virtual packages
111 for (undo, _) in lundo:
112 for p, value in undo['virtual'].items():
113 provided_pkg, arch = p
114 if value is None: 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true
115 del provides_t[arch][provided_pkg]
116 else:
117 provides_t[arch][provided_pkg] = undo['virtual'][p]
119 if self.parent_transaction:
120 self.parent_transaction._pending_child = False
122 @property
123 def is_rolled_back(self):
124 return self._is_rolled_back
126 @property
127 def is_committed(self):
128 return self._is_committed