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 |