Merge lp:~jtv/launchpad/bug-411514 into lp:launchpad

Proposed by Jeroen T. Vermeulen on 2010-02-12
Status: Merged
Approved by: Jeroen T. Vermeulen on 2010-02-12
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~jtv/launchpad/bug-411514
Merge into: lp:launchpad
Diff against target: 72 lines (+36/-15)
2 files modified
lib/lp/translations/utilities/tests/test_file_importer.py (+24/-0)
lib/lp/translations/utilities/translation_import.py (+12/-15)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-411514
Reviewer Review Type Date Requested Status
Abel Deuring (community) code 2010-02-12 Approve on 2010-02-12
Review via email: mp+19168@code.launchpad.net

Commit Message

Fix "email address already in use" poimport oopses.

To post a comment you must log in.
Jeroen T. Vermeulen (jtv) wrote :

= Bug 411514 =

Now that we're getting proper oops reports from the translations import script, it's become painfully obvious that imports are failing every day because of a bug in handling the person/account split.

Danilo did some digging and came up with this explanation: sometimes there's an Account with the email address we're looking for, but no Person is associated with it. In that case, creating a Person (and implicitly an Account) will fail because the Account already exists.

The Soyuz team has already solved this by calling IPersonSet.ensurePerson, which handles the various cases of existence, nonexistence, and partial existence.

A test exercises this scenario; I used it to reproduce the bug first. The actual code is a bit simpler now, as it now always prepares to create a person without knowing or caring whether there already is one. Another test verifies that behavior for invalid email addresses is unchanged.

For Q/A, ensure that imports still happen & watch the oopses go away. One way to trigger the former oops deliberately is to upload a translation file listing martijn at foodfight (org, not com) as the author in the header. Hoi Martijn!

Test:
{{{
./bin/test -vv -t test_file_importer
}}}

No lint.

Jeroen

Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/utilities/tests/test_file_importer.py'
2--- lib/lp/translations/utilities/tests/test_file_importer.py 2009-07-17 00:26:05 +0000
3+++ lib/lp/translations/utilities/tests/test_file_importer.py 2010-02-12 11:33:20 +0000
4@@ -307,6 +307,30 @@
5 po_importer.potemplate.displayname),
6 'Did not create the correct comment for %s' % test_email)
7
8+ def test_getPersonByEmail_personless_account(self):
9+ # An Account without a Person attached is a difficult case for
10+ # _getPersonByEmail: it has to create the Person but re-use an
11+ # existing Account and email address.
12+ (pot_importer, po_importer) = self._createImporterForExportedEntries()
13+ test_email = 'freecdsplease@example.com'
14+ account = self.factory.makeAccount('Send me Ubuntu', test_email)
15+
16+ person = po_importer._getPersonByEmail(test_email)
17+
18+ self.assertEqual(account, person.account)
19+
20+ # The same person will come up for the same address next time.
21+ self.assertEqual(person, po_importer._getPersonByEmail(test_email))
22+
23+ def test_getPersonByEmail_bad_address(self):
24+ # _getPersonByEmail returns None for malformed addresses.
25+ (pot_importer, po_importer) = self._createImporterForExportedEntries()
26+ test_email = 'john over at swansea'
27+
28+ person = po_importer._getPersonByEmail(test_email)
29+
30+ self.assertEqual(None, person)
31+
32 def test_FileImporter_importFile_ok(self):
33 # Test correct import operation for both
34 # exported and published files.
35
36=== modified file 'lib/lp/translations/utilities/translation_import.py'
37--- lib/lp/translations/utilities/translation_import.py 2009-09-14 09:12:58 +0000
38+++ lib/lp/translations/utilities/translation_import.py 2010-02-12 11:33:20 +0000
39@@ -758,21 +758,18 @@
40 return None
41
42 personset = getUtility(IPersonSet)
43- person = personset.getByEmail(email)
44-
45- if person is None:
46- # We create a new person, without a password.
47- comment = 'when importing the %s translation of %s' % (
48- self.pofile.language.displayname, self.potemplate.displayname)
49-
50- try:
51- person, dummy = personset.createPersonAndEmail(
52- email, PersonCreationRationale.POFILEIMPORT,
53- displayname=name, comment=comment)
54- except InvalidEmailAddress:
55- return None
56-
57- return person
58+
59+ # We may have to create a new person. If we do, this is the
60+ # rationale.
61+ comment = 'when importing the %s translation of %s' % (
62+ self.pofile.language.displayname, self.potemplate.displayname)
63+ rationale = PersonCreationRationale.POFILEIMPORT
64+
65+ try:
66+ return personset.ensurePerson(
67+ email, displayname=name, rationale=rationale, comment=comment)
68+ except InvalidEmailAddress:
69+ return None
70
71 def importMessage(self, message):
72 """See FileImporter."""