Merge lp:~mwhudson/launchpad/in-memory-launchpad-server into lp:launchpad
- in-memory-launchpad-server
- Merge into devel
Proposed by
Michael Hudson-Doyle
Status: | Merged |
---|---|
Approved by: | Tim Penhey |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~mwhudson/launchpad/in-memory-launchpad-server |
Merge into: | lp:launchpad |
Diff against target: |
667 lines 14 files modified
lib/lp/code/configure.zcml (+3/-0) lib/lp/code/interfaces/branchlookup.py (+11/-0) lib/lp/code/model/branchlookup.py (+22/-8) lib/lp/code/model/tests/test_branchlookup.py (+43/-0) lib/lp/code/xmlrpc/codehosting.py (+1/-19) lib/lp/code/xmlrpc/tests/test_codehosting.py (+3/-22) lib/lp/codehosting/rewrite.py (+14/-19) lib/lp/codehosting/tests/test_branchdistro.py (+3/-3) lib/lp/codehosting/vfs/branchfs.py (+60/-9) lib/lp/codehosting/vfs/tests/test_branchfs.py (+36/-18) lib/lp/services/tests/test_utils.py (+32/-0) lib/lp/services/utils.py (+29/-0) lib/lp/testing/__init__.py (+4/-2) scripts/branch-distro.py (+2/-1) |
To merge this branch: | bzr merge lp:~mwhudson/launchpad/in-memory-launchpad-server |
Related bugs: | |
Related blueprints: |
Branching Ubuntu on release
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey (community) | Approve | ||
Review via email: mp+13613@code.launchpad.net |
Commit message
Add a way to access branches by querying the database directly rather than via an XML-RPC call, and use it in the branch-distro.py script.
Description of the change
To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote : | # |
Revision history for this message
Tim Penhey (thumper) wrote : | # |
I think this is actually pretty good.
Some of the tests are missing comments/
class name and method name makes it pretty clear what is being tested.
merge approved
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/code/configure.zcml' | |||
2 | --- lib/lp/code/configure.zcml 2009-10-15 05:45:57 +0000 | |||
3 | +++ lib/lp/code/configure.zcml 2009-10-20 21:56:15 +0000 | |||
4 | @@ -355,6 +355,9 @@ | |||
5 | 355 | provides="lp.code.interfaces.branchlookup.ILinkedBranchTraverser"> | 355 | provides="lp.code.interfaces.branchlookup.ILinkedBranchTraverser"> |
6 | 356 | <allow interface="lp.code.interfaces.branchlookup.ILinkedBranchTraverser"/> | 356 | <allow interface="lp.code.interfaces.branchlookup.ILinkedBranchTraverser"/> |
7 | 357 | </securedutility> | 357 | </securedutility> |
8 | 358 | <adapter factory="lp.code.model.branchlookup.ProductTraversable" /> | ||
9 | 359 | <adapter factory="lp.code.model.branchlookup.DistributionTraversable" /> | ||
10 | 360 | <adapter factory="lp.code.model.branchlookup.DistroSeriesTraversable" /> | ||
11 | 358 | <securedutility | 361 | <securedutility |
12 | 359 | class="lp.code.model.branchscanner.BranchScanner" | 362 | class="lp.code.model.branchscanner.BranchScanner" |
13 | 360 | provides="lp.code.interfaces.branchscanner.IBranchScanner"> | 363 | provides="lp.code.interfaces.branchscanner.IBranchScanner"> |
14 | 361 | 364 | ||
15 | === modified file 'lib/lp/code/interfaces/branchlookup.py' | |||
16 | --- lib/lp/code/interfaces/branchlookup.py 2009-07-19 23:17:34 +0000 | |||
17 | +++ lib/lp/code/interfaces/branchlookup.py 2009-10-20 21:56:15 +0000 | |||
18 | @@ -68,6 +68,17 @@ | |||
19 | 68 | Return None if no match was found. | 68 | Return None if no match was found. |
20 | 69 | """ | 69 | """ |
21 | 70 | 70 | ||
22 | 71 | def getIdAndTrailingPath(self, path, from_slave=False): | ||
23 | 72 | """Return id of and path within the branch identified by the `path`. | ||
24 | 73 | |||
25 | 74 | To explain by example, if the branch with id 5 has unique name | ||
26 | 75 | '~user/project/name', getIdAndTrailingPath('~user/project/name/foo') | ||
27 | 76 | will return (5, '/foo'). | ||
28 | 77 | |||
29 | 78 | :return: ``(branch_id, trailing_path)``, both will be ``None`` if no | ||
30 | 79 | branch is identified. | ||
31 | 80 | """ | ||
32 | 81 | |||
33 | 71 | def uriToUniqueName(uri): | 82 | def uriToUniqueName(uri): |
34 | 72 | """Return the unique name for the URI, if the URI is on codehosting. | 83 | """Return the unique name for the URI, if the URI is on codehosting. |
35 | 73 | 84 | ||
36 | 74 | 85 | ||
37 | === modified file 'lib/lp/code/model/branchlookup.py' | |||
38 | --- lib/lp/code/model/branchlookup.py 2009-07-19 23:17:34 +0000 | |||
39 | +++ lib/lp/code/model/branchlookup.py 2009-10-20 21:56:15 +0000 | |||
40 | @@ -8,8 +8,7 @@ | |||
41 | 8 | # then get the IBranchLookup utility. | 8 | # then get the IBranchLookup utility. |
42 | 9 | __all__ = [] | 9 | __all__ = [] |
43 | 10 | 10 | ||
46 | 11 | from zope.component import ( | 11 | from zope.component import adapts, getUtility, queryMultiAdapter |
45 | 12 | adapts, getSiteManager, getUtility, queryMultiAdapter) | ||
47 | 13 | from zope.interface import implements | 12 | from zope.interface import implements |
48 | 14 | 13 | ||
49 | 15 | from storm.expr import Join | 14 | from storm.expr import Join |
50 | @@ -37,7 +36,9 @@ | |||
51 | 37 | from lp.registry.interfaces.productseries import NoSuchProductSeries | 36 | from lp.registry.interfaces.productseries import NoSuchProductSeries |
52 | 38 | from lp.registry.interfaces.sourcepackagename import ( | 37 | from lp.registry.interfaces.sourcepackagename import ( |
53 | 39 | NoSuchSourcePackageName) | 38 | NoSuchSourcePackageName) |
54 | 39 | from lp.services.utils import iter_split | ||
55 | 40 | from canonical.launchpad.validators.name import valid_name | 40 | from canonical.launchpad.validators.name import valid_name |
56 | 41 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, ISlaveStore | ||
57 | 41 | from canonical.launchpad.webapp.authorization import check_permission | 42 | from canonical.launchpad.webapp.authorization import check_permission |
58 | 42 | from canonical.launchpad.webapp.interfaces import ( | 43 | from canonical.launchpad.webapp.interfaces import ( |
59 | 43 | IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR) | 44 | IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR) |
60 | @@ -160,12 +161,6 @@ | |||
61 | 160 | return sourcepackage.getSuiteSourcePackage(self.pocket) | 161 | return sourcepackage.getSuiteSourcePackage(self.pocket) |
62 | 161 | 162 | ||
63 | 162 | 163 | ||
64 | 163 | sm = getSiteManager() | ||
65 | 164 | sm.registerAdapter(ProductTraversable) | ||
66 | 165 | sm.registerAdapter(DistributionTraversable) | ||
67 | 166 | sm.registerAdapter(DistroSeriesTraversable) | ||
68 | 167 | |||
69 | 168 | |||
70 | 169 | class LinkedBranchTraverser: | 164 | class LinkedBranchTraverser: |
71 | 170 | """Utility for traversing to objects that can have linked branches.""" | 165 | """Utility for traversing to objects that can have linked branches.""" |
72 | 171 | 166 | ||
73 | @@ -259,6 +254,25 @@ | |||
74 | 259 | return None | 254 | return None |
75 | 260 | return self._getBranchInNamespace(namespace_data, branch_name) | 255 | return self._getBranchInNamespace(namespace_data, branch_name) |
76 | 261 | 256 | ||
77 | 257 | def getIdAndTrailingPath(self, path, from_slave=False): | ||
78 | 258 | """See `IBranchLookup`. """ | ||
79 | 259 | if from_slave: | ||
80 | 260 | store = ISlaveStore(Branch) | ||
81 | 261 | else: | ||
82 | 262 | store = IMasterStore(Branch) | ||
83 | 263 | prefixes = [] | ||
84 | 264 | for first, second in iter_split(path[1:], '/'): | ||
85 | 265 | prefixes.append(first) | ||
86 | 266 | result = store.find( | ||
87 | 267 | (Branch.id, Branch.unique_name), | ||
88 | 268 | Branch.unique_name.is_in(prefixes), Branch.private == False).one() | ||
89 | 269 | if result is None: | ||
90 | 270 | return None, None | ||
91 | 271 | else: | ||
92 | 272 | branch_id, unique_name = result | ||
93 | 273 | trailing = path[len(unique_name) + 1:] | ||
94 | 274 | return branch_id, trailing | ||
95 | 275 | |||
96 | 262 | def _getBranchInNamespace(self, namespace_data, branch_name): | 276 | def _getBranchInNamespace(self, namespace_data, branch_name): |
97 | 263 | if namespace_data['product'] == '+junk': | 277 | if namespace_data['product'] == '+junk': |
98 | 264 | return self._getPersonalBranch( | 278 | return self._getPersonalBranch( |
99 | 265 | 279 | ||
100 | === modified file 'lib/lp/code/model/tests/test_branchlookup.py' | |||
101 | --- lib/lp/code/model/tests/test_branchlookup.py 2009-08-28 06:39:38 +0000 | |||
102 | +++ lib/lp/code/model/tests/test_branchlookup.py 2009-10-20 21:56:15 +0000 | |||
103 | @@ -63,6 +63,49 @@ | |||
104 | 63 | self.assertEqual(branch, found_branch) | 63 | self.assertEqual(branch, found_branch) |
105 | 64 | 64 | ||
106 | 65 | 65 | ||
107 | 66 | class TestGetIdAndTrailingPath(TestCaseWithFactory): | ||
108 | 67 | """Tests for `IBranchLookup.getIdAndTrailingPath`.""" | ||
109 | 68 | |||
110 | 69 | layer = DatabaseFunctionalLayer | ||
111 | 70 | |||
112 | 71 | def setUp(self): | ||
113 | 72 | TestCaseWithFactory.setUp(self) | ||
114 | 73 | self.branch_set = getUtility(IBranchLookup) | ||
115 | 74 | |||
116 | 75 | def test_not_found(self): | ||
117 | 76 | unused_name = self.factory.getUniqueString() | ||
118 | 77 | result = self.branch_set.getIdAndTrailingPath('/' + unused_name) | ||
119 | 78 | self.assertEqual((None, None), result) | ||
120 | 79 | |||
121 | 80 | def test_junk(self): | ||
122 | 81 | branch = self.factory.makePersonalBranch() | ||
123 | 82 | result = self.branch_set.getIdAndTrailingPath('/' + branch.unique_name) | ||
124 | 83 | self.assertEqual((branch.id, ''), result) | ||
125 | 84 | |||
126 | 85 | def test_product(self): | ||
127 | 86 | branch = self.factory.makeProductBranch() | ||
128 | 87 | result = self.branch_set.getIdAndTrailingPath('/' + branch.unique_name) | ||
129 | 88 | self.assertEqual((branch.id, ''), result) | ||
130 | 89 | |||
131 | 90 | def test_source_package(self): | ||
132 | 91 | branch = self.factory.makePackageBranch() | ||
133 | 92 | result = self.branch_set.getIdAndTrailingPath('/' + branch.unique_name) | ||
134 | 93 | self.assertEqual((branch.id, ''), result) | ||
135 | 94 | |||
136 | 95 | def test_trailing_slash(self): | ||
137 | 96 | branch = self.factory.makeAnyBranch() | ||
138 | 97 | result = self.branch_set.getIdAndTrailingPath( | ||
139 | 98 | '/' + branch.unique_name + '/') | ||
140 | 99 | self.assertEqual((branch.id, '/'), result) | ||
141 | 100 | |||
142 | 101 | def test_trailing_path(self): | ||
143 | 102 | branch = self.factory.makeAnyBranch() | ||
144 | 103 | path = self.factory.getUniqueString() | ||
145 | 104 | result = self.branch_set.getIdAndTrailingPath( | ||
146 | 105 | '/' + branch.unique_name + '/' + path) | ||
147 | 106 | self.assertEqual((branch.id, '/' + path), result) | ||
148 | 107 | |||
149 | 108 | |||
150 | 66 | class TestGetByPath(TestCaseWithFactory): | 109 | class TestGetByPath(TestCaseWithFactory): |
151 | 67 | """Test `IBranchLookup.getByLPPath`.""" | 110 | """Test `IBranchLookup.getByLPPath`.""" |
152 | 68 | 111 | ||
153 | 69 | 112 | ||
154 | === modified file 'lib/lp/code/xmlrpc/codehosting.py' | |||
155 | --- lib/lp/code/xmlrpc/codehosting.py 2009-07-23 02:07:29 +0000 | |||
156 | +++ lib/lp/code/xmlrpc/codehosting.py 2009-10-20 21:56:15 +0000 | |||
157 | @@ -8,7 +8,6 @@ | |||
158 | 8 | 'BranchFileSystem', | 8 | 'BranchFileSystem', |
159 | 9 | 'BranchPuller', | 9 | 'BranchPuller', |
160 | 10 | 'datetime_from_tuple', | 10 | 'datetime_from_tuple', |
161 | 11 | 'iter_split', | ||
162 | 12 | ] | 11 | ] |
163 | 13 | 12 | ||
164 | 14 | 13 | ||
165 | @@ -37,6 +36,7 @@ | |||
166 | 37 | from lp.registry.interfaces.person import IPersonSet, NoSuchPerson | 36 | from lp.registry.interfaces.person import IPersonSet, NoSuchPerson |
167 | 38 | from lp.registry.interfaces.product import NoSuchProduct | 37 | from lp.registry.interfaces.product import NoSuchProduct |
168 | 39 | from lp.services.scripts.interfaces.scriptactivity import IScriptActivitySet | 38 | from lp.services.scripts.interfaces.scriptactivity import IScriptActivitySet |
169 | 39 | from lp.services.utils import iter_split | ||
170 | 40 | from canonical.launchpad.validators import LaunchpadValidationError | 40 | from canonical.launchpad.validators import LaunchpadValidationError |
171 | 41 | from canonical.launchpad.webapp import LaunchpadXMLRPCView | 41 | from canonical.launchpad.webapp import LaunchpadXMLRPCView |
172 | 42 | from canonical.launchpad.webapp.authorization import check_permission | 42 | from canonical.launchpad.webapp.authorization import check_permission |
173 | @@ -337,21 +337,3 @@ | |||
174 | 337 | raise faults.PathTranslationError(path) | 337 | raise faults.PathTranslationError(path) |
175 | 338 | return run_with_login(requester_id, translate_path) | 338 | return run_with_login(requester_id, translate_path) |
176 | 339 | 339 | ||
177 | 340 | |||
178 | 341 | def iter_split(string, splitter): | ||
179 | 342 | """Iterate over ways to split 'string' in two with 'splitter'. | ||
180 | 343 | |||
181 | 344 | If 'string' is empty, then yield nothing. Otherwise, yield tuples like | ||
182 | 345 | ('a/b/c', ''), ('a/b', 'c'), ('a', 'b/c') for a string 'a/b/c' and a | ||
183 | 346 | splitter '/'. | ||
184 | 347 | |||
185 | 348 | The tuples are yielded such that the first tuple has everything in the | ||
186 | 349 | first tuple. With each iteration, the first element gets smaller and the | ||
187 | 350 | second gets larger. It stops iterating just before it would have to yield | ||
188 | 351 | ('', 'a/b/c'). | ||
189 | 352 | """ | ||
190 | 353 | if string == '': | ||
191 | 354 | return | ||
192 | 355 | tokens = string.split(splitter) | ||
193 | 356 | for i in reversed(range(1, len(tokens) + 1)): | ||
194 | 357 | yield splitter.join(tokens[:i]), splitter.join(tokens[i:]) | ||
195 | 358 | 340 | ||
196 | === modified file 'lib/lp/code/xmlrpc/tests/test_codehosting.py' | |||
197 | --- lib/lp/code/xmlrpc/tests/test_codehosting.py 2009-07-17 00:26:05 +0000 | |||
198 | +++ lib/lp/code/xmlrpc/tests/test_codehosting.py 2009-10-20 21:56:15 +0000 | |||
199 | @@ -23,7 +23,7 @@ | |||
200 | 23 | from lp.code.interfaces.codehosting import ( | 23 | from lp.code.interfaces.codehosting import ( |
201 | 24 | BRANCH_TRANSPORT, CONTROL_TRANSPORT) | 24 | BRANCH_TRANSPORT, CONTROL_TRANSPORT) |
202 | 25 | from canonical.launchpad.interfaces.launchpad import ILaunchBag | 25 | from canonical.launchpad.interfaces.launchpad import ILaunchBag |
204 | 26 | from lp.testing import TestCase, TestCaseWithFactory | 26 | from lp.testing import TestCaseWithFactory |
205 | 27 | from lp.testing.factory import LaunchpadObjectFactory | 27 | from lp.testing.factory import LaunchpadObjectFactory |
206 | 28 | from canonical.launchpad.webapp.interfaces import NotFoundError | 28 | from canonical.launchpad.webapp.interfaces import NotFoundError |
207 | 29 | from canonical.launchpad.xmlrpc import faults | 29 | from canonical.launchpad.xmlrpc import faults |
208 | @@ -36,7 +36,7 @@ | |||
209 | 36 | from lp.code.model.tests.test_branchpuller import AcquireBranchToPullTests | 36 | from lp.code.model.tests.test_branchpuller import AcquireBranchToPullTests |
210 | 37 | from lp.code.xmlrpc.codehosting import ( | 37 | from lp.code.xmlrpc.codehosting import ( |
211 | 38 | BranchFileSystem, BranchPuller, LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES, | 38 | BranchFileSystem, BranchPuller, LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES, |
213 | 39 | iter_split, run_with_login) | 39 | run_with_login) |
214 | 40 | 40 | ||
215 | 41 | 41 | ||
216 | 42 | UTC = pytz.timezone('UTC') | 42 | UTC = pytz.timezone('UTC') |
217 | @@ -1109,23 +1109,6 @@ | |||
218 | 1109 | trailing_path='.bzr') | 1109 | trailing_path='.bzr') |
219 | 1110 | 1110 | ||
220 | 1111 | 1111 | ||
221 | 1112 | class TestIterateSplit(TestCase): | ||
222 | 1113 | """Tests for iter_split.""" | ||
223 | 1114 | |||
224 | 1115 | def test_iter_split(self): | ||
225 | 1116 | # iter_split loops over each way of splitting a string in two using | ||
226 | 1117 | # the given splitter. | ||
227 | 1118 | self.assertEqual([('one', '')], list(iter_split('one', '/'))) | ||
228 | 1119 | self.assertEqual([], list(iter_split('', '/'))) | ||
229 | 1120 | self.assertEqual( | ||
230 | 1121 | [('one/two', ''), ('one', 'two')], | ||
231 | 1122 | list(iter_split('one/two', '/'))) | ||
232 | 1123 | self.assertEqual( | ||
233 | 1124 | [('one/two/three', ''), ('one/two', 'three'), | ||
234 | 1125 | ('one', 'two/three')], | ||
235 | 1126 | list(iter_split('one/two/three', '/'))) | ||
236 | 1127 | |||
237 | 1128 | |||
238 | 1129 | class LaunchpadDatabaseFrontend: | 1112 | class LaunchpadDatabaseFrontend: |
239 | 1130 | """A 'frontend' to Launchpad's branch services. | 1113 | """A 'frontend' to Launchpad's branch services. |
240 | 1131 | 1114 | ||
241 | @@ -1182,7 +1165,5 @@ | |||
242 | 1182 | 'layer': FunctionalLayer}), | 1165 | 'layer': FunctionalLayer}), |
243 | 1183 | ] | 1166 | ] |
244 | 1184 | multiply_tests(puller_tests, scenarios, suite) | 1167 | multiply_tests(puller_tests, scenarios, suite) |
248 | 1185 | suite.addTests( | 1168 | suite.addTests(loader.loadTestsFromTestCase(TestRunWithLogin)) |
246 | 1186 | map(loader.loadTestsFromTestCase, | ||
247 | 1187 | [TestRunWithLogin, TestIterateSplit])) | ||
249 | 1188 | return suite | 1169 | return suite |
250 | 1189 | 1170 | ||
251 | === modified file 'lib/lp/codehosting/rewrite.py' | |||
252 | --- lib/lp/codehosting/rewrite.py 2009-07-23 23:42:47 +0000 | |||
253 | +++ lib/lp/codehosting/rewrite.py 2009-10-20 21:56:15 +0000 | |||
254 | @@ -8,11 +8,14 @@ | |||
255 | 8 | 8 | ||
256 | 9 | from bzrlib import urlutils | 9 | from bzrlib import urlutils |
257 | 10 | 10 | ||
260 | 11 | from canonical.launchpad.interfaces import ISlaveStore | 11 | from zope.component import getUtility |
261 | 12 | from lp.code.model.branch import Branch | 12 | |
262 | 13 | from canonical.config import config | ||
263 | 14 | |||
264 | 15 | from lp.code.interfaces.branchlookup import IBranchLookup | ||
265 | 13 | from lp.codehosting.vfs import branch_id_to_path | 16 | from lp.codehosting.vfs import branch_id_to_path |
266 | 17 | from lp.services.utils import iter_split | ||
267 | 14 | 18 | ||
268 | 15 | from canonical.config import config | ||
269 | 16 | 19 | ||
270 | 17 | __all__ = ['BranchRewriter'] | 20 | __all__ = ['BranchRewriter'] |
271 | 18 | 21 | ||
272 | @@ -31,7 +34,6 @@ | |||
273 | 31 | else: | 34 | else: |
274 | 32 | self._now = _now | 35 | self._now = _now |
275 | 33 | self.logger = logger | 36 | self.logger = logger |
276 | 34 | self.store = ISlaveStore(Branch) | ||
277 | 35 | self._cache = {} | 37 | self._cache = {} |
278 | 36 | 38 | ||
279 | 37 | def _codebrowse_url(self, path): | 39 | def _codebrowse_url(self, path): |
280 | @@ -45,26 +47,19 @@ | |||
281 | 45 | In addition this method returns whether the answer can from the cache | 47 | In addition this method returns whether the answer can from the cache |
282 | 46 | or from the database. | 48 | or from the database. |
283 | 47 | """ | 49 | """ |
290 | 48 | parts = location[1:].split('/') | 50 | for first, second in iter_split(location[1:], '/'): |
291 | 49 | prefixes = [] | 51 | if first in self._cache: |
292 | 50 | for i in range(1, len(parts) + 1): | 52 | branch_id, inserted_time = self._cache[first] |
287 | 51 | prefix = '/'.join(parts[:i]) | ||
288 | 52 | if prefix in self._cache: | ||
289 | 53 | branch_id, inserted_time = self._cache[prefix] | ||
293 | 54 | if (self._now() < inserted_time + | 53 | if (self._now() < inserted_time + |
294 | 55 | config.codehosting.branch_rewrite_cache_lifetime): | 54 | config.codehosting.branch_rewrite_cache_lifetime): |
302 | 56 | trailing = location[len(prefix) + 1:] | 55 | return branch_id, second, "HIT" |
303 | 57 | return branch_id, trailing, "HIT" | 56 | branch_id, trailing = getUtility(IBranchLookup).getIdAndTrailingPath( |
304 | 58 | prefixes.append(prefix) | 57 | location, from_slave=True) |
305 | 59 | result = self.store.find( | 58 | if branch_id is None: |
299 | 60 | (Branch.id, Branch.unique_name), | ||
300 | 61 | Branch.unique_name.is_in(prefixes), Branch.private == False).one() | ||
301 | 62 | if result is None: | ||
306 | 63 | return None, None, "MISS" | 59 | return None, None, "MISS" |
307 | 64 | else: | 60 | else: |
309 | 65 | branch_id, unique_name = result | 61 | unique_name = location[1:-len(trailing)] |
310 | 66 | self._cache[unique_name] = (branch_id, self._now()) | 62 | self._cache[unique_name] = (branch_id, self._now()) |
311 | 67 | trailing = location[len(unique_name) + 1:] | ||
312 | 68 | return branch_id, trailing, "MISS" | 63 | return branch_id, trailing, "MISS" |
313 | 69 | 64 | ||
314 | 70 | def rewriteLine(self, resource_location): | 65 | def rewriteLine(self, resource_location): |
315 | 71 | 66 | ||
316 | === modified file 'lib/lp/codehosting/tests/test_branchdistro.py' | |||
317 | --- lib/lp/codehosting/tests/test_branchdistro.py 2009-10-14 00:18:23 +0000 | |||
318 | +++ lib/lp/codehosting/tests/test_branchdistro.py 2009-10-20 21:56:15 +0000 | |||
319 | @@ -25,7 +25,7 @@ | |||
320 | 25 | import transaction | 25 | import transaction |
321 | 26 | 26 | ||
322 | 27 | from canonical.config import config | 27 | from canonical.config import config |
324 | 28 | from canonical.testing.layers import ZopelessAppServerLayer | 28 | from canonical.testing.layers import LaunchpadZopelessLayer |
325 | 29 | from canonical.launchpad.scripts.logger import FakeLogger, QuietFakeLogger | 29 | from canonical.launchpad.scripts.logger import FakeLogger, QuietFakeLogger |
326 | 30 | 30 | ||
327 | 31 | from lp.codehosting.branchdistro import DistroBrancher, switch_branches | 31 | from lp.codehosting.branchdistro import DistroBrancher, switch_branches |
328 | @@ -104,11 +104,11 @@ | |||
329 | 104 | class TestDistroBrancher(TestCaseWithFactory): | 104 | class TestDistroBrancher(TestCaseWithFactory): |
330 | 105 | """Tests for `DistroBrancher`.""" | 105 | """Tests for `DistroBrancher`.""" |
331 | 106 | 106 | ||
333 | 107 | layer = ZopelessAppServerLayer | 107 | layer = LaunchpadZopelessLayer |
334 | 108 | 108 | ||
335 | 109 | def setUp(self): | 109 | def setUp(self): |
336 | 110 | TestCaseWithFactory.setUp(self) | 110 | TestCaseWithFactory.setUp(self) |
338 | 111 | self.useBzrBranches(real_server=True) | 111 | self.useBzrBranches(real_server=True, direct_database=True) |
339 | 112 | 112 | ||
340 | 113 | def makeOfficialPackageBranch(self, distroseries=None): | 113 | def makeOfficialPackageBranch(self, distroseries=None): |
341 | 114 | """Make an official package branch with an underlying bzr branch.""" | 114 | """Make an official package branch with an underlying bzr branch.""" |
342 | 115 | 115 | ||
343 | === modified file 'lib/lp/codehosting/vfs/branchfs.py' | |||
344 | --- lib/lp/codehosting/vfs/branchfs.py 2009-07-17 18:46:25 +0000 | |||
345 | +++ lib/lp/codehosting/vfs/branchfs.py 2009-10-20 21:56:15 +0000 | |||
346 | @@ -53,6 +53,7 @@ | |||
347 | 53 | 'BadUrlSsh', | 53 | 'BadUrlSsh', |
348 | 54 | 'branch_id_to_path', | 54 | 'branch_id_to_path', |
349 | 55 | 'BranchPolicy', | 55 | 'BranchPolicy', |
350 | 56 | 'DirectDatabaseLaunchpadServer', | ||
351 | 56 | 'get_lp_server', | 57 | 'get_lp_server', |
352 | 57 | 'get_multi_server', | 58 | 'get_multi_server', |
353 | 58 | 'get_puller_server', | 59 | 'get_puller_server', |
354 | @@ -77,6 +78,7 @@ | |||
355 | 77 | from twisted.internet import defer | 78 | from twisted.internet import defer |
356 | 78 | from twisted.python import failure | 79 | from twisted.python import failure |
357 | 79 | 80 | ||
358 | 81 | from zope.component import getUtility | ||
359 | 80 | from zope.interface import implements, Interface | 82 | from zope.interface import implements, Interface |
360 | 81 | 83 | ||
361 | 82 | from lp.codehosting.vfs.branchfsclient import ( | 84 | from lp.codehosting.vfs.branchfsclient import ( |
362 | @@ -87,6 +89,7 @@ | |||
363 | 87 | get_chrooted_transport, get_readonly_transport, TranslationError) | 89 | get_chrooted_transport, get_readonly_transport, TranslationError) |
364 | 88 | from canonical.config import config | 90 | from canonical.config import config |
365 | 89 | from lp.code.enums import BranchType | 91 | from lp.code.enums import BranchType |
366 | 92 | from lp.code.interfaces.branchlookup import IBranchLookup | ||
367 | 90 | from lp.code.interfaces.codehosting import ( | 93 | from lp.code.interfaces.codehosting import ( |
368 | 91 | BRANCH_TRANSPORT, CONTROL_TRANSPORT, LAUNCHPAD_SERVICES) | 94 | BRANCH_TRANSPORT, CONTROL_TRANSPORT, LAUNCHPAD_SERVICES) |
369 | 92 | from canonical.launchpad.xmlrpc import faults | 95 | from canonical.launchpad.xmlrpc import faults |
370 | @@ -180,20 +183,25 @@ | |||
371 | 180 | return get_multi_server(write_mirrored=True) | 183 | return get_multi_server(write_mirrored=True) |
372 | 181 | 184 | ||
373 | 182 | 185 | ||
375 | 183 | def get_multi_server(write_hosted=False, write_mirrored=False): | 186 | def get_multi_server(write_hosted=False, write_mirrored=False, |
376 | 187 | direct_database=False): | ||
377 | 184 | """Get a server with access to both mirrored and hosted areas. | 188 | """Get a server with access to both mirrored and hosted areas. |
378 | 185 | 189 | ||
381 | 186 | The server wraps up two `LaunchpadInternalServer`s. One of them points to | 190 | The server wraps up two `LaunchpadInternalServer`s or |
382 | 187 | the hosted branch area, the other points to the mirrored area. | 191 | `DirectDatabaseLaunchpadServer`s. One server points to the hosted branch |
383 | 192 | area and the other points to the mirrored area. | ||
384 | 188 | 193 | ||
385 | 189 | Write permision defaults to False, but can be overridden. | 194 | Write permision defaults to False, but can be overridden. |
386 | 195 | |||
387 | 190 | :param write_hosted: if True, lp-hosted URLs are writeable. Otherwise, | 196 | :param write_hosted: if True, lp-hosted URLs are writeable. Otherwise, |
388 | 191 | they are read-only. | 197 | they are read-only. |
389 | 192 | :param write_mirrored: if True, lp-mirrored URLs are writeable. | 198 | :param write_mirrored: if True, lp-mirrored URLs are writeable. |
390 | 193 | Otherwise, they are read-only. | 199 | Otherwise, they are read-only. |
391 | 200 | |||
392 | 201 | :param direct_database: if True, use a server implementation that talks | ||
393 | 202 | directly to the database. If False, the default, use a server | ||
394 | 203 | implementation that talks to the internal XML-RPC server. | ||
395 | 194 | """ | 204 | """ |
396 | 195 | proxy = xmlrpclib.ServerProxy(config.codehosting.branchfs_endpoint) | ||
397 | 196 | branchfs_endpoint = BlockingProxy(proxy) | ||
398 | 197 | hosted_transport = get_chrooted_transport( | 205 | hosted_transport = get_chrooted_transport( |
399 | 198 | config.codehosting.hosted_branches_root, mkdir=True) | 206 | config.codehosting.hosted_branches_root, mkdir=True) |
400 | 199 | if not write_hosted: | 207 | if not write_hosted: |
401 | @@ -202,10 +210,16 @@ | |||
402 | 202 | config.codehosting.mirrored_branches_root, mkdir=True) | 210 | config.codehosting.mirrored_branches_root, mkdir=True) |
403 | 203 | if not write_mirrored: | 211 | if not write_mirrored: |
404 | 204 | mirrored_transport = get_readonly_transport(mirrored_transport) | 212 | mirrored_transport = get_readonly_transport(mirrored_transport) |
409 | 205 | hosted_server = LaunchpadInternalServer( | 213 | if direct_database: |
410 | 206 | 'lp-hosted:///', branchfs_endpoint, hosted_transport) | 214 | make_server = DirectDatabaseLaunchpadServer |
411 | 207 | mirrored_server = LaunchpadInternalServer( | 215 | else: |
412 | 208 | 'lp-mirrored:///', branchfs_endpoint, mirrored_transport) | 216 | proxy = xmlrpclib.ServerProxy(config.codehosting.branchfs_endpoint) |
413 | 217 | branchfs_endpoint = BlockingProxy(proxy) | ||
414 | 218 | def make_server(scheme, transport): | ||
415 | 219 | return LaunchpadInternalServer( | ||
416 | 220 | scheme, branchfs_endpoint, transport) | ||
417 | 221 | hosted_server = make_server('lp-hosted:///', hosted_transport) | ||
418 | 222 | mirrored_server = make_server('lp-mirrored:///', mirrored_transport) | ||
419 | 209 | return _MultiServer(hosted_server, mirrored_server) | 223 | return _MultiServer(hosted_server, mirrored_server) |
420 | 210 | 224 | ||
421 | 211 | 225 | ||
422 | @@ -427,6 +441,43 @@ | |||
423 | 427 | self.tearDown() | 441 | self.tearDown() |
424 | 428 | 442 | ||
425 | 429 | 443 | ||
426 | 444 | class DirectDatabaseLaunchpadServer(AsyncVirtualServer): | ||
427 | 445 | def __init__(self, scheme, branch_transport): | ||
428 | 446 | AsyncVirtualServer.__init__(self, scheme) | ||
429 | 447 | self._transport_dispatch = BranchTransportDispatch(branch_transport) | ||
430 | 448 | |||
431 | 449 | def setUp(self): | ||
432 | 450 | super(DirectDatabaseLaunchpadServer, self).setUp() | ||
433 | 451 | try: | ||
434 | 452 | self._transport_dispatch.base_transport.ensure_base() | ||
435 | 453 | except TransportNotPossible: | ||
436 | 454 | pass | ||
437 | 455 | |||
438 | 456 | def destroy(self): | ||
439 | 457 | """Delete the on-disk branches and tear down.""" | ||
440 | 458 | self._transport_dispatch.base_transport.delete_tree('.') | ||
441 | 459 | self.tearDown() | ||
442 | 460 | |||
443 | 461 | def translateVirtualPath(self, virtual_url_fragment): | ||
444 | 462 | """See `AsyncVirtualServer.translateVirtualPath`. | ||
445 | 463 | |||
446 | 464 | This implementation connects to the database directly. | ||
447 | 465 | """ | ||
448 | 466 | deferred = defer.succeed( | ||
449 | 467 | getUtility(IBranchLookup).getIdAndTrailingPath( | ||
450 | 468 | virtual_url_fragment)) | ||
451 | 469 | |||
452 | 470 | def process_result((branch_id, trailing)): | ||
453 | 471 | if branch_id is None: | ||
454 | 472 | raise NoSuchFile(virtual_url_fragment) | ||
455 | 473 | else: | ||
456 | 474 | return self._transport_dispatch.makeTransport( | ||
457 | 475 | (BRANCH_TRANSPORT, dict(id=branch_id), trailing[1:])) | ||
458 | 476 | |||
459 | 477 | deferred.addCallback(process_result) | ||
460 | 478 | return deferred | ||
461 | 479 | |||
462 | 480 | |||
463 | 430 | class AsyncLaunchpadTransport(AsyncVirtualTransport): | 481 | class AsyncLaunchpadTransport(AsyncVirtualTransport): |
464 | 431 | """Virtual transport to implement the Launchpad VFS for branches. | 482 | """Virtual transport to implement the Launchpad VFS for branches. |
465 | 432 | 483 | ||
466 | 433 | 484 | ||
467 | === modified file 'lib/lp/codehosting/vfs/tests/test_branchfs.py' | |||
468 | --- lib/lp/codehosting/vfs/tests/test_branchfs.py 2009-06-25 04:06:00 +0000 | |||
469 | +++ lib/lp/codehosting/vfs/tests/test_branchfs.py 2009-10-20 21:56:15 +0000 | |||
470 | @@ -24,9 +24,9 @@ | |||
471 | 24 | from twisted.trial.unittest import TestCase as TrialTestCase | 24 | from twisted.trial.unittest import TestCase as TrialTestCase |
472 | 25 | 25 | ||
473 | 26 | from lp.codehosting.vfs.branchfs import ( | 26 | from lp.codehosting.vfs.branchfs import ( |
477 | 27 | AsyncLaunchpadTransport, branch_id_to_path, LaunchpadInternalServer, | 27 | AsyncLaunchpadTransport, BranchTransportDispatch, |
478 | 28 | LaunchpadServer, BranchTransportDispatch, TransportDispatch, | 28 | DirectDatabaseLaunchpadServer, LaunchpadInternalServer, LaunchpadServer, |
479 | 29 | UnknownTransportType) | 29 | TransportDispatch, UnknownTransportType, branch_id_to_path) |
480 | 30 | from lp.codehosting.bzrutils import ensure_base | 30 | from lp.codehosting.bzrutils import ensure_base |
481 | 31 | from lp.codehosting.inmemory import InMemoryFrontend, XMLRPCWrapper | 31 | from lp.codehosting.inmemory import InMemoryFrontend, XMLRPCWrapper |
482 | 32 | from lp.codehosting.sftp import FatLocalTransport | 32 | from lp.codehosting.sftp import FatLocalTransport |
483 | @@ -34,8 +34,8 @@ | |||
484 | 34 | from lp.code.enums import BranchType | 34 | from lp.code.enums import BranchType |
485 | 35 | from lp.code.interfaces.codehosting import ( | 35 | from lp.code.interfaces.codehosting import ( |
486 | 36 | BRANCH_TRANSPORT, CONTROL_TRANSPORT) | 36 | BRANCH_TRANSPORT, CONTROL_TRANSPORT) |
489 | 37 | from lp.testing import TestCase | 37 | from lp.testing import TestCase, TestCaseWithFactory |
490 | 38 | from canonical.testing import TwistedLayer | 38 | from canonical.testing import TwistedLayer, ZopelessDatabaseLayer |
491 | 39 | 39 | ||
492 | 40 | 40 | ||
493 | 41 | def branch_to_path(branch, add_slash=True): | 41 | def branch_to_path(branch, add_slash=True): |
494 | @@ -293,18 +293,8 @@ | |||
495 | 293 | self.assertEqual('lp-%d:///' % id(self.server), self.server.get_url()) | 293 | self.assertEqual('lp-%d:///' % id(self.server), self.server.get_url()) |
496 | 294 | 294 | ||
497 | 295 | 295 | ||
510 | 296 | class TestLaunchpadInternalServer(MixinBaseLaunchpadServerTests, | 296 | class LaunchpadInternalServerTests: |
511 | 297 | TrialTestCase, BzrTestCase): | 297 | """Tests for the internal server classes, used by e.g. the scanner.""" |
500 | 298 | """Tests for the LaunchpadInternalServer, used by the puller and scanner. | ||
501 | 299 | """ | ||
502 | 300 | |||
503 | 301 | def setUp(self): | ||
504 | 302 | BzrTestCase.setUp(self) | ||
505 | 303 | MixinBaseLaunchpadServerTests.setUp(self) | ||
506 | 304 | |||
507 | 305 | def getLaunchpadServer(self, authserver, user_id): | ||
508 | 306 | return LaunchpadInternalServer( | ||
509 | 307 | 'lp-test:///', XMLRPCWrapper(authserver), MemoryTransport()) | ||
512 | 308 | 298 | ||
513 | 309 | def test_translate_branch_path(self): | 299 | def test_translate_branch_path(self): |
514 | 310 | branch = self.factory.makeAnyBranch() | 300 | branch = self.factory.makeAnyBranch() |
515 | @@ -314,7 +304,7 @@ | |||
516 | 314 | expected_transport, expected_path = dispatch | 304 | expected_transport, expected_path = dispatch |
517 | 315 | 305 | ||
518 | 316 | deferred = self.server.translateVirtualPath( | 306 | deferred = self.server.translateVirtualPath( |
520 | 317 | '%s/.bzr/README' % (branch.unique_name,)) | 307 | '/%s/.bzr/README' % (branch.unique_name,)) |
521 | 318 | def check_branch_transport((transport, path)): | 308 | def check_branch_transport((transport, path)): |
522 | 319 | self.assertEqual(expected_path, path) | 309 | self.assertEqual(expected_path, path) |
523 | 320 | # Can't test for equality of transports, since URLs and object | 310 | # Can't test for equality of transports, since URLs and object |
524 | @@ -348,6 +338,34 @@ | |||
525 | 348 | BzrDir.open_containing_from_transport, transport) | 338 | BzrDir.open_containing_from_transport, transport) |
526 | 349 | 339 | ||
527 | 350 | 340 | ||
528 | 341 | class TestLaunchpadInternalServer(MixinBaseLaunchpadServerTests, | ||
529 | 342 | TrialTestCase, BzrTestCase, | ||
530 | 343 | LaunchpadInternalServerTests): | ||
531 | 344 | """Tests for `LaunchpadInternalServer`, used by the puller and scanner.""" | ||
532 | 345 | |||
533 | 346 | def setUp(self): | ||
534 | 347 | BzrTestCase.setUp(self) | ||
535 | 348 | MixinBaseLaunchpadServerTests.setUp(self) | ||
536 | 349 | |||
537 | 350 | def getLaunchpadServer(self, authserver, user_id): | ||
538 | 351 | return LaunchpadInternalServer( | ||
539 | 352 | 'lp-test:///', XMLRPCWrapper(authserver), MemoryTransport()) | ||
540 | 353 | |||
541 | 354 | |||
542 | 355 | class TestDirectDatabaseLaunchpadServer(TestCaseWithFactory, TrialTestCase, | ||
543 | 356 | LaunchpadInternalServerTests): | ||
544 | 357 | """Tests for `DirectDatabaseLaunchpadServer`.""" | ||
545 | 358 | |||
546 | 359 | layer = ZopelessDatabaseLayer | ||
547 | 360 | |||
548 | 361 | def setUp(self): | ||
549 | 362 | super(TestDirectDatabaseLaunchpadServer, self).setUp() | ||
550 | 363 | self.requester = self.factory.makePerson() | ||
551 | 364 | self.server = DirectDatabaseLaunchpadServer( | ||
552 | 365 | 'lp-test://', MemoryTransport()) | ||
553 | 366 | |||
554 | 367 | |||
555 | 368 | |||
556 | 351 | class TestAsyncVirtualTransport(TrialTestCase, TestCaseInTempDir): | 369 | class TestAsyncVirtualTransport(TrialTestCase, TestCaseInTempDir): |
557 | 352 | """Tests for `AsyncVirtualTransport`.""" | 370 | """Tests for `AsyncVirtualTransport`.""" |
558 | 353 | 371 | ||
559 | 354 | 372 | ||
560 | === added file 'lib/lp/services/tests/test_utils.py' | |||
561 | --- lib/lp/services/tests/test_utils.py 1970-01-01 00:00:00 +0000 | |||
562 | +++ lib/lp/services/tests/test_utils.py 2009-10-20 21:56:15 +0000 | |||
563 | @@ -0,0 +1,32 @@ | |||
564 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | ||
565 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
566 | 3 | |||
567 | 4 | """Module docstring goes here.""" | ||
568 | 5 | |||
569 | 6 | __metaclass__ = type | ||
570 | 7 | |||
571 | 8 | import unittest | ||
572 | 9 | |||
573 | 10 | from lp.services.utils import iter_split | ||
574 | 11 | from lp.testing import TestCase | ||
575 | 12 | |||
576 | 13 | |||
577 | 14 | class TestIterateSplit(TestCase): | ||
578 | 15 | """Tests for iter_split.""" | ||
579 | 16 | |||
580 | 17 | def test_iter_split(self): | ||
581 | 18 | # iter_split loops over each way of splitting a string in two using | ||
582 | 19 | # the given splitter. | ||
583 | 20 | self.assertEqual([('one', '')], list(iter_split('one', '/'))) | ||
584 | 21 | self.assertEqual([], list(iter_split('', '/'))) | ||
585 | 22 | self.assertEqual( | ||
586 | 23 | [('one/two', ''), ('one', 'two')], | ||
587 | 24 | list(iter_split('one/two', '/'))) | ||
588 | 25 | self.assertEqual( | ||
589 | 26 | [('one/two/three', ''), ('one/two', 'three'), | ||
590 | 27 | ('one', 'two/three')], | ||
591 | 28 | list(iter_split('one/two/three', '/'))) | ||
592 | 29 | |||
593 | 30 | |||
594 | 31 | def test_suite(): | ||
595 | 32 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
596 | 0 | 33 | ||
597 | === added file 'lib/lp/services/utils.py' | |||
598 | --- lib/lp/services/utils.py 1970-01-01 00:00:00 +0000 | |||
599 | +++ lib/lp/services/utils.py 2009-10-20 21:56:15 +0000 | |||
600 | @@ -0,0 +1,29 @@ | |||
601 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | ||
602 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
603 | 3 | |||
604 | 4 | |||
605 | 5 | """Implementations of the XML-RPC APIs for codehosting.""" | ||
606 | 6 | |||
607 | 7 | __metaclass__ = type | ||
608 | 8 | __all__ = [ | ||
609 | 9 | 'iter_split', | ||
610 | 10 | ] | ||
611 | 11 | |||
612 | 12 | |||
613 | 13 | def iter_split(string, splitter): | ||
614 | 14 | """Iterate over ways to split 'string' in two with 'splitter'. | ||
615 | 15 | |||
616 | 16 | If 'string' is empty, then yield nothing. Otherwise, yield tuples like | ||
617 | 17 | ('a/b/c', ''), ('a/b', 'c'), ('a', 'b/c') for a string 'a/b/c' and a | ||
618 | 18 | splitter '/'. | ||
619 | 19 | |||
620 | 20 | The tuples are yielded such that the first tuple has everything in the | ||
621 | 21 | first tuple. With each iteration, the first element gets smaller and the | ||
622 | 22 | second gets larger. It stops iterating just before it would have to yield | ||
623 | 23 | ('', 'a/b/c'). | ||
624 | 24 | """ | ||
625 | 25 | if string == '': | ||
626 | 26 | return | ||
627 | 27 | tokens = string.split(splitter) | ||
628 | 28 | for i in reversed(range(1, len(tokens) + 1)): | ||
629 | 29 | yield splitter.join(tokens[:i]), splitter.join(tokens[i:]) | ||
630 | 0 | 30 | ||
631 | === modified file 'lib/lp/testing/__init__.py' | |||
632 | --- lib/lp/testing/__init__.py 2009-09-16 21:22:12 +0000 | |||
633 | +++ lib/lp/testing/__init__.py 2009-10-20 21:56:15 +0000 | |||
634 | @@ -572,7 +572,7 @@ | |||
635 | 572 | os.environ['BZR_HOME'] = os.getcwd() | 572 | os.environ['BZR_HOME'] = os.getcwd() |
636 | 573 | self.addCleanup(restore_bzr_home) | 573 | self.addCleanup(restore_bzr_home) |
637 | 574 | 574 | ||
639 | 575 | def useBzrBranches(self, real_server=False): | 575 | def useBzrBranches(self, real_server=False, direct_database=False): |
640 | 576 | """Prepare for using bzr branches. | 576 | """Prepare for using bzr branches. |
641 | 577 | 577 | ||
642 | 578 | This sets up support for lp-hosted and lp-mirrored URLs, | 578 | This sets up support for lp-hosted and lp-mirrored URLs, |
643 | @@ -586,7 +586,9 @@ | |||
644 | 586 | self.useTempBzrHome() | 586 | self.useTempBzrHome() |
645 | 587 | self.real_bzr_server = real_server | 587 | self.real_bzr_server = real_server |
646 | 588 | if real_server: | 588 | if real_server: |
648 | 589 | server = get_multi_server(write_hosted=True, write_mirrored=True) | 589 | server = get_multi_server( |
649 | 590 | write_hosted=True, write_mirrored=True, | ||
650 | 591 | direct_database=direct_database) | ||
651 | 590 | server.setUp() | 592 | server.setUp() |
652 | 591 | self.addCleanup(server.destroy) | 593 | self.addCleanup(server.destroy) |
653 | 592 | else: | 594 | else: |
654 | 593 | 595 | ||
655 | === modified file 'scripts/branch-distro.py' | |||
656 | --- scripts/branch-distro.py 2009-10-13 23:50:17 +0000 | |||
657 | +++ scripts/branch-distro.py 2009-10-20 21:56:15 +0000 | |||
658 | @@ -24,7 +24,8 @@ | |||
659 | 24 | if len(self.args) != 3: | 24 | if len(self.args) != 3: |
660 | 25 | self.parser.error("Wrong number of arguments.") | 25 | self.parser.error("Wrong number of arguments.") |
661 | 26 | brancher = DistroBrancher.fromNames(self.logger, *self.args) | 26 | brancher = DistroBrancher.fromNames(self.logger, *self.args) |
663 | 27 | server = get_multi_server(write_mirrored=True, write_hosted=True) | 27 | server = get_multi_server( |
664 | 28 | write_mirrored=True, write_hosted=True, direct_database=True) | ||
665 | 28 | server.setUp() | 29 | server.setUp() |
666 | 29 | try: | 30 | try: |
667 | 30 | if self.options.check: | 31 | if self.options.check: |
Hi,
This branch adds a way of translating branch paths from external to internal by direct communication with the database, and uses it for the branch-distro.py script. Tests on staging became about 50x faster as a result of this change.
Cheers,
mwh