Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH 2/7] binpkg-multi-instance 2 of 7
Date: Wed, 18 Feb 2015 03:06:37
Message-Id: 1424228745-7794-3-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 0/7] Add FEATURES=binpkg-multi-instance (bug 150031) by Zac Medico
1 Add multi-instance support to fakedbapi, which allows multiple instances
2 with the same cpv to be stored simultaneously, as long as they are
3 distinguishable using the new _pkg_str build_id, build_time, file_size,
4 and mtime attributes. This will be used to add multi-instance support to
5 the bindbapi class (which inherits from fakedbapi).
6 ---
7 pym/portage/dbapi/virtual.py | 113 +++++++++++++++++++++++++++++++++----------
8 1 file changed, 87 insertions(+), 26 deletions(-)
9
10 diff --git a/pym/portage/dbapi/virtual.py b/pym/portage/dbapi/virtual.py
11 index ba9745c..3b7d10e 100644
12 --- a/pym/portage/dbapi/virtual.py
13 +++ b/pym/portage/dbapi/virtual.py
14 @@ -11,12 +11,17 @@ class fakedbapi(dbapi):
15 """A fake dbapi that allows consumers to inject/remove packages to/from it
16 portage.settings is required to maintain the dbAPI.
17 """
18 - def __init__(self, settings=None, exclusive_slots=True):
19 + def __init__(self, settings=None, exclusive_slots=True,
20 + multi_instance=False):
21 """
22 @param exclusive_slots: When True, injecting a package with SLOT
23 metadata causes an existing package in the same slot to be
24 automatically removed (default is True).
25 @type exclusive_slots: Boolean
26 + @param multi_instance: When True, multiple instances with the
27 + same cpv may be stored simultaneously, as long as they are
28 + distinguishable (default is False).
29 + @type multi_instance: Boolean
30 """
31 self._exclusive_slots = exclusive_slots
32 self.cpvdict = {}
33 @@ -25,6 +30,56 @@ class fakedbapi(dbapi):
34 from portage import settings
35 self.settings = settings
36 self._match_cache = {}
37 + self._set_multi_instance(multi_instance)
38 +
39 + def _set_multi_instance(self, multi_instance):
40 + """
41 + Enable or disable multi_instance mode. This should before any
42 + packages are injected, so that all packages are indexed with
43 + the same implementation of self._instance_key.
44 + """
45 + if self.cpvdict:
46 + raise AssertionError("_set_multi_instance called after "
47 + "packages have already been added")
48 + self._multi_instance = multi_instance
49 + if multi_instance:
50 + self._instance_key = self._instance_key_multi_instance
51 + else:
52 + self._instance_key = self._instance_key_cpv
53 +
54 + def _instance_key_cpv(self, cpv, support_string=False):
55 + return cpv
56 +
57 + def _instance_key_multi_instance(self, cpv, support_string=False):
58 + try:
59 + return (cpv, cpv.build_id, cpv.file_size, cpv.build_time,
60 + cpv.mtime)
61 + except AttributeError:
62 + if not support_string:
63 + raise
64 +
65 + # Fallback for interfaces such as aux_get where API consumers
66 + # may pass in a plain string.
67 + latest = None
68 + for pkg in self.cp_list(cpv_getkey(cpv)):
69 + if pkg == cpv and (
70 + latest is None or
71 + latest.build_time < pkg.build_time):
72 + latest = pkg
73 +
74 + if latest is not None:
75 + return (latest, latest.build_id, latest.file_size,
76 + latest.build_time, latest.mtime)
77 +
78 + raise KeyError(cpv)
79 +
80 + def clear(self):
81 + """
82 + Remove all packages.
83 + """
84 + self._clear_cache()
85 + self.cpvdict.clear()
86 + self.cpdict.clear()
87
88 def _clear_cache(self):
89 if self._categories is not None:
90 @@ -43,7 +98,8 @@ class fakedbapi(dbapi):
91 return result[:]
92
93 def cpv_exists(self, mycpv, myrepo=None):
94 - return mycpv in self.cpvdict
95 + return self._instance_key(mycpv,
96 + support_string=True) in self.cpvdict
97
98 def cp_list(self, mycp, use_cache=1, myrepo=None):
99 # NOTE: Cache can be safely shared with the match cache, since the
100 @@ -63,7 +119,10 @@ class fakedbapi(dbapi):
101 return list(self.cpdict)
102
103 def cpv_all(self):
104 - return list(self.cpvdict)
105 + if self._multi_instance:
106 + return [x[0] for x in self.cpvdict]
107 + else:
108 + return list(self.cpvdict)
109
110 def cpv_inject(self, mycpv, metadata=None):
111 """Adds a cpv to the list of available packages. See the
112 @@ -99,13 +158,14 @@ class fakedbapi(dbapi):
113 except AttributeError:
114 pass
115
116 - self.cpvdict[mycpv] = metadata
117 + instance_key = self._instance_key(mycpv)
118 + self.cpvdict[instance_key] = metadata
119 if not self._exclusive_slots:
120 myslot = None
121 if myslot and mycp in self.cpdict:
122 # If necessary, remove another package in the same SLOT.
123 for cpv in self.cpdict[mycp]:
124 - if mycpv != cpv:
125 + if instance_key != self._instance_key(cpv):
126 try:
127 other_slot = cpv.slot
128 except AttributeError:
129 @@ -115,40 +175,41 @@ class fakedbapi(dbapi):
130 self.cpv_remove(cpv)
131 break
132
133 - cp_list = self.cpdict.get(mycp)
134 - if cp_list is None:
135 - cp_list = []
136 - self.cpdict[mycp] = cp_list
137 - try:
138 - cp_list.remove(mycpv)
139 - except ValueError:
140 - pass
141 + cp_list = self.cpdict.get(mycp, [])
142 + cp_list = [x for x in cp_list
143 + if self._instance_key(x) != instance_key]
144 cp_list.append(mycpv)
145 + self.cpdict[mycp] = cp_list
146
147 def cpv_remove(self,mycpv):
148 """Removes a cpv from the list of available packages."""
149 self._clear_cache()
150 mycp = cpv_getkey(mycpv)
151 - if mycpv in self.cpvdict:
152 - del self.cpvdict[mycpv]
153 - if mycp not in self.cpdict:
154 - return
155 - while mycpv in self.cpdict[mycp]:
156 - del self.cpdict[mycp][self.cpdict[mycp].index(mycpv)]
157 - if not len(self.cpdict[mycp]):
158 - del self.cpdict[mycp]
159 + instance_key = self._instance_key(mycpv)
160 + self.cpvdict.pop(instance_key, None)
161 + cp_list = self.cpdict.get(mycp)
162 + if cp_list is not None:
163 + cp_list = [x for x in cp_list
164 + if self._instance_key(x) != instance_key]
165 + if cp_list:
166 + self.cpdict[mycp] = cp_list
167 + else:
168 + del self.cpdict[mycp]
169
170 def aux_get(self, mycpv, wants, myrepo=None):
171 - if not self.cpv_exists(mycpv):
172 + metadata = self.cpvdict.get(
173 + self._instance_key(mycpv, support_string=True))
174 + if metadata is None:
175 raise KeyError(mycpv)
176 - metadata = self.cpvdict[mycpv]
177 - if not metadata:
178 - return ["" for x in wants]
179 return [metadata.get(x, "") for x in wants]
180
181 def aux_update(self, cpv, values):
182 self._clear_cache()
183 - self.cpvdict[cpv].update(values)
184 + metadata = self.cpvdict.get(
185 + self._instance_key(cpv, support_string=True))
186 + if metadata is None:
187 + raise KeyError(cpv)
188 + metadata.update(values)
189
190 class testdbapi(object):
191 """A dbapi instance with completely fake functions to get by hitting disk
192 --
193 2.0.5