Merge ~cjwatson/launchpad:scripts-utilities-js-future-imports into launchpad:master
- Git
- lp:~cjwatson/launchpad
- scripts-utilities-js-future-imports
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | eb5bd07abacda491dac633d21173854485bda9a8 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:scripts-utilities-js-future-imports |
Merge into: | launchpad:master |
Diff against target: |
555 lines (+94/-170) 10 files modified
lib/lp/scripts/utilities/js/combinecss.py (+2/-0) lib/lp/scripts/utilities/js/combo.py (+6/-31) lib/lp/scripts/utilities/js/jsbuild.py (+26/-28) lib/lp/scripts/utilities/js/tests/test_combo.py (+3/-61) lib/lp/services/encoding.py (+19/-1) lib/lp/services/tests/test_encoding.py (+33/-3) lib/lp/services/webapp/servers.py (+1/-15) lib/lp/services/webapp/tests/test_servers.py (+1/-26) lib/lp/testing/layers.py (+1/-1) lib/lp/testing/pages.py (+2/-4) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+377136@code.launchpad.net |
Commit message
Convert lp.scripts.
Also remove the unused combo_app.
Description of the change
This requires care because of WSGI's requirement for native strings in some places.
To post a comment you must log in.
- eb5bd07... by Colin Watson
-
Remove lp.scripts.
utilities. js.combo. combo_app It was unused; we use convoy for this instead.
Revision history for this message
Colin Watson (cjwatson) wrote : | # |
Revision history for this message
William Grant (wgrant) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/scripts/utilities/js/combinecss.py b/lib/lp/scripts/utilities/js/combinecss.py |
2 | index 2047d25..1aa1072 100755 |
3 | --- a/lib/lp/scripts/utilities/js/combinecss.py |
4 | +++ b/lib/lp/scripts/utilities/js/combinecss.py |
5 | @@ -1,6 +1,8 @@ |
6 | # Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
7 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | |
9 | +from __future__ import absolute_import, print_function, unicode_literals |
10 | + |
11 | import os |
12 | |
13 | import scandir |
14 | diff --git a/lib/lp/scripts/utilities/js/combo.py b/lib/lp/scripts/utilities/js/combo.py |
15 | index 4d27293..f568bc2 100644 |
16 | --- a/lib/lp/scripts/utilities/js/combo.py |
17 | +++ b/lib/lp/scripts/utilities/js/combo.py |
18 | @@ -1,6 +1,8 @@ |
19 | # Copyright 2011-2019 Canonical Ltd. This software is licensed under the |
20 | # GNU Affero General Public License version 3 (see the file LICENSE). |
21 | |
22 | +from __future__ import absolute_import, print_function, unicode_literals |
23 | + |
24 | __metaclass__ = type |
25 | |
26 | import os |
27 | @@ -8,7 +10,7 @@ import urlparse |
28 | |
29 | from six.moves.urllib.parse import parse_qsl |
30 | |
31 | -from jsbuild import ( |
32 | +from lp.scripts.utilities.js.jsbuild import ( |
33 | CSSComboFile, |
34 | JSComboFile, |
35 | ) |
36 | @@ -32,7 +34,7 @@ def parse_qs(query): |
37 | return tuple([param for param, value in params]) |
38 | |
39 | |
40 | -def combine_files(fnames, root, resource_prefix="", |
41 | +def combine_files(fnames, root, resource_prefix=b"", |
42 | minify_css=True, rewrite_urls=True): |
43 | """Combine many files into one. |
44 | |
45 | @@ -56,33 +58,6 @@ def combine_files(fnames, root, resource_prefix="", |
46 | if not full.startswith(root) or not os.path.exists(full): |
47 | yield combo.get_comment("[missing]") |
48 | else: |
49 | - f = open(full, "r") |
50 | - content = f.read() |
51 | - f.close() |
52 | + with open(full, "rb") as f: |
53 | + content = f.read() |
54 | yield combo.filter_file_content(content, full) |
55 | - |
56 | - |
57 | -def combo_app(root, resource_prefix="", minify_css=True, rewrite_urls=True): |
58 | - """A simple YUI Combo Service WSGI app. |
59 | - |
60 | - Serves any files under C{root}, setting an appropriate |
61 | - C{Content-Type} header. |
62 | - """ |
63 | - root = os.path.abspath(root) |
64 | - |
65 | - def app(environ, start_response, root=root): |
66 | - fnames = parse_qs(environ["QUERY_STRING"]) |
67 | - content_type = "text/plain" |
68 | - if fnames: |
69 | - if fnames[0].endswith(".js"): |
70 | - content_type = "text/javascript" |
71 | - elif fnames[0].endswith(".css"): |
72 | - content_type = "text/css" |
73 | - else: |
74 | - start_response("404 Not Found", [("Content-Type", content_type)]) |
75 | - return ("Not Found",) |
76 | - start_response("200 OK", [("Content-Type", content_type)]) |
77 | - return combine_files(fnames, root, resource_prefix, |
78 | - minify_css, rewrite_urls) |
79 | - |
80 | - return app |
81 | diff --git a/lib/lp/scripts/utilities/js/jsbuild.py b/lib/lp/scripts/utilities/js/jsbuild.py |
82 | index 5499ea7..14841c0 100644 |
83 | --- a/lib/lp/scripts/utilities/js/jsbuild.py |
84 | +++ b/lib/lp/scripts/utilities/js/jsbuild.py |
85 | @@ -3,6 +3,8 @@ |
86 | |
87 | """build.py - Minifies and creates the JS build directory.""" |
88 | |
89 | +from __future__ import absolute_import, print_function, unicode_literals |
90 | + |
91 | __metaclass__ = type |
92 | __all__ = [ |
93 | 'CSSComboFile', |
94 | @@ -26,9 +28,9 @@ BUILD_DIR = os.path.normpath(os.path.join( |
95 | DEFAULT_SRC_DIR = os.path.normpath(os.path.join( |
96 | HERE, os.pardir, os.pardir, os.pardir, 'app', 'javascript')) |
97 | |
98 | -ESCAPE_STAR_PROPERTY_RE = re.compile(r'\*([a-zA-Z0-9_-]+):') |
99 | -UNESCAPE_STAR_PROPERTY_RE = re.compile(r'([a-zA-Z0-9_-]+)_ie_star_hack:') |
100 | -URL_RE = re.compile("url\([ \"\']*([^ \"\']+)[ \"\']*\)") |
101 | +ESCAPE_STAR_PROPERTY_RE = re.compile(br'\*([a-zA-Z0-9_-]+):') |
102 | +UNESCAPE_STAR_PROPERTY_RE = re.compile(br'([a-zA-Z0-9_-]+)_ie_star_hack:') |
103 | +URL_RE = re.compile(br"url\([ \"\']*([^ \"\']+)[ \"\']*\)") |
104 | |
105 | |
106 | def relative_path(from_file, to_file): |
107 | @@ -77,37 +79,33 @@ class ComboFile: |
108 | |
109 | def update(self): |
110 | """Update the file from its source files.""" |
111 | - target_fh = open(self.target_file, 'w') |
112 | - try: |
113 | + with open(self.target_file, 'wb') as target_fh: |
114 | for src_file in self.src_files: |
115 | self.log("Processing '%s'" % os.path.basename(src_file)) |
116 | target_fh.write(self.get_file_header(src_file)) |
117 | - fh = open(src_file) |
118 | - content = fh.read() |
119 | - fh.close() |
120 | + with open(src_file, 'rb') as fh: |
121 | + content = fh.read() |
122 | try: |
123 | target_fh.write( |
124 | self.filter_file_content(content, src_file)) |
125 | except Exception: |
126 | os.remove(self.target_file) |
127 | raise |
128 | - finally: |
129 | - target_fh.close() |
130 | |
131 | def get_comment(self, msg): |
132 | - """Return a string wrapped in a comment to be include in the output. |
133 | + """Return a byte string wrapped in a comment to include in the output. |
134 | |
135 | Can be used to help annotate the output file. |
136 | """ |
137 | - return '' |
138 | + return b'' |
139 | |
140 | def get_file_header(self, path): |
141 | - """Return a string to include before outputting a file. |
142 | + """Return a byte string to include before outputting a file. |
143 | |
144 | Can be used by subclasses to output a file reference in the combined |
145 | file. Default implementation returns nothing. |
146 | """ |
147 | - return '' |
148 | + return b'' |
149 | |
150 | def filter_file_content(self, file_content, path): |
151 | """Hook to process the file content before being combined.""" |
152 | @@ -122,13 +120,13 @@ class JSComboFile(ComboFile): |
153 | """ |
154 | |
155 | def get_comment(self, msg): |
156 | - return "// %s\n" % msg |
157 | + return b"// %s\n" % msg.encode("UTF-8") |
158 | |
159 | def get_file_header(self, path): |
160 | return self.get_comment(relative_path(self.target_file, path)) |
161 | |
162 | def filter_file_content(self, file_content, path): |
163 | - return file_content + '\n' |
164 | + return file_content + b'\n' |
165 | |
166 | |
167 | class CSSComboFile(ComboFile): |
168 | @@ -138,15 +136,15 @@ class CSSComboFile(ComboFile): |
169 | to the new location, and minify the result. |
170 | """ |
171 | |
172 | - def __init__(self, src_files, target_file, resource_prefix="", |
173 | + def __init__(self, src_files, target_file, resource_prefix=b"", |
174 | minify=True, rewrite_urls=True): |
175 | super(CSSComboFile, self).__init__(src_files, target_file) |
176 | - self.resource_prefix = resource_prefix.rstrip("/") |
177 | + self.resource_prefix = resource_prefix.rstrip(b"/") |
178 | self.minify = minify |
179 | self.rewrite_urls = rewrite_urls |
180 | |
181 | def get_comment(self, msg): |
182 | - return "/* %s */\n" % msg |
183 | + return b"/* %s */\n" % msg.encode("UTF-8") |
184 | |
185 | def get_file_header(self, path): |
186 | return self.get_comment(relative_path(self.target_file, path)) |
187 | @@ -164,18 +162,18 @@ class CSSComboFile(ComboFile): |
188 | def fix_relative_url(match): |
189 | url = match.group(1) |
190 | # Don't modify absolute URLs or 'data:' urls. |
191 | - if (url.startswith("http") or |
192 | - url.startswith("/") or |
193 | - url.startswith("data:")): |
194 | + if (url.startswith(b"http") or |
195 | + url.startswith(b"/") or |
196 | + url.startswith(b"data:")): |
197 | return match.group(0) |
198 | parts = relative_parts + url.split("/") |
199 | result = [] |
200 | for part in parts: |
201 | - if part == ".." and result and result[-1] != "..": |
202 | + if part == b".." and result and result[-1] != b"..": |
203 | result.pop(-1) |
204 | continue |
205 | result.append(part) |
206 | - return "url(%s)" % "/".join( |
207 | + return b"url(%s)" % b"/".join( |
208 | filter(None, [self.resource_prefix] + result)) |
209 | file_content = URL_RE.sub(fix_relative_url, file_content) |
210 | |
211 | @@ -186,16 +184,16 @@ class CSSComboFile(ComboFile): |
212 | cssutils.ser.prefs.useMinified() |
213 | |
214 | stylesheet = ESCAPE_STAR_PROPERTY_RE.sub( |
215 | - r'\1_ie_star_hack:', file_content) |
216 | + br'\1_ie_star_hack:', file_content) |
217 | settings.set('DXImageTransform.Microsoft', True) |
218 | parser = cssutils.CSSParser(raiseExceptions=True) |
219 | css = parser.parseString(stylesheet) |
220 | stylesheet = UNESCAPE_STAR_PROPERTY_RE.sub( |
221 | - r'*\1:', css.cssText) |
222 | - return stylesheet + "\n" |
223 | + br'*\1:', css.cssText) |
224 | + return stylesheet + b"\n" |
225 | finally: |
226 | cssutils.setSerializer(old_serializer) |
227 | - return file_content + "\n" |
228 | + return file_content + b"\n" |
229 | |
230 | |
231 | class Builder: |
232 | diff --git a/lib/lp/scripts/utilities/js/tests/test_combo.py b/lib/lp/scripts/utilities/js/tests/test_combo.py |
233 | index b1c8bb7..c745a6c 100644 |
234 | --- a/lib/lp/scripts/utilities/js/tests/test_combo.py |
235 | +++ b/lib/lp/scripts/utilities/js/tests/test_combo.py |
236 | @@ -1,17 +1,16 @@ |
237 | # Copyright 2011-2012 Canonical Ltd. This software is licensed under the |
238 | # GNU Affero General Public License version 3 (see the file LICENSE). |
239 | |
240 | +from __future__ import absolute_import, print_function, unicode_literals |
241 | + |
242 | __metaclass__ = type |
243 | |
244 | import os |
245 | import shutil |
246 | import tempfile |
247 | |
248 | -from paste.fixture import TestApp |
249 | - |
250 | from lp.scripts.utilities.js.combo import ( |
251 | combine_files, |
252 | - combo_app, |
253 | parse_url, |
254 | ) |
255 | from lp.testing import TestCase |
256 | @@ -435,7 +434,7 @@ class TestCombo(ComboTestBase): |
257 | "".join(combine_files(["widget/assets/skins/sam/widget.css", |
258 | "editor/assets/skins/sam/editor.css"], |
259 | root=test_dir, |
260 | - resource_prefix="/static/")).strip(), |
261 | + resource_prefix=b"/static/")).strip(), |
262 | expected) |
263 | |
264 | def test_missing_file_is_ignored(self): |
265 | @@ -513,60 +512,3 @@ class TestCombo(ComboTestBase): |
266 | self.assertEqual( |
267 | "".join(combine_files([hack], root=test_dir)).strip(), |
268 | expected) |
269 | - |
270 | - |
271 | -class TestWSGICombo(ComboTestBase): |
272 | - |
273 | - def setUp(self): |
274 | - super(TestWSGICombo, self).setUp() |
275 | - self.root = self.makeDir() |
276 | - self.app = TestApp(combo_app(self.root)) |
277 | - |
278 | - def test_combo_app_sets_content_type_for_js(self): |
279 | - """The WSGI App should set a proper Content-Type for Javascript.""" |
280 | - self.makeSampleFile( |
281 | - self.root, |
282 | - os.path.join("yui", "yui-min.js"), |
283 | - "** yui-min **") |
284 | - self.makeSampleFile( |
285 | - self.root, |
286 | - os.path.join("oop", "oop-min.js"), |
287 | - "** oop-min **") |
288 | - self.makeSampleFile( |
289 | - self.root, |
290 | - os.path.join("event-custom", "event-custom-min.js"), |
291 | - "** event-custom-min **") |
292 | - |
293 | - expected = "\n".join(("// yui/yui-min.js", |
294 | - "** yui-min **", |
295 | - "// oop/oop-min.js", |
296 | - "** oop-min **", |
297 | - "// event-custom/event-custom-min.js", |
298 | - "** event-custom-min **")) |
299 | - |
300 | - res = self.app.get("/?" + "&".join( |
301 | - ["yui/yui-min.js", |
302 | - "oop/oop-min.js", |
303 | - "event-custom/event-custom-min.js"]), status=200) |
304 | - self.assertEqual(res.headers, [("Content-Type", "text/javascript")]) |
305 | - self.assertEqual(res.body.strip(), expected) |
306 | - |
307 | - def test_combo_app_sets_content_type_for_css(self): |
308 | - """The WSGI App should set a proper Content-Type for CSS.""" |
309 | - self.makeSampleFile( |
310 | - self.root, |
311 | - os.path.join("widget", "skin", "sam", "widget.css"), |
312 | - "/* widget-skin-sam */") |
313 | - |
314 | - expected = "/* widget/skin/sam/widget.css */" |
315 | - |
316 | - res = self.app.get("/?" + "&".join( |
317 | - ["widget/skin/sam/widget.css"]), status=200) |
318 | - self.assertEqual(res.headers, [("Content-Type", "text/css")]) |
319 | - self.assertEqual(res.body.strip(), expected) |
320 | - |
321 | - def test_no_filename_gives_404(self): |
322 | - """If no filename is included, a 404 should be returned.""" |
323 | - res = self.app.get("/", status=404) |
324 | - self.assertEqual(res.headers, [("Content-Type", "text/plain")]) |
325 | - self.assertEqual(res.body, "Not Found") |
326 | diff --git a/lib/lp/services/encoding.py b/lib/lp/services/encoding.py |
327 | index 2483176..7feb308 100644 |
328 | --- a/lib/lp/services/encoding.py |
329 | +++ b/lib/lp/services/encoding.py |
330 | @@ -1,4 +1,4 @@ |
331 | -# Copyright 2009-2015 Canonical Ltd. This software is licensed under the |
332 | +# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
333 | # GNU Affero General Public License version 3 (see the file LICENSE). |
334 | |
335 | """Character encoding utilities""" |
336 | @@ -8,11 +8,14 @@ __all__ = [ |
337 | 'escape_nonascii_uniquely', |
338 | 'guess', |
339 | 'is_ascii_only', |
340 | + 'wsgi_native_string', |
341 | ] |
342 | |
343 | import codecs |
344 | import re |
345 | |
346 | +import six |
347 | + |
348 | |
349 | _boms = [ |
350 | (codecs.BOM_UTF16_BE, 'utf_16_be'), |
351 | @@ -212,3 +215,18 @@ def is_ascii_only(string): |
352 | return False |
353 | else: |
354 | return True |
355 | + |
356 | + |
357 | +def wsgi_native_string(s): |
358 | + """Make a native string suitable for use in WSGI. |
359 | + |
360 | + PEP 3333 requires environment variables to be native strings that |
361 | + contain only code points representable in ISO-8859-1. To support |
362 | + porting to Python 3 via an intermediate stage of Unicode literals in |
363 | + Python 2, we enforce this here. |
364 | + """ |
365 | + result = six.ensure_str(s, encoding='ISO-8859-1') |
366 | + if six.PY3 and isinstance(s, six.text_type): |
367 | + # Ensure we're limited to ISO-8859-1. |
368 | + result.encode('ISO-8859-1') |
369 | + return result |
370 | diff --git a/lib/lp/services/tests/test_encoding.py b/lib/lp/services/tests/test_encoding.py |
371 | index 1d91552..4a0e235 100644 |
372 | --- a/lib/lp/services/tests/test_encoding.py |
373 | +++ b/lib/lp/services/tests/test_encoding.py |
374 | @@ -1,14 +1,44 @@ |
375 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
376 | +# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
377 | # GNU Affero General Public License version 3 (see the file LICENSE). |
378 | |
379 | from doctest import ( |
380 | DocTestSuite, |
381 | ELLIPSIS, |
382 | ) |
383 | +import unittest |
384 | + |
385 | +import six |
386 | |
387 | import lp.services.encoding |
388 | +from lp.services.encoding import wsgi_native_string |
389 | +from lp.testing import TestCase |
390 | + |
391 | + |
392 | +class TestWSGINativeString(TestCase): |
393 | + |
394 | + def _toNative(self, s): |
395 | + if six.PY3: |
396 | + return s |
397 | + else: |
398 | + return s.encode('ISO-8859-1') |
399 | + |
400 | + def test_not_bytes_or_unicode(self): |
401 | + self.assertRaises(TypeError, wsgi_native_string, object()) |
402 | + |
403 | + def test_bytes_iso_8859_1(self): |
404 | + self.assertEqual( |
405 | + self._toNative(u'foo\xfe'), wsgi_native_string(b'foo\xfe')) |
406 | + |
407 | + def test_unicode_iso_8859_1(self): |
408 | + self.assertEqual( |
409 | + self._toNative(u'foo\xfe'), wsgi_native_string(u'foo\xfe')) |
410 | + |
411 | + def test_unicode_not_iso_8859_1(self): |
412 | + self.assertRaises(UnicodeEncodeError, wsgi_native_string, u'foo\u2014') |
413 | |
414 | |
415 | def test_suite(): |
416 | - suite = DocTestSuite(lp.services.encoding, optionflags=ELLIPSIS) |
417 | - return suite |
418 | + return unittest.TestSuite(( |
419 | + unittest.TestLoader().loadTestsFromName(__name__), |
420 | + DocTestSuite(lp.services.encoding, optionflags=ELLIPSIS), |
421 | + )) |
422 | diff --git a/lib/lp/services/webapp/servers.py b/lib/lp/services/webapp/servers.py |
423 | index c9fc0e6..e99fb05 100644 |
424 | --- a/lib/lp/services/webapp/servers.py |
425 | +++ b/lib/lp/services/webapp/servers.py |
426 | @@ -64,6 +64,7 @@ from lp.app import versioninfo |
427 | from lp.app.errors import UnexpectedFormData |
428 | import lp.layers |
429 | from lp.services.config import config |
430 | +from lp.services.encoding import wsgi_native_string |
431 | from lp.services.features import get_relevant_feature_controller |
432 | from lp.services.features.flags import NullFeatureController |
433 | from lp.services.feeds.interfaces.application import IFeedsApplication |
434 | @@ -534,21 +535,6 @@ def get_query_string_params(request): |
435 | return decoded_qs |
436 | |
437 | |
438 | -def wsgi_native_string(s): |
439 | - """Make a native string suitable for use in WSGI. |
440 | - |
441 | - PEP 3333 requires environment variables to be native strings that |
442 | - contain only code points representable in ISO-8859-1. To support |
443 | - porting to Python 3 via an intermediate stage of Unicode literals in |
444 | - Python 2, we enforce this here. |
445 | - """ |
446 | - result = six.ensure_str(s, encoding='ISO-8859-1') |
447 | - if six.PY3 and isinstance(s, six.text_type): |
448 | - # Ensure we're limited to ISO-8859-1. |
449 | - result.encode('ISO-8859-1') |
450 | - return result |
451 | - |
452 | - |
453 | class LaunchpadBrowserRequestMixin: |
454 | """Provides methods used for both API and web browser requests.""" |
455 | |
456 | diff --git a/lib/lp/services/webapp/tests/test_servers.py b/lib/lp/services/webapp/tests/test_servers.py |
457 | index 79595e6..02afdb7 100644 |
458 | --- a/lib/lp/services/webapp/tests/test_servers.py |
459 | +++ b/lib/lp/services/webapp/tests/test_servers.py |
460 | @@ -1,4 +1,4 @@ |
461 | -# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
462 | +# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
463 | # GNU Affero General Public License version 3 (see the file LICENSE). |
464 | |
465 | __metaclass__ = type |
466 | @@ -21,7 +21,6 @@ from lazr.restful.testing.webservice import ( |
467 | IGenericEntry, |
468 | WebServiceTestCase, |
469 | ) |
470 | -import six |
471 | from zope.component import ( |
472 | getGlobalSiteManager, |
473 | getUtility, |
474 | @@ -48,7 +47,6 @@ from lp.services.webapp.servers import ( |
475 | WebServicePublication, |
476 | WebServiceRequestPublicationFactory, |
477 | WebServiceTestRequest, |
478 | - wsgi_native_string, |
479 | ) |
480 | from lp.testing import ( |
481 | EventRecorder, |
482 | @@ -355,29 +353,6 @@ class TestWebServiceRequest(WebServiceTestCase): |
483 | request.response.getHeader('Vary'), 'Accept') |
484 | |
485 | |
486 | -class TestWSGINativeString(TestCase): |
487 | - |
488 | - def _toNative(self, s): |
489 | - if six.PY3: |
490 | - return s |
491 | - else: |
492 | - return s.encode('ISO-8859-1') |
493 | - |
494 | - def test_not_bytes_or_unicode(self): |
495 | - self.assertRaises(TypeError, wsgi_native_string, object()) |
496 | - |
497 | - def test_bytes_iso_8859_1(self): |
498 | - self.assertEqual( |
499 | - self._toNative(u'foo\xfe'), wsgi_native_string(b'foo\xfe')) |
500 | - |
501 | - def test_unicode_iso_8859_1(self): |
502 | - self.assertEqual( |
503 | - self._toNative(u'foo\xfe'), wsgi_native_string(u'foo\xfe')) |
504 | - |
505 | - def test_unicode_not_iso_8859_1(self): |
506 | - self.assertRaises(UnicodeEncodeError, wsgi_native_string, u'foo\u2014') |
507 | - |
508 | - |
509 | class TestBasicLaunchpadRequest(TestCase): |
510 | """Tests for the base request class""" |
511 | |
512 | diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py |
513 | index 59617f5..65fb63e 100644 |
514 | --- a/lib/lp/testing/layers.py |
515 | +++ b/lib/lp/testing/layers.py |
516 | @@ -121,6 +121,7 @@ from lp.services.config.fixture import ( |
517 | ) |
518 | from lp.services.database.interfaces import IStore |
519 | from lp.services.database.sqlbase import session_store |
520 | +from lp.services.encoding import wsgi_native_string |
521 | from lp.services.job.tests import celery_worker |
522 | from lp.services.librarian.model import LibraryFileAlias |
523 | from lp.services.librarianserver.testing.server import LibrarianServerFixture |
524 | @@ -147,7 +148,6 @@ from lp.services.webapp.interfaces import IOpenLaunchBag |
525 | from lp.services.webapp.servers import ( |
526 | LaunchpadAccessLogger, |
527 | register_launchpad_request_publication_factories, |
528 | - wsgi_native_string, |
529 | ) |
530 | import lp.services.webapp.session |
531 | from lp.testing import ( |
532 | diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py |
533 | index 79bfba5..1c23980 100644 |
534 | --- a/lib/lp/testing/pages.py |
535 | +++ b/lib/lp/testing/pages.py |
536 | @@ -79,6 +79,7 @@ from lp.services.beautifulsoup import ( |
537 | SoupStrainer, |
538 | ) |
539 | from lp.services.config import config |
540 | +from lp.services.encoding import wsgi_native_string |
541 | from lp.services.oauth.interfaces import ( |
542 | IOAuthConsumerSet, |
543 | OAUTH_REALM, |
544 | @@ -86,10 +87,7 @@ from lp.services.oauth.interfaces import ( |
545 | from lp.services.webapp import canonical_url |
546 | from lp.services.webapp.authorization import LaunchpadPermissiveSecurityPolicy |
547 | from lp.services.webapp.interfaces import OAuthPermission |
548 | -from lp.services.webapp.servers import ( |
549 | - LaunchpadTestRequest, |
550 | - wsgi_native_string, |
551 | - ) |
552 | +from lp.services.webapp.servers import LaunchpadTestRequest |
553 | from lp.services.webapp.url import urlsplit |
554 | from lp.testing import ( |
555 | ANONYMOUS, |
At William's suggestion, I removed combo_app entirely, since it's unused. We might as well keep the rest of this port, though; I think the various type changes are still correct, and wsgi_native_ string' s new home is somewhat more appropriate.