Code review comment for lp:~henninge/launchpad/bug-523810-needs-information-age
- bug-523810-needs-information-age
- Merge into devel
Revision history for this message
Henning Eggers (henninge) wrote : | # |
1 | === modified file 'lib/lp/translations/interfaces/translationimportqueue.py' |
2 | --- lib/lp/translations/interfaces/translationimportqueue.py 2010-02-19 08:37:27 +0000 |
3 | +++ lib/lp/translations/interfaces/translationimportqueue.py 2010-02-19 10:36:42 +0000 |
4 | @@ -41,7 +41,7 @@ |
5 | 'RosettaImportStatus', |
6 | 'SpecialTranslationImportTargetFilter', |
7 | 'TranslationFileType', |
8 | - 'TranslationImportQueueEntryAge', |
9 | + 'translation_import_queue_entry_age', |
10 | 'UserCannotSetTranslationImportStatus', |
11 | ] |
12 | |
13 | @@ -115,18 +115,18 @@ |
14 | |
15 | |
16 | # Some time spans in days. |
17 | -_month = 30 |
18 | -_half_year = 366 / 2 |
19 | +DAYS_IN_MONTH = 30 |
20 | +DAYS_IN_HALF_YEAR = 366 / 2 |
21 | |
22 | |
23 | # Period after which entries with certain statuses are culled from the |
24 | # queue. |
25 | -TranslationImportQueueEntryAge = { |
26 | +translation_import_queue_entry_age = { |
27 | RosettaImportStatus.DELETED: timedelta(days=3), |
28 | - RosettaImportStatus.FAILED: timedelta(days=_month), |
29 | + RosettaImportStatus.FAILED: timedelta(days=DAYS_IN_MONTH), |
30 | RosettaImportStatus.IMPORTED: timedelta(days=3), |
31 | - RosettaImportStatus.NEEDS_INFORMATION: timedelta(days=_half_year), |
32 | - RosettaImportStatus.NEEDS_REVIEW: timedelta(days=_half_year), |
33 | + RosettaImportStatus.NEEDS_INFORMATION: timedelta(days=DAYS_IN_HALF_YEAR), |
34 | + RosettaImportStatus.NEEDS_REVIEW: timedelta(days=DAYS_IN_HALF_YEAR), |
35 | } |
36 | |
37 | |
38 | |
39 | === modified file 'lib/lp/translations/model/translationimportqueue.py' |
40 | --- lib/lp/translations/model/translationimportqueue.py 2010-02-19 07:46:41 +0000 |
41 | +++ lib/lp/translations/model/translationimportqueue.py 2010-02-19 10:56:20 +0000 |
42 | @@ -58,7 +58,7 @@ |
43 | RosettaImportStatus, |
44 | SpecialTranslationImportTargetFilter, |
45 | TranslationImportQueueConflictError, |
46 | - TranslationImportQueueEntryAge, |
47 | + translation_import_queue_entry_age, |
48 | UserCannotSetTranslationImportStatus) |
49 | from lp.translations.interfaces.potemplate import IPOTemplate |
50 | from lp.translations.interfaces.translations import TranslationConstants |
51 | @@ -1244,7 +1244,7 @@ |
52 | """ |
53 | now = datetime.datetime.now(pytz.UTC) |
54 | deletion_clauses = [] |
55 | - for status, max_age in TranslationImportQueueEntryAge.iteritems(): |
56 | + for status, max_age in translation_import_queue_entry_age.iteritems(): |
57 | cutoff = now - max_age |
58 | deletion_clauses.append(And( |
59 | TranslationImportQueueEntry.status == status, |
60 | |
61 | === modified file 'lib/lp/translations/tests/test_autoapproval.py' |
62 | --- lib/lp/translations/tests/test_autoapproval.py 2010-02-19 09:01:21 +0000 |
63 | +++ lib/lp/translations/tests/test_autoapproval.py 2010-02-19 10:57:42 +0000 |
64 | @@ -34,7 +34,7 @@ |
65 | TranslationImportQueue, TranslationImportQueueEntry) |
66 | from lp.translations.interfaces.customlanguagecode import ICustomLanguageCode |
67 | from lp.translations.interfaces.translationimportqueue import ( |
68 | - RosettaImportStatus, TranslationImportQueueEntryAge) |
69 | + RosettaImportStatus, translation_import_queue_entry_age) |
70 | from lp.testing import TestCaseWithFactory |
71 | from lp.testing.factory import LaunchpadObjectFactory |
72 | from canonical.launchpad.webapp.testing import verifyObject |
73 | @@ -42,20 +42,26 @@ |
74 | |
75 | |
76 | class GardenerDbUserMixin(object): |
77 | - """Switch to the translations import queue gardener database role.""" |
78 | - |
79 | - def become_the_gardener(self): |
80 | - """Switch the user once.""" |
81 | + """Switch to the translations import queue gardener database role. |
82 | + |
83 | + Admittedly, this might be a little over-engineered but it looks good. ;) |
84 | + """ |
85 | + |
86 | + def _become(self, dbuser): |
87 | + """Switch to a different db user.""" |
88 | transaction.commit() |
89 | - self.layer.switchDbUser('translations_import_queue_gardener') |
90 | + self.layer.switchDbUser(dbuser) |
91 | + |
92 | + def becomeTheGardener(self): |
93 | + """One-way method to avoid unnecessary switch back.""" |
94 | + self._become('translations_import_queue_gardener') |
95 | |
96 | @contextmanager |
97 | - def being_the_gardener(self): |
98 | + def beingTheGardener(self): |
99 | """Context manager to restore the launchpad user.""" |
100 | - self.become_the_gardener() |
101 | + self._become('translations_import_queue_gardener') |
102 | yield |
103 | - transaction.commit() |
104 | - self.layer.switchDbUser('launchpad') |
105 | + self._become('launchpad') |
106 | |
107 | |
108 | class TestCustomLanguageCode(unittest.TestCase): |
109 | @@ -191,7 +197,7 @@ |
110 | # Of course matching will work without custom language codes. |
111 | tr_file = self._makePOFile('tr') |
112 | entry = self._makeQueueEntry('tr') |
113 | - self.become_the_gardener() |
114 | + self.becomeTheGardener() |
115 | self.assertEqual(entry.getGuessedPOFile(), tr_file) |
116 | |
117 | def test_CustomLanguageCodeEnablesMatch(self): |
118 | @@ -203,7 +209,7 @@ |
119 | |
120 | self._setCustomLanguageCode('fy_NL', 'fy') |
121 | |
122 | - self.become_the_gardener() |
123 | + self.becomeTheGardener() |
124 | self.assertEqual(entry.getGuessedPOFile(), fy_file) |
125 | |
126 | def test_CustomLanguageCodeParsesBogusLanguage(self): |
127 | @@ -214,7 +220,7 @@ |
128 | |
129 | self._setCustomLanguageCode('flemish', 'nl') |
130 | |
131 | - self.become_the_gardener() |
132 | + self.becomeTheGardener() |
133 | nl_file = entry.getGuessedPOFile() |
134 | self.assertEqual(nl_file.language.code, 'nl') |
135 | |
136 | @@ -227,7 +233,7 @@ |
137 | |
138 | self._setCustomLanguageCode('sv', None) |
139 | |
140 | - self.become_the_gardener() |
141 | + self.becomeTheGardener() |
142 | self.assertEqual(entry.getGuessedPOFile(), None) |
143 | self.assertEqual(entry.status, RosettaImportStatus.DELETED) |
144 | |
145 | @@ -240,7 +246,7 @@ |
146 | |
147 | self._setCustomLanguageCode('elx', 'el') |
148 | |
149 | - self.become_the_gardener() |
150 | + self.becomeTheGardener() |
151 | el_file = entry.getGuessedPOFile() |
152 | self.failIfEqual(el_file, elx_file) |
153 | self.assertEqual(el_file.language.code, 'el') |
154 | @@ -255,7 +261,7 @@ |
155 | |
156 | self._setCustomLanguageCode('nb', 'nn') |
157 | |
158 | - self.become_the_gardener() |
159 | + self.becomeTheGardener() |
160 | self.assertEqual(entry.getGuessedPOFile(), nn_file) |
161 | |
162 | def test_CustomLanguageCodeReplacesMatch(self): |
163 | @@ -268,7 +274,7 @@ |
164 | self._setCustomLanguageCode('pt', None) |
165 | self._setCustomLanguageCode('pt_PT', 'pt') |
166 | |
167 | - self.become_the_gardener() |
168 | + self.becomeTheGardener() |
169 | self.assertEqual(pt_entry.getGuessedPOFile(), None) |
170 | self.assertEqual(pt_PT_entry.getGuessedPOFile(), pt_file) |
171 | |
172 | @@ -282,7 +288,7 @@ |
173 | self._setCustomLanguageCode('zh_CN', 'zh_TW') |
174 | self._setCustomLanguageCode('zh_TW', 'zh_CN') |
175 | |
176 | - self.become_the_gardener() |
177 | + self.becomeTheGardener() |
178 | self.assertEqual(zh_CN_entry.getGuessedPOFile(), zh_TW_file) |
179 | self.assertEqual(zh_TW_entry.getGuessedPOFile(), zh_CN_file) |
180 | |
181 | @@ -327,7 +333,7 @@ |
182 | # When multiple templates match for a product series, |
183 | # getPOTemplateByPathAndOrigin returns none. |
184 | self._setUpProduct() |
185 | - self.become_the_gardener() |
186 | + self.becomeTheGardener() |
187 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
188 | 'test.pot', productseries=self.productseries) |
189 | self.assertEqual(None, guessed_template) |
190 | @@ -336,7 +342,7 @@ |
191 | # When multiple templates match on sourcepackagename, |
192 | # getPOTemplateByPathAndOrigin returns none. |
193 | self._setUpDistro() |
194 | - self.become_the_gardener() |
195 | + self.becomeTheGardener() |
196 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
197 | 'test.pot', sourcepackagename=self.packagename) |
198 | self.assertEqual(None, guessed_template) |
199 | @@ -345,7 +351,7 @@ |
200 | # When multiple templates match on from_sourcepackagename, |
201 | # getPOTemplateByPathAndOrigin returns none. |
202 | self._setUpDistro() |
203 | - self.become_the_gardener() |
204 | + self.becomeTheGardener() |
205 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
206 | 'test.pot', sourcepackagename=self.from_packagename) |
207 | self.assertEqual(None, guessed_template) |
208 | @@ -379,7 +385,7 @@ |
209 | self.distrotemplate1.sourcepackagename = match_package |
210 | self.distrotemplate2.from_sourcepackagename = match_package |
211 | |
212 | - self.become_the_gardener() |
213 | + self.becomeTheGardener() |
214 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
215 | 'test.pot', sourcepackagename=match_package) |
216 | self.assertEqual(self.distrotemplate2, guessed_template) |
217 | @@ -390,7 +396,7 @@ |
218 | self._setUpProduct() |
219 | self.producttemplate1.iscurrent = False |
220 | self.producttemplate2.iscurrent = True |
221 | - self.become_the_gardener() |
222 | + self.becomeTheGardener() |
223 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
224 | 'test.pot', productseries=self.productseries) |
225 | self.assertEqual(guessed_template, self.producttemplate2) |
226 | @@ -400,7 +406,7 @@ |
227 | self._setUpProduct() |
228 | self.producttemplate1.iscurrent = False |
229 | self.producttemplate2.iscurrent = False |
230 | - self.become_the_gardener() |
231 | + self.becomeTheGardener() |
232 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
233 | 'test.pot', productseries=self.productseries) |
234 | self.assertEqual(guessed_template, None) |
235 | @@ -414,7 +420,7 @@ |
236 | self.distrotemplate2.iscurrent = True |
237 | self.distrotemplate1.from_sourcepackagename = None |
238 | self.distrotemplate2.from_sourcepackagename = None |
239 | - self.become_the_gardener() |
240 | + self.becomeTheGardener() |
241 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
242 | 'test.pot', distroseries=self.distroseries, |
243 | sourcepackagename=self.packagename) |
244 | @@ -427,7 +433,7 @@ |
245 | self.distrotemplate2.iscurrent = False |
246 | self.distrotemplate1.from_sourcepackagename = None |
247 | self.distrotemplate2.from_sourcepackagename = None |
248 | - self.become_the_gardener() |
249 | + self.becomeTheGardener() |
250 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
251 | 'test.pot', distroseries=self.distroseries, |
252 | sourcepackagename=self.packagename) |
253 | @@ -442,7 +448,7 @@ |
254 | self.distrotemplate2.iscurrent = True |
255 | self.distrotemplate1.from_sourcepackagename = self.from_packagename |
256 | self.distrotemplate2.from_sourcepackagename = self.from_packagename |
257 | - self.become_the_gardener() |
258 | + self.becomeTheGardener() |
259 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
260 | 'test.pot', distroseries=self.distroseries, |
261 | sourcepackagename=self.from_packagename) |
262 | @@ -456,7 +462,7 @@ |
263 | self.distrotemplate2.iscurrent = False |
264 | self.distrotemplate1.from_sourcepackagename = self.from_packagename |
265 | self.distrotemplate2.from_sourcepackagename = self.from_packagename |
266 | - self.become_the_gardener() |
267 | + self.becomeTheGardener() |
268 | guessed_template = self.templateset.getPOTemplateByPathAndOrigin( |
269 | 'test.pot', distroseries=self.distroseries, |
270 | sourcepackagename=self.from_packagename) |
271 | @@ -467,7 +473,7 @@ |
272 | # translation domain. |
273 | self._setUpDistro() |
274 | subset = POTemplateSubset(distroseries=self.distroseries) |
275 | - self.become_the_gardener() |
276 | + self.becomeTheGardener() |
277 | potemplate = subset.getPOTemplateByTranslationDomain('test1') |
278 | self.assertEqual(potemplate, self.distrotemplate1) |
279 | |
280 | @@ -475,7 +481,7 @@ |
281 | # Test getPOTemplateByTranslationDomain for the zero-match case. |
282 | self._setUpDistro() |
283 | subset = POTemplateSubset(distroseries=self.distroseries) |
284 | - self.become_the_gardener() |
285 | + self.becomeTheGardener() |
286 | potemplate = subset.getPOTemplateByTranslationDomain('notesthere') |
287 | self.assertEqual(potemplate, None) |
288 | |
289 | @@ -490,7 +496,7 @@ |
290 | clashing_template = other_subset.new( |
291 | 'test3', 'test1', 'test3.pot', self.distro.owner) |
292 | distro_subset = POTemplateSubset(distroseries=self.distroseries) |
293 | - self.become_the_gardener() |
294 | + self.becomeTheGardener() |
295 | potemplate = distro_subset.getPOTemplateByTranslationDomain('test1') |
296 | self.assertEqual(potemplate, None) |
297 | |
298 | @@ -520,7 +526,7 @@ |
299 | 'program/nl.po', 'other contents', False, template.owner, |
300 | productseries=template.productseries, potemplate=template) |
301 | |
302 | - self.become_the_gardener() |
303 | + self.becomeTheGardener() |
304 | entry1.getGuessedPOFile() |
305 | |
306 | self.assertEqual(entry1.potemplate, None) |
307 | @@ -642,7 +648,7 @@ |
308 | poname, self.pocontents, False, self.distroseries.owner, |
309 | sourcepackagename=self.kde_i18n_ca, |
310 | distroseries=self.distroseries) |
311 | - self.become_the_gardener() |
312 | + self.becomeTheGardener() |
313 | pofile = entry.getGuessedPOFile() |
314 | self.assertEqual(pofile, self.pofile_ca) |
315 | |
316 | @@ -654,7 +660,7 @@ |
317 | poname, self.pocontents, False, self.distroseries.owner, |
318 | sourcepackagename=self.kde_l10n_nl, |
319 | distroseries=self.distroseries) |
320 | - self.become_the_gardener() |
321 | + self.becomeTheGardener() |
322 | pofile = entry.getGuessedPOFile() |
323 | self.assertEqual(pofile, self.pofile_nl) |
324 | |
325 | @@ -682,7 +688,7 @@ |
326 | entry = self.queue.addOrUpdateEntry( |
327 | 'nl.po', '# ...', False, template.owner, productseries=trunk) |
328 | |
329 | - self.become_the_gardener() |
330 | + self.becomeTheGardener() |
331 | pofile = entry._get_pofile_from_language('nl', 'domain') |
332 | self.assertNotEqual(None, pofile) |
333 | |
334 | @@ -700,7 +706,7 @@ |
335 | entry = self.queue.addOrUpdateEntry( |
336 | 'nl.po', '# ...', False, template.owner, productseries=trunk) |
337 | |
338 | - self.become_the_gardener() |
339 | + self.becomeTheGardener() |
340 | pofile = entry._get_pofile_from_language('nl', 'domain') |
341 | self.assertEqual(None, pofile) |
342 | |
343 | @@ -720,7 +726,7 @@ |
344 | entry = self.queue.addOrUpdateEntry( |
345 | 'nl.po', '# ...', False, template.owner, productseries=trunk) |
346 | |
347 | - self.become_the_gardener() |
348 | + self.becomeTheGardener() |
349 | pofile = entry._get_pofile_from_language('nl', 'domain') |
350 | self.assertNotEqual(None, pofile) |
351 | |
352 | @@ -778,10 +784,14 @@ |
353 | entry_id = entry.id |
354 | |
355 | self._setStatus(entry, RosettaImportStatus.APPROVED, one_year_ago) |
356 | + # No write or delete action expected, so no reason to switch the |
357 | + # database user. If it writes or deletes, the test has failed anyway. |
358 | self.queue._cleanUpObsoleteEntries(self.store) |
359 | self.assertTrue(self._exists(entry_id)) |
360 | |
361 | self._setStatus(entry, RosettaImportStatus.BLOCKED, one_year_ago) |
362 | + # No write or delete action expected, so no reason to switch the |
363 | + # database user. If it writes or deletes, the test has failed anyway. |
364 | self.queue._cleanUpObsoleteEntries(self.store) |
365 | self.assertTrue(self._exists(entry_id)) |
366 | |
367 | @@ -799,7 +809,7 @@ |
368 | for status in affected_statuses: |
369 | entry = self._makeProductEntry() |
370 | entry.potemplate = self.factory.makePOTemplate() |
371 | - maximum_age = TranslationImportQueueEntryAge[status] |
372 | + maximum_age = translation_import_queue_entry_age[status] |
373 | # Avoid the exact date here by a day because that could introduce |
374 | # spurious test failures. |
375 | almost_oldest_possible_date = ( |
376 | @@ -807,14 +817,17 @@ |
377 | self._setStatus(entry, status, almost_oldest_possible_date) |
378 | entry_id = entry.id |
379 | |
380 | + # No write or delete action expected, so no reason to switch the |
381 | + # database user. If it writes or deletes, the test has failed |
382 | + # anyway. |
383 | self.queue._cleanUpObsoleteEntries(self.store) |
384 | self.assertTrue(self._exists(entry_id)) |
385 | |
386 | - # Now cross the border, again cushoning it by a day. |
387 | + # Now cross the border, again cushioning it by a day. |
388 | entry.date_status_changed -= timedelta(days=2) |
389 | entry.syncUpdate() |
390 | |
391 | - with self.being_the_gardener(): |
392 | + with self.beingTheGardener(): |
393 | self.queue._cleanUpObsoleteEntries(self.store) |
394 | self.assertFalse( |
395 | self._exists(entry_id), |
396 | @@ -833,7 +846,7 @@ |
397 | entry.productseries.product.active = False |
398 | entry.productseries.product.syncUpdate() |
399 | |
400 | - self.become_the_gardener() |
401 | + self.becomeTheGardener() |
402 | self.queue._cleanUpInactiveProductEntries(self.store) |
403 | self.assertFalse(self._exists(entry_id)) |
404 | |
405 | @@ -849,7 +862,7 @@ |
406 | entry.distroseries.status = SeriesStatus.OBSOLETE |
407 | entry.distroseries.syncUpdate() |
408 | |
409 | - self.become_the_gardener() |
410 | + self.becomeTheGardener() |
411 | self.queue._cleanUpObsoleteDistroEntries(self.store) |
412 | self.assertFalse(self._exists(entry_id)) |
413 | |
414 | @@ -885,7 +898,7 @@ |
415 | entry = self._makeQueueEntry(trunk) |
416 | rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
417 | |
418 | - self.become_the_gardener() |
419 | + self.becomeTheGardener() |
420 | |
421 | pofile = entry.getGuessedPOFile() |
422 | |
423 | @@ -903,7 +916,7 @@ |
424 | |
425 | entry = self._makeQueueEntry(trunk) |
426 | |
427 | - self.become_the_gardener() |
428 | + self.becomeTheGardener() |
429 | |
430 | pofile = entry.getGuessedPOFile() |
431 |
> Hi Henning,
Hallo Abel!
> overall a nice branc, but I have a couple of questions, see
> below.
Thanks for the review, though. ;)
> > === modified file translations/ interfaces/ translationimpo rtqueue. py' translations/ interfaces/ translationimpo rtqueue. py 2010-02-05 translations/ interfaces/ translationimpo rtqueue. py 2010-02-19 msg=E0211, E0213 tatus', tionImportTarge tFilter' , eType', ortQueueEntryAg e', ranslationImpor tStatus' , ortQueueEntryAg e = { atus.DELETED: timedelta(days=3), atus.FAILED: timedelta( days=_month) , MONTH/DAYS_ OF_HALF_ YEAR or DAYS_IN_ MONTH/DAYS_ IN_HALF_ YEAR.
'lib/lp/
> > --- lib/lp/
> 10:39:45 +0000
> > +++ lib/lp/
> 09:03:16 +0000
> > @@ -3,6 +3,8 @@
> >
> > # pylint: disable-
> >
> > +from datetime import timedelta
> > +
> > from zope.interface import Interface, Attribute
> > from zope.schema import (
> > Bool, Choice, Datetime, Field, Int, Object, Text, TextLine)
> > @@ -39,6 +41,7 @@
> > 'RosettaImportS
> > 'SpecialTransla
> > 'TranslationFil
> > + 'TranslationImp
> > 'UserCannotSetT
> > ]
> >
> > @@ -111,6 +114,22 @@
> > """)
> >
> >
> > +# Some time spans in days.
> > +_month = 30
> > +_half_year = 366 / 2
>
> This being constants, they should be defined as MONTH and HALF_YEAR,
> I think, but...
>
> > +
> > +
> > +# Period after which entries with certain statuses are culled from the
> > +# queue.
> > +TranslationImp
> > + RosettaImportSt
> > + RosettaImportSt
>
> ...seeing this, I think the constants are probably better called
> DAYS_OF_
Yes, you are right about the capitalization. And the names are good
suggestions, too. Fixed that.
> atus.IMPORTED: timedelta(days=3), atus.NEEDS_ INFORMATION: timedelta( days=_half_ year), atus.NEEDS_ REVIEW: timedelta( days=_half_ year), import_ queue_entry_ age. Also, I don't think that you
> > + RosettaImportSt
> > + RosettaImportSt
> > + RosettaImportSt
> > +}
>
> Our coding style convetions say that this should called
> translation_
> need to define it in the interfaces module, you could also
> add it to the __all__ list of the model module.
I renamed it but left it in place here, as per our discussion on IRC. We independent and I don't see a pressing reason *not* to
agreed that the general rule is that tests should not import from model
code. I also noted that I consider these expiration times as
implementation-
have this here in the interface module. You agreed to the latter.
> ionImportTarget Filter( DBEnumeratedTyp e): translations/ model/translati onimportqueue. py' translations/ tests/test_ autoapproval. py' translations/ tests/test_ autoapproval. py 2010-01-26 translations/ tests/test_ autoapproval. py 2010-02-19 rtQueue, TranslationImpo rtQueueEntry) .interfaces. customlanguagec ode import .interfaces. translationimpo rtqueue import ( atus) atus, TranslationImpo rtQueueEntryAge ) Factory launchpad. webapp. testing import verifyObject ssLayer the_gardener( layer): ixin(object) : commit( ) ser('translatio ns_import_ queue_gardener' ) the_gardener( self):
> > +
> > +
> > class SpecialTranslat
> > """Special "meta-targets" to filter the queue view by."""
> >
> >
> > === modified file 'lib/lp/
[...]
> > === modified file 'lib/lp/
> > --- lib/lp/
15:25:53
> +0000
> > +++ lib/lp/
09:03:16
> +0000
> > @@ -8,6 +8,9 @@
> > through the possibilities should go here.
> > """
> >
> > +from __future__ import with_statement
> > +
> > +from contextlib import contextmanager
> > from datetime import datetime, timedelta
> > from pytz import UTC
> > import transaction
> > @@ -31,17 +34,28 @@
> > TranslationImpo
> > from lp.translations
> ICustomLanguageCode
> > from lp.translations
> > - RosettaImportSt
> > + RosettaImportSt
> > from lp.testing import TestCaseWithFactory
> > from lp.testing.factory import LaunchpadObject
> > from canonical.
> > from canonical.testing import LaunchpadZopele
> >
> >
> > -def become_
> > +class GardenerDbUserM
> > """Switch to the translations import queue gardener database
role."""
> > - transaction.
> > - layer.switchDbU
> > +
> > + def become_
>
> The method name should be camelCased.
Right, fixed.
> commit( ) switchDbUser( 'translations_ import_ queue_gardener' ) gardener( self):
> > + """Switch the user once."""
> > + transaction.
> > + self.layer.
> > +
> > + @contextmanager
> > + def being_the_
>
> I think this name should also be camelCased, but I am not sure --
> do we consider context meanagers to be similar to methods or similar
> to properties?
It needs to be called(), so I'd consider it a method. Changed to camelCase.
> the_gardener( ) commit( ) switchDbUser( 'launchpad' ) the_gardener( ) and the_gardener( ) quite often in the tests, the-gardener( )" there.
> > + """Context manager to restore the launchpad user."""
> > + self.become_
> > + yield
> > + transaction.
> > + self.layer.
>
> Did you consider to drop the method become_
> to include the code directly in the context manager? I see
> that you call become_
> but you could also use "with self.being_
> OK, that would add the second commit() and switchDbUser()
> calls to all these tests, but would this be a noticable slow down?
I try to avoid any slowdown in tests. I documented this as a "one-way"
function now, so this should be clearer.
> teh_gardener( ), but at least for my coffee-starved brain
> Perhaps it is a sign that I need even more coffee this morning,
> but my first thought seeing this code was "the code before and
> after the 'yield' looks somewhat asymmetric, and why do we need
> to commit() and switchDbUser() as a cleanup?" Sure, it dawned to
> me that these calls are needed due to the corresponding calls in
> become_
> that was slightly difficult ;)
Sorry about that. I noticed the asymmetry, too, but did nothing about
it. Shame on me. It looks nice and symmetric now. ;-)
> oleteEntries_ needs_review( self): eEntries cleans up entries in Needs Review ctEntry( ) makePOTemplate( ) (entry, RosettaImportSt atus.NEEDS_ REVIEW, None) _cleanUpObsolet eEntries( self.store) (self._ exists( entry_id) ) status_ changed -= timedelta(days=200) the_gardener( self.layer) _cleanUpObsolet eEntries( self.store) e(self. _exists( entry_id) ) atus.DELETED, atus.FAILED, atus.IMPORTED, atus.NEEDS_ INFORMATION, atus.NEEDS_ REVIEW, ctEntry( ) makePOTemplate( ) rtQueueEntryAge [status] oldest_ possible_ date = ( (entry, status, almost_ oldest_ possible_ date) _cleanUpObsolet eEntries( self.store) (self._ exists( entry_id) ) eEntries( self.store) is called by the status_ changed -= timedelta(days=2) the_gardener( ): _cleanUpObsolet eEntries( self.store) entry_id) ,
> [...]
>
> > - def test_cleanUpObs
> > - # _cleanUpObsolet
> > - # state after a very long wait.
> > - entry = self._makeProdu
> > - entry.potemplate = self.factory.
> > - self._setStatus
> > - entry_id = entry.id
> > -
> > - self.queue.
> > - self.assertTrue
> > -
> > - entry.date_
> > - entry.syncUpdate()
> > -
> > - become_
> > - self.queue.
> > - self.assertFals
> > + affected_statuses = [
> > + RosettaImportSt
> > + RosettaImportSt
> > + RosettaImportSt
> > + RosettaImportSt
> > + RosettaImportSt
> > + ]
> > + for status in affected_statuses:
> > + entry = self._makeProdu
> > + entry.potemplate = self.factory.
> > + maximum_age = TranslationImpo
> > + # Avoid the exact date here by a day because that could
> introduce
> > + # spurious test failures.
> > + almost_
> > + datetime.now(UTC) - maximum_age + timedelta(days=1))
> > + self._setStatus
> > + entry_id = entry.id
> > +
> > + self.queue.
> > + self.assertTrue
>
> so, here _cleanUpObsolet
> regular user...
>
> > +
> > + # Now cross the border, again cushoning it by a day.
> > + entry.date_
> > + entry.syncUpdate()
> > +
> > + with self.being_
> > + self.queue.
> > + self.assertFalse(
> > + self._exists(
> > + "Queue entry in state '%s' was not removed." %
status)
>
> while it is called here by the gardener? While I'm writing this
> comments, it dawns to me that the former call does not involve
> any DB write or delete operations, while the latter does, but
> this might be worth a short comment.
Comment added.
Thank you for the review! ;-)