Merge lp:~cjwatson/lazr.restful/py3-unicode into lp:lazr.restful

Proposed by Colin Watson
Status: Merged
Merged at revision: 253
Proposed branch: lp:~cjwatson/lazr.restful/py3-unicode
Merge into: lp:lazr.restful
Diff against target: 396 lines (+52/-40)
9 files modified
src/lazr/restful/_operation.py (+3/-1)
src/lazr/restful/_resource.py (+13/-13)
src/lazr/restful/declarations.py (+4/-2)
src/lazr/restful/docs/webservice.rst (+3/-2)
src/lazr/restful/marshallers.py (+13/-11)
src/lazr/restful/testing/helpers.py (+3/-1)
src/lazr/restful/testing/webservice.py (+6/-5)
src/lazr/restful/tests/test_webservice.py (+5/-4)
src/lazr/restful/utils.py (+2/-1)
To merge this branch: bzr merge lp:~cjwatson/lazr.restful/py3-unicode
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+387891@code.launchpad.net

Commit message

Port simple uses of str/unicode/basestring to Python 3.

Description of the change

Most cases are fairly obvious, but we need to pay some attention to whether str is intended to be a byte string or a native string. basestring normally means "native string or text" (i.e. six.string_types), but in a few places it means "bytes or text" instead: in particular, the return value from EntryResource.do_GET and CollectionResource.do_GET may be either.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/restful/_operation.py'
--- src/lazr/restful/_operation.py 2020-02-04 11:52:59 +0000
+++ src/lazr/restful/_operation.py 2020-07-22 22:45:07 +0000
@@ -5,6 +5,7 @@
5from __future__ import absolute_import, print_function5from __future__ import absolute_import, print_function
66
7import simplejson7import simplejson
8import six
89
9from zope.component import getMultiAdapter, getUtility, queryMultiAdapter10from zope.component import getMultiAdapter, getUtility, queryMultiAdapter
10from zope.event import notify11from zope.event import notify
@@ -145,7 +146,8 @@
145 # has its response batched.146 # has its response batched.
146 return True147 return True
147148
148 if zope_isinstance(result, (basestring, dict, set, list, tuple)):149 if zope_isinstance(
150 result, (bytes, six.text_type, dict, set, list, tuple)):
149 # Ordinary Python data structures generally are not151 # Ordinary Python data structures generally are not
150 # batched.152 # batched.
151 return False153 return False
152154
=== modified file 'src/lazr/restful/_resource.py'
--- src/lazr/restful/_resource.py 2020-06-30 15:52:03 +0000
+++ src/lazr/restful/_resource.py 2020-07-22 22:45:07 +0000
@@ -151,12 +151,12 @@
151151
152def decode_value(value):152def decode_value(value):
153 """Return a unicode value curresponding to `value`."""153 """Return a unicode value curresponding to `value`."""
154 if isinstance(value, unicode):154 if isinstance(value, six.text_type):
155 return value155 return value
156 elif isinstance(value, str):156 elif isinstance(value, bytes):
157 return value.decode("utf-8")157 return value.decode("utf-8")
158 else:158 else:
159 return unicode(value)159 return six.text_type(value)
160160
161161
162def encode_value(value):162def encode_value(value):
@@ -164,17 +164,17 @@
164164
165 Non-unicode strings are assumed to be UTF-8 already.165 Non-unicode strings are assumed to be UTF-8 already.
166 """166 """
167 if isinstance(value, unicode):167 if isinstance(value, six.text_type):
168 return value.encode("utf-8")168 return value.encode("utf-8")
169 elif isinstance(value, str):169 elif isinstance(value, bytes):
170 return value170 return value
171 else:171 else:
172 return str(value)172 return bytes(value)
173173
174174
175def _default_html_renderer(value):175def _default_html_renderer(value):
176 """The default technique for rendering a value as an HTML snippet."""176 """The default technique for rendering a value as an HTML snippet."""
177 return cgi.escape(unicode(value))177 return cgi.escape(six.text_type(value))
178178
179179
180@adapter(Interface, IField, IWebServiceClientRequest)180@adapter(Interface, IField, IWebServiceClientRequest)
@@ -701,7 +701,7 @@
701 :return: The result of the operation: either a string or an701 :return: The result of the operation: either a string or an
702 object that needs to be serialized to JSON.702 object that needs to be serialized to JSON.
703 """703 """
704 if not isinstance(operation_name, basestring):704 if not isinstance(operation_name, six.string_types):
705 self.request.response.setStatus(400)705 self.request.response.setStatus(400)
706 return "Expected a single operation: %r" % (operation_name,)706 return "Expected a single operation: %r" % (operation_name,)
707707
@@ -727,7 +727,7 @@
727 :return: The result of the operation: either a string or an727 :return: The result of the operation: either a string or an
728 object that needs to be serialized to JSON.728 object that needs to be serialized to JSON.
729 """729 """
730 if not isinstance(operation_name, basestring):730 if not isinstance(operation_name, six.string_types):
731 self.request.response.setStatus(400)731 self.request.response.setStatus(400)
732 return "Expected a single operation: %r" % (operation_name,)732 return "Expected a single operation: %r" % (operation_name,)
733733
@@ -1163,7 +1163,7 @@
1163 # instead of whatever object the raise site1163 # instead of whatever object the raise site
1164 # thought would be a good idea.1164 # thought would be a good idea.
1165 if (len(e.args) > 0 and1165 if (len(e.args) > 0 and
1166 isinstance(e.args[0], basestring)):1166 isinstance(e.args[0], six.string_types)):
1167 error = e.args[0]1167 error = e.args[0]
1168 else:1168 else:
1169 error = "Constraint not satisfied."1169 error = "Constraint not satisfied."
@@ -1311,7 +1311,7 @@
1311 # generating the representation might itself change across1311 # generating the representation might itself change across
1312 # versions.1312 # versions.
1313 revno = getUtility(IWebServiceConfiguration).code_revision1313 revno = getUtility(IWebServiceConfiguration).code_revision
1314 return [core.encode('utf-8') for core in [revno, unicode(value)]]1314 return [core.encode('utf-8') for core in [revno, six.text_type(value)]]
13151315
1316 def _representation(self, media_type):1316 def _representation(self, media_type):
1317 """Create a representation of the field value."""1317 """Create a representation of the field value."""
@@ -1547,7 +1547,7 @@
1547 operation_name = self.request.form.pop('ws.op', None)1547 operation_name = self.request.form.pop('ws.op', None)
1548 if operation_name is not None:1548 if operation_name is not None:
1549 result = self.handleCustomGET(operation_name)1549 result = self.handleCustomGET(operation_name)
1550 if isinstance(result, basestring):1550 if isinstance(result, (bytes, six.text_type)):
1551 # The custom operation took care of everything and1551 # The custom operation took care of everything and
1552 # just needs this string served to the client.1552 # just needs this string served to the client.
1553 return result1553 return result
@@ -1788,7 +1788,7 @@
1788 operation_name = self.request.form.pop('ws.op', None)1788 operation_name = self.request.form.pop('ws.op', None)
1789 if operation_name is not None:1789 if operation_name is not None:
1790 result = self.handleCustomGET(operation_name)1790 result = self.handleCustomGET(operation_name)
1791 if isinstance(result, str) or isinstance(result, unicode):1791 if isinstance(result, (bytes, six.text_type)):
1792 # The custom operation took care of everything and1792 # The custom operation took care of everything and
1793 # just needs this string served to the client.1793 # just needs this string served to the client.
1794 return result1794 return result
17951795
=== modified file 'src/lazr/restful/declarations.py'
--- src/lazr/restful/declarations.py 2020-02-05 10:46:46 +0000
+++ src/lazr/restful/declarations.py 2020-07-22 22:45:07 +0000
@@ -47,6 +47,7 @@
47import itertools47import itertools
48import sys48import sys
4949
50import six
50from zope.component import getUtility, getGlobalSiteManager51from zope.component import getUtility, getGlobalSiteManager
51from zope.interface import classImplements52from zope.interface import classImplements
52from zope.interface.advice import addClassAdvisor53from zope.interface.advice import addClassAdvisor
@@ -738,7 +739,8 @@
738 # zope schema are expected to be unicode, whereas it's739 # zope schema are expected to be unicode, whereas it's
739 # really possible that the method's default is a simple740 # really possible that the method's default is a simple
740 # string.741 # string.
741 if isinstance(default, str) and IText.providedBy(param_def):742 if (six.PY2 and
743 isinstance(default, str) and IText.providedBy(param_def)):
742 default = unicode(default)744 default = unicode(default)
743 param_def.default = default745 param_def.default = default
744 param_def.required = False746 param_def.required = False
@@ -1805,5 +1807,5 @@
1805 # runtime. Use a generic string that won't conflict with a1807 # runtime. Use a generic string that won't conflict with a
1806 # real version string.1808 # real version string.
1807 version = "__Earliest"1809 version = "__Earliest"
1808 name = "%s_%s" % (base_name, version.encode('utf8'))1810 name = "%s_%s" % (base_name, six.ensure_str(version))
1809 return make_identifier_safe(name)1811 return make_identifier_safe(name)
18101812
=== modified file 'src/lazr/restful/docs/webservice.rst'
--- src/lazr/restful/docs/webservice.rst 2020-02-04 13:17:32 +0000
+++ src/lazr/restful/docs/webservice.rst 2020-07-22 22:45:07 +0000
@@ -1092,8 +1092,9 @@
1092'home page' for the web service.1092'home page' for the web service.
10931093
1094 >>> import simplejson1094 >>> import simplejson
1095 >>> import six
1095 >>> response = app(request)1096 >>> response = app(request)
1096 >>> representation = simplejson.loads(unicode(response))1097 >>> representation = simplejson.loads(six.text_type(response))
10971098
1098 >>> representation["authors_collection_link"]1099 >>> representation["authors_collection_link"]
1099 u'http://api.cookbooks.dev/beta/authors'1100 u'http://api.cookbooks.dev/beta/authors'
@@ -1168,7 +1169,7 @@
11681169
1169 >>> def load_json(s):1170 >>> def load_json(s):
1170 ... """Convert a JSON string to Unicode and then load it."""1171 ... """Convert a JSON string to Unicode and then load it."""
1171 ... return simplejson.loads(unicode(s))1172 ... return simplejson.loads(six.text_type(s))
11721173
1173 >>> representation = load_json(collection())1174 >>> representation = load_json(collection())
1174 >>> representation['resource_type_link']1175 >>> representation['resource_type_link']
11751176
=== modified file 'src/lazr/restful/marshallers.py'
--- src/lazr/restful/marshallers.py 2020-06-30 15:52:03 +0000
+++ src/lazr/restful/marshallers.py 2020-07-22 22:45:07 +0000
@@ -29,6 +29,7 @@
29from StringIO import StringIO29from StringIO import StringIO
3030
31import simplejson31import simplejson
32import six
32from six.moves.urllib.parse import unquote33from six.moves.urllib.parse import unquote
3334
34from zope.datetime import (35from zope.datetime import (
@@ -89,7 +90,7 @@
89 request_host = full_request_host90 request_host = full_request_host
90 request_port = default_port91 request_port = default_port
9192
92 if not isinstance(url, basestring):93 if not isinstance(url, six.string_types):
93 raise ValueError(u"got '%s', expected string: %r" % (94 raise ValueError(u"got '%s', expected string: %r" % (
94 type(url).__name__, url))95 type(url).__name__, url))
95 if url.startswith('/'):96 if url.startswith('/'):
@@ -178,10 +179,10 @@
178 if value != '':179 if value != '':
179 try:180 try:
180 v = value181 v = value
181 if isinstance(v, str):182 if isinstance(v, bytes):
182 v = v.decode('utf8') # assume utf8183 v = v.decode('utf8') # assume utf8
183 elif not isinstance(v, unicode):184 elif not isinstance(v, six.text_type):
184 v = unicode(v)185 v = six.text_type(v)
185 value = simplejson.loads(v)186 value = simplejson.loads(v)
186 except (ValueError, TypeError):187 except (ValueError, TypeError):
187 # Pass the value as is. This saves client from having to encode188 # Pass the value as is. This saves client from having to encode
@@ -267,7 +268,7 @@
267class BytesFieldMarshaller(SimpleFieldMarshaller):268class BytesFieldMarshaller(SimpleFieldMarshaller):
268 """FieldMarshaller for IBytes field."""269 """FieldMarshaller for IBytes field."""
269270
270 _type = str271 _type = bytes
271 _type_error_message = 'not a string: %r'272 _type_error_message = 'not a string: %r'
272273
273 @property274 @property
@@ -295,8 +296,8 @@
295 if safe_hasattr(value, 'seek'):296 if safe_hasattr(value, 'seek'):
296 value.seek(0)297 value.seek(0)
297 value = value.read()298 value = value.read()
298 elif not isinstance(value, basestring):299 elif not isinstance(value, (bytes, six.text_type)):
299 value = str(value)300 value = bytes(value)
300 else:301 else:
301 # Leave string conversion to _marshall_from_json_data.302 # Leave string conversion to _marshall_from_json_data.
302 pass303 pass
@@ -307,7 +308,7 @@
307308
308 Convert all strings to byte strings.309 Convert all strings to byte strings.
309 """310 """
310 if isinstance(value, unicode):311 if isinstance(value, six.text_type):
311 value = value.encode('utf-8')312 value = value.encode('utf-8')
312 return super(313 return super(
313 BytesFieldMarshaller, self)._marshall_from_json_data(value)314 BytesFieldMarshaller, self)._marshall_from_json_data(value)
@@ -316,7 +317,7 @@
316class TextFieldMarshaller(SimpleFieldMarshaller):317class TextFieldMarshaller(SimpleFieldMarshaller):
317 """FieldMarshaller for IText fields."""318 """FieldMarshaller for IText fields."""
318319
319 _type = unicode320 _type = six.text_type
320 _type_error_message = 'not a unicode string: %r'321 _type_error_message = 'not a unicode string: %r'
321322
322 def _marshall_from_request(self, value):323 def _marshall_from_request(self, value):
@@ -324,7 +325,7 @@
324325
325 Converts the value to unicode.326 Converts the value to unicode.
326 """327 """
327 value = unicode(value)328 value = six.text_type(value)
328 return super(TextFieldMarshaller, self)._marshall_from_request(value)329 return super(TextFieldMarshaller, self)._marshall_from_request(value)
329330
330331
@@ -366,7 +367,8 @@
366 Looks up the value as a token in the vocabulary.367 Looks up the value as a token in the vocabulary.
367 """368 """
368 try:369 try:
369 return self.field.vocabulary.getTermByToken(str(value)).value370 return self.field.vocabulary.getTermByToken(
371 six.text_type(value)).value
370 except LookupError:372 except LookupError:
371 raise ValueError(u"%r isn't a valid token" % value)373 raise ValueError(u"%r isn't a valid token" % value)
372374
373375
=== modified file 'src/lazr/restful/testing/helpers.py'
--- src/lazr/restful/testing/helpers.py 2020-02-04 11:52:59 +0000
+++ src/lazr/restful/testing/helpers.py 2020-07-22 22:45:07 +0000
@@ -50,7 +50,9 @@
5050
51 :param response: an httplib HTTPResponse object.51 :param response: an httplib HTTPResponse object.
52 """52 """
53 response_unicode = str(response).decode("utf-8")53 response_unicode = str(response)
54 if isinstance(response_unicode, bytes): # Python 2
55 response_unicode = response_unicode.decode("utf-8")
54 return encode_unicode(response_unicode)56 return encode_unicode(response_unicode)
5557
5658
5759
=== modified file 'src/lazr/restful/testing/webservice.py'
--- src/lazr/restful/testing/webservice.py 2020-02-04 21:53:16 +0000
+++ src/lazr/restful/testing/webservice.py 2020-07-22 22:45:07 +0000
@@ -28,6 +28,7 @@
2828
29import wsgi_intercept29import wsgi_intercept
3030
31import six
31from six.moves.urllib.parse import (32from six.moves.urllib.parse import (
32 quote,33 quote,
33 urlencode,34 urlencode,
@@ -122,7 +123,7 @@
122 if result is None:123 if result is None:
123 result = ''124 result = ''
124125
125 if not isinstance(result, basestring):126 if not isinstance(result, six.string_types):
126 raise ValueError('only strings and None results are handled')127 raise ValueError('only strings and None results are handled')
127128
128 self.result = result129 self.result = result
@@ -209,7 +210,7 @@
209 """210 """
210211
211 def __init__(self, global_config, publication, **options):212 def __init__(self, global_config, publication, **options):
212 if isinstance(publication, basestring):213 if isinstance(publication, six.string_types):
213 Application.__init__(self, global_config, publication, **options)214 Application.__init__(self, global_config, publication, **options)
214 else:215 else:
215 self.publication = publication(global_config, **options)216 self.publication = publication(global_config, **options)
@@ -334,7 +335,7 @@
334 # To be properly marshalled all values must be strings or converted to335 # To be properly marshalled all values must be strings or converted to
335 # JSON.336 # JSON.
336 for key, value in args.items():337 for key, value in args.items():
337 if not isinstance(value, basestring):338 if not isinstance(value, six.string_types):
338 args[key] = simplejson.dumps(value)339 args[key] = simplejson.dumps(value)
339 return urlencode(args)340 return urlencode(args)
340341
@@ -363,7 +364,7 @@
363364
364 This may mean turning the value into a JSON string.365 This may mean turning the value into a JSON string.
365 """366 """
366 if not isinstance(value, basestring):367 if not isinstance(value, six.string_types):
367 value = simplejson.dumps(value)368 value = simplejson.dumps(value)
368 return quote(value)369 return quote(value)
369370
@@ -408,7 +409,7 @@
408 def jsonBody(self):409 def jsonBody(self):
409 """Return the body of the web service request as a JSON document."""410 """Return the body of the web service request as a JSON document."""
410 try:411 try:
411 json = simplejson.loads(unicode(self.body))412 json = simplejson.loads(six.ensure_text(self.body))
412 if isinstance(json, list):413 if isinstance(json, list):
413 json = sorted(json)414 json = sorted(json)
414 return json415 return json
415416
=== modified file 'src/lazr/restful/tests/test_webservice.py'
--- src/lazr/restful/tests/test_webservice.py 2020-02-19 16:09:10 +0000
+++ src/lazr/restful/tests/test_webservice.py 2020-07-22 22:45:07 +0000
@@ -18,6 +18,7 @@
18import simplejson18import simplejson
19import unittest19import unittest
2020
21import six
21from zope.component import (22from zope.component import (
22 eventtesting,23 eventtesting,
23 getGlobalSiteManager,24 getGlobalSiteManager,
@@ -536,8 +537,8 @@
536 def resource(self, value_1="value 1", value_2="value 2"):537 def resource(self, value_1="value 1", value_2="value 2"):
537 """Simplify the entry_resource call."""538 """Simplify the entry_resource call."""
538 with self.entry_resource(539 with self.entry_resource(
539 IHasTwoFields, HasTwoFields,540 IHasTwoFields, HasTwoFields,
540 unicode(value_1), unicode(value_2)) as resource:541 six.text_type(value_1), six.text_type(value_2)) as resource:
541 yield resource542 yield resource
542543
543 def test_web_layer_json_representation_omits_lp_html(self):544 def test_web_layer_json_representation_omits_lp_html(self):
@@ -598,7 +599,7 @@
598 json = None599 json = None
599 with self.resource() as resource:600 with self.resource() as resource:
600 json_plus_xhtml = resource.JSON_PLUS_XHTML_TYPE601 json_plus_xhtml = resource.JSON_PLUS_XHTML_TYPE
601 json = simplejson.loads(unicode(602 json = simplejson.loads(six.text_type(
602 resource._representation(json_plus_xhtml)))603 resource._representation(json_plus_xhtml)))
603 resource.applyChanges(json, json_plus_xhtml)604 resource.applyChanges(json, json_plus_xhtml)
604 self.assertEqual(resource.request.response.getStatus(), 209)605 self.assertEqual(resource.request.response.getStatus(), 209)
@@ -607,7 +608,7 @@
607 self.register_html_field_renderer()608 self.register_html_field_renderer()
608 json = None609 json = None
609 with self.resource() as resource:610 with self.resource() as resource:
610 json = simplejson.loads(unicode(611 json = simplejson.loads(six.text_type(
611 resource._representation(resource.JSON_PLUS_XHTML_TYPE)))612 resource._representation(resource.JSON_PLUS_XHTML_TYPE)))
612 resource.applyChanges(json, resource.JSON_TYPE)613 resource.applyChanges(json, resource.JSON_TYPE)
613 self.assertEqual(resource.request.response.getStatus(), 400)614 self.assertEqual(resource.request.response.getStatus(), 400)
614615
=== modified file 'src/lazr/restful/utils.py'
--- src/lazr/restful/utils.py 2020-02-04 11:52:59 +0000
+++ src/lazr/restful/utils.py 2020-07-22 22:45:07 +0000
@@ -31,6 +31,7 @@
31import subprocess31import subprocess
3232
33from simplejson import encoder33from simplejson import encoder
34import six
3435
35from zope.component import getUtility36from zope.component import getUtility
36from zope.schema import getFieldsInOrder37from zope.schema import getFieldsInOrder
@@ -359,7 +360,7 @@
359360
360def smartquote(str):361def smartquote(str):
361 """Return a copy of the string, with typographical quote marks applied."""362 """Return a copy of the string, with typographical quote marks applied."""
362 str = unicode(str)363 str = six.text_type(str)
363 str = re.compile(u'(^| )(")([^" ])').sub(u'\\1\u201c\\3', str)364 str = re.compile(u'(^| )(")([^" ])').sub(u'\\1\u201c\\3', str)
364 str = re.compile(u'([^ "])(")($|[\s.,;:!?])').sub(u'\\1\u201d\\3', str)365 str = re.compile(u'([^ "])(")($|[\s.,;:!?])').sub(u'\\1\u201d\\3', str)
365 return str366 return str

Subscribers

People subscribed via source and target branches