Merge lp:~thumper/launchpad/fix-unicode-path-oops into lp:launchpad

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 12791
Proposed branch: lp:~thumper/launchpad/fix-unicode-path-oops
Merge into: lp:launchpad
Prerequisite: lp:~thumper/launchpad/fix-dud-referer
Diff against target: 179 lines (+48/-28)
8 files modified
lib/canonical/launchpad/doc/renamed-view.txt (+1/-1)
lib/canonical/launchpad/helpers.py (+0/-20)
lib/canonical/launchpad/rest/configuration.py (+4/-0)
lib/canonical/launchpad/webapp/publisher.py (+6/-1)
lib/canonical/launchpad/webapp/tests/test_publication.py (+13/-3)
lib/lp/services/encoding.py (+21/-0)
lib/lp/services/mail/sendmail.py (+2/-2)
lib/lp/services/mail/tests/test_sendmail.py (+1/-1)
To merge this branch: bzr merge lp:~thumper/launchpad/fix-unicode-path-oops
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+56688@code.launchpad.net

Commit message

[r=lifeless][bug=200572]

Description of the change

This branch catches a couple of unicode issues.

One is where we were trying to look for something in storm with a potentially bad byte ordering, and the second was when we got a NotFoundError, the rendering of the page checked for a webservice request, which tryied to UTF-8 decode the string, which again failed.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

We shouldn't really be raising a NotFound there *either*, but this is an improvement.

review: Approve
Revision history for this message
Tim Penhey (thumper) wrote :

Why not?

Revision history for this message
Robert Collins (lifeless) wrote :

On Fri, Apr 8, 2011 at 1:40 PM, Tim Penhey <email address hidden> wrote:
> Why not?

Because we shouldn't be raising an OOPS for any 404 unless its coming
from launchpad/a canonical property which we can fix the url on.

There is a separate bug for that, so it doesn't need to affect your work today.

-Rob

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/doc/renamed-view.txt'
2--- lib/canonical/launchpad/doc/renamed-view.txt 2010-11-09 23:38:44 +0000
3+++ lib/canonical/launchpad/doc/renamed-view.txt 2011-04-10 23:56:45 +0000
4@@ -89,7 +89,7 @@
5 >>> view.publishTraverse(request, u'foo')
6 Traceback (most recent call last):
7 ...
8- NotFound: Object: u'foo', name: <Distribution 'Ubuntu' (ubuntu)>
9+ NotFound: Object: <Distribution 'Ubuntu' (ubuntu)>, name: u'foo'
10
11
12 == Registering from ZCML ==
13
14=== modified file 'lib/canonical/launchpad/helpers.py'
15--- lib/canonical/launchpad/helpers.py 2011-03-30 23:37:11 +0000
16+++ lib/canonical/launchpad/helpers.py 2011-04-10 23:56:45 +0000
17@@ -482,26 +482,6 @@
18 return open(fullpath).read()
19
20
21-def is_ascii_only(string):
22- """Ensure that the string contains only ASCII characters.
23-
24- >>> is_ascii_only(u'ascii only')
25- True
26- >>> is_ascii_only('ascii only')
27- True
28- >>> is_ascii_only('\xf4')
29- False
30- >>> is_ascii_only(u'\xf4')
31- False
32- """
33- try:
34- string.encode('ascii')
35- except UnicodeError:
36- return False
37- else:
38- return True
39-
40-
41 def truncate_text(text, max_length):
42 """Return a version of string no longer than max_length characters.
43
44
45=== modified file 'lib/canonical/launchpad/rest/configuration.py'
46--- lib/canonical/launchpad/rest/configuration.py 2011-03-22 11:54:10 +0000
47+++ lib/canonical/launchpad/rest/configuration.py 2011-04-10 23:56:45 +0000
48@@ -60,6 +60,10 @@
49
50 def createRequest(self, body_instream, environ):
51 """See `IWebServiceConfiguration`."""
52+ # The request is going to try to decode the 'PATH_INFO' using utf-8,
53+ # so if it is currently unicode, encode it.
54+ if isinstance(environ.get('PATH_INFO'), unicode):
55+ environ['PATH_INFO'] = environ['PATH_INFO'].encode('utf-8')
56 request = WebServiceClientRequest(body_instream, environ)
57 request.setPublication(WebServicePublication(None))
58 return request
59
60=== modified file 'lib/canonical/launchpad/webapp/publisher.py'
61--- lib/canonical/launchpad/webapp/publisher.py 2011-03-21 21:33:25 +0000
62+++ lib/canonical/launchpad/webapp/publisher.py 2011-04-10 23:56:45 +0000
63@@ -70,6 +70,7 @@
64 from canonical.launchpad.webapp.vhosts import allvhosts
65 from canonical.lazr.utils import get_current_browser_request
66 from lp.app.errors import NotFoundError
67+from lp.services.encoding import is_ascii_only
68
69 # HTTP Status code constants - define as appropriate.
70 HTTP_MOVED_PERMANENTLY = 301
71@@ -657,6 +658,10 @@
72 This needs moving into the publication component, once it has been
73 refactored.
74 """
75+ # Launchpad only produces ascii URLs. If the name is not ascii, we
76+ # can say nothing is found here.
77+ if not is_ascii_only(name):
78+ raise NotFound(self.context, name)
79 nextobj = self._publishTraverse(request, name)
80 getUtility(IOpenLaunchBag).add(nextobj)
81 return nextobj
82@@ -839,7 +844,7 @@
83
84 def publishTraverse(self, request, name):
85 """See zope.publisher.interfaces.browser.IBrowserPublisher."""
86- raise NotFound(name, self.context)
87+ raise NotFound(self.context, name)
88
89 def browserDefault(self, request):
90 """See zope.publisher.interfaces.browser.IBrowserPublisher."""
91
92=== modified file 'lib/canonical/launchpad/webapp/tests/test_publication.py'
93--- lib/canonical/launchpad/webapp/tests/test_publication.py 2011-04-06 05:13:57 +0000
94+++ lib/canonical/launchpad/webapp/tests/test_publication.py 2011-04-10 23:56:45 +0000
95@@ -455,6 +455,16 @@
96 self.assertEqual('NotFound', self.oopses[0].type)
97
98
99-def test_suite():
100- suite = unittest.TestLoader().loadTestsFromName(__name__)
101- return suite
102+class TestUnicodePath(TestCaseWithFactory):
103+
104+ layer = DatabaseFunctionalLayer
105+
106+ def test_non_ascii_url(self):
107+ # The only oops should be a NotFound.
108+ browser = self.getUserBrowser()
109+ self.assertRaises(
110+ NotFound,
111+ browser.open,
112+ 'http://launchpad.dev/%ED%B4%B5')
113+ self.assertEqual(1, len(self.oopses))
114+ self.assertEqual('NotFound', self.oopses[0].type)
115
116=== modified file 'lib/lp/services/encoding.py'
117--- lib/lp/services/encoding.py 2011-02-17 17:02:54 +0000
118+++ lib/lp/services/encoding.py 2011-04-10 23:56:45 +0000
119@@ -8,6 +8,7 @@
120 'ascii_smash',
121 'escape_nonascii_uniquely',
122 'guess',
123+ 'is_ascii_only',
124 ]
125
126 import re
127@@ -387,3 +388,23 @@
128 def quote(match):
129 return '\\x%x' % ord(match.group(0))
130 return nonascii_regex.sub(quote, bogus_string)
131+
132+
133+def is_ascii_only(string):
134+ """Ensure that the string contains only ASCII characters.
135+
136+ >>> is_ascii_only(u'ascii only')
137+ True
138+ >>> is_ascii_only('ascii only')
139+ True
140+ >>> is_ascii_only('\xf4')
141+ False
142+ >>> is_ascii_only(u'\xf4')
143+ False
144+ """
145+ try:
146+ string.encode('ascii')
147+ except UnicodeError:
148+ return False
149+ else:
150+ return True
151
152=== modified file 'lib/lp/services/mail/sendmail.py'
153--- lib/lp/services/mail/sendmail.py 2010-12-14 18:54:38 +0000
154+++ lib/lp/services/mail/sendmail.py 2011-04-10 23:56:45 +0000
155@@ -50,9 +50,9 @@
156 from zope.sendmail.interfaces import IMailDelivery
157
158 from canonical.config import config
159+from canonical.lp import isZopeless
160 from lp.app import versioninfo
161-from canonical.launchpad.helpers import is_ascii_only
162-from canonical.lp import isZopeless
163+from lp.services.encoding import is_ascii_only
164 from lp.services.mail.stub import TestMailer
165 from lp.services.timeline.requesttimeline import get_request_timeline
166
167
168=== modified file 'lib/lp/services/mail/tests/test_sendmail.py'
169--- lib/lp/services/mail/tests/test_sendmail.py 2010-09-04 05:37:08 +0000
170+++ lib/lp/services/mail/tests/test_sendmail.py 2011-04-10 23:56:45 +0000
171@@ -7,7 +7,7 @@
172 from email.Message import Message
173 import unittest
174
175-from canonical.launchpad.helpers import is_ascii_only
176+from lp.services.encoding import is_ascii_only
177 from lp.services.mail import sendmail
178 from lp.services.mail.sendmail import MailController
179 from lp.testing import TestCase