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

Proposed by Jeroen T. Vermeulen
Status: Work in progress
Proposed branch: lp:~jtv/launchpad/bug-876348
Merge into: lp:launchpad
Diff against target: 140 lines (+60/-12)
2 files modified
lib/lp/archiveuploader/dscfile.py (+19/-11)
lib/lp/archiveuploader/tests/test_dscfile.py (+41/-1)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-876348
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+79954@code.launchpad.net

Commit message

Turn archive-uploader InvalidEmailAddress errors into UploadErrors.

Description of the change

One possible solution for bug 876348: make DSCFile.parseAddress do what the docstring says it will in this case — raise UploadError if the email address is malformed. This particular type of exception should be handled a bit more robustly.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

You need to raise EarlyReturnUploadError and it should work. UploadError indicates a non-replyable catastrophic error.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

From IRC (times in UTC):

(13:36:37) bigjools: jtv: actually EarlyReturn is not ideal thinking about it
(13:36:37) bigjools: we just need to add a rejection and let processing continue so it collects all the errors
(13:37:19) bigjools: I really detest the upload processor
(13:47:51) jtv: bigjools: I guess it's two problems: one, “malformed email address and policy that says to create the user” does something very different from “unknown email address and policy that says not to create the user” or “malformed email address and policy that says not to create the user.” Two, it'd be nice to have more robust handling of these error conditions.
(13:47:59) jtv: Does that make sense?

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

(13:51:12) bigjools: jtv: no
(13:51:21) bigjools: but then I am trying to do 2 things at once here
(13:52:04) jtv: OK — it's late here anyway, so we can leave this to ferment in the hindbrain for now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archiveuploader/dscfile.py'
2--- lib/lp/archiveuploader/dscfile.py 2011-05-20 13:36:11 +0000
3+++ lib/lp/archiveuploader/dscfile.py 2011-10-20 12:11:38 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """ DSCFile and related.
10@@ -29,6 +29,7 @@
11 from debian.deb822 import Deb822Dict
12 from zope.component import getUtility
13
14+from canonical.launchpad.interfaces.emailaddress import InvalidEmailAddress
15 from canonical.launchpad.interfaces.gpghandler import (
16 GPGVerificationError,
17 IGPGHandler,
18@@ -203,18 +204,25 @@
19 package = self._dict['Source']
20 version = self._dict['Version']
21 if self.policy.distroseries and self.policy.pocket:
22- policy_suite = ('%s/%s' % (self.policy.distroseries.name,
23- self.policy.pocket.name))
24+ policy_suite = '/'.join([
25+ self.policy.distroseries.name,
26+ self.policy.pocket.name,
27+ ])
28 else:
29 policy_suite = '(unknown)'
30- person = getUtility(IPersonSet).ensurePerson(
31- email, name, PersonCreationRationale.SOURCEPACKAGEUPLOAD,
32- comment=('when the %s_%s package was uploaded to %s'
33- % (package, version, policy_suite)))
34+ comment = "when the %s_%s package was uploaded to %s" % (
35+ package, version, policy_suite)
36+ try:
37+ person = getUtility(IPersonSet).ensurePerson(
38+ email, name, PersonCreationRationale.SOURCEPACKAGEUPLOAD,
39+ comment=comment)
40+ except InvalidEmailAddress:
41+ self.logger.info("Invalid email address: '%s'", email)
42+ person = None
43
44 if person is None:
45- raise UploadError("Unable to identify '%s':<%s> in launchpad"
46- % (name, email))
47+ raise UploadError(
48+ "Unable to identify '%s':<%s> in launchpad" % (name, email))
49
50 return {
51 "rfc822": rfc822,
52@@ -234,7 +242,8 @@
53 "Binary",
54 "Maintainer",
55 "Architecture",
56- "Files"])
57+ "Files",
58+ ])
59
60 known_fields = mandatory_fields.union(set([
61 "Build-Depends",
62@@ -288,7 +297,6 @@
63 raise EarlyReturnUploadError(
64 "Unsupported source format: %s" % self._dict['Format'])
65
66-
67 #
68 # Useful properties.
69 #
70
71=== modified file 'lib/lp/archiveuploader/tests/test_dscfile.py'
72--- lib/lp/archiveuploader/tests/test_dscfile.py 2011-03-21 12:55:50 +0000
73+++ lib/lp/archiveuploader/tests/test_dscfile.py 2011-10-20 12:11:38 +0000
74@@ -5,21 +5,24 @@
75
76 __metaclass__ = type
77
78+from collections import namedtuple
79 import os
80
81-from canonical.testing.layers import LaunchpadZopelessLayer
82+from canonical.testing.layers import LaunchpadZopelessLayer, ZopelessDatabaseLayer
83 from lp.archiveuploader.dscfile import (
84 cleanup_unpacked_dir,
85 DSCFile,
86 find_changelog,
87 find_copyright,
88 format_to_file_checker_map,
89+ SignableTagFile,
90 unpack_source,
91 )
92 from lp.archiveuploader.nascentuploadfile import UploadError
93 from lp.archiveuploader.tests import datadir
94 from lp.archiveuploader.uploadpolicy import BuildDaemonUploadPolicy
95 from lp.registry.interfaces.sourcepackage import SourcePackageFileType
96+from lp.registry.model.person import Person
97 from lp.services.log.logger import BufferLogger, DevNullLogger
98 from lp.soyuz.enums import SourcePackageFormat
99 from lp.testing import (
100@@ -316,3 +319,40 @@
101 os.chmod(unpacked_dir, 0600)
102 cleanup_unpacked_dir(unpacked_dir)
103 self.assertFalse(os.path.exists(unpacked_dir))
104+
105+
106+class TestSignableTagFile(TestCaseWithFactory):
107+ """Test `SignableTagFile`, a helper mixin."""
108+
109+ layer = ZopelessDatabaseLayer
110+
111+ def makeSignableTagFile(self):
112+ """Create a minimal `SignableTagFile` object."""
113+ FakePolicy = namedtuple(
114+ 'FakePolicy',
115+ ['pocket', 'distroseries', 'create_people'])
116+ tagfile = SignableTagFile()
117+ tagfile.logger = DevNullLogger()
118+ tagfile.policy = FakePolicy(None, None, create_people=True)
119+ tagfile._dict = {
120+ 'Source': 'arbitrary-source-package-name',
121+ 'Version': '1.0',
122+ }
123+ return tagfile
124+
125+ def test_parseAddress_finds_addressee(self):
126+ tagfile = self.makeSignableTagFile()
127+ email = self.factory.getUniqueEmailAddress()
128+ person = self.factory.makePerson(email=email)
129+ self.assertEqual(person, tagfile.parseAddress(email)['person'])
130+
131+ def test_parseAddress_creates_addressee_for_unknown_address(self):
132+ unknown_email = self.factory.getUniqueEmailAddress()
133+ results = self.makeSignableTagFile().parseAddress(unknown_email)
134+ self.assertEqual(unknown_email, results['email'])
135+ self.assertIsInstance(results['person'], Person)
136+
137+ def test_parseAddress_raises_UploadError_if_address_is_malformed(self):
138+ self.assertRaises(
139+ UploadError,
140+ self.makeSignableTagFile().parseAddress, "invalid@bad-address")