diff roundup/rest.py @ 5653:ba67e397f063

Fix string/bytes issues under python 3. 1) cgi/client.py: override cgi.FieldStorage's make_file so that file is always created in binary/byte mode. This means that json (and xml) are bytes not strings. 2) rest.py: try harder to find dicttoxml in roundup directory or on sys.path. This just worked under python 2 but python 3 only searches sys.path by default and does not search relative like python 2. 3) rest.py: replace headers.getheader call removed from python 3 with equivalent code. 4) rest.py: make value returned from dispatch into bytes not string. 5) test/caseinsensitivedict.py, test/test_CaseInsensitiveDict.py: get code from stackoverflow that implements a case insensitive key dict. So dict['foo'], dict['Foo'] are the same entry. Used for looking up headers in mocked http rewuset header array. 6) test/rest_common.py: rework tests for etags and rest to properly supply bytes to the called routines. Calls to s2b and b2s and use of BytesIO and overriding make_file in cgi.FieldStorage to try to make sure it works under python 3.
author John Rouillard <rouilj@ieee.org>
date Sun, 17 Mar 2019 19:28:26 -0400
parents d791c5ba5852
children 207e0f5d551c
line wrap: on
line diff
--- a/roundup/rest.py	Sun Mar 17 19:00:43 2019 -0400
+++ b/roundup/rest.py	Sun Mar 17 19:28:26 2019 -0400
@@ -20,9 +20,15 @@
 import re
 
 try:
-    from dicttoxml import dicttoxml
+    # if dicttoxml installed in roundup directory, use it
+    from .dicttoxml import dicttoxml
 except ImportError:
-    dicttoxml = None
+    try:
+        # else look in sys.path
+        from dicttoxml import dicttoxml
+    except ImportError:
+        # else not supported
+        dicttoxml = None
 
 from roundup import hyperdb
 from roundup import date
@@ -160,7 +166,8 @@
     etags = []
     if '@etag' in input:
         etags.append(input['@etag'].value);
-    etags.append(headers.getheader("ETag", None))
+    if "ETag" in headers:
+        etags.append(headers["ETag"])
     return etags
 
 def parse_accept_header(accept):
@@ -220,7 +227,9 @@
             else:
                 media_params.append((key, value))
         result.append((media_type, dict(media_params), q))
-    result.sort(lambda x, y: -cmp(x[2], y[2]))
+    # was: result.sort(lambda x, y: -cmp(x[2], y[2]))
+    # change for python 3 support
+    result.sort(key=lambda x: x[2], reverse=True)
     return result
 
 
@@ -1299,7 +1308,9 @@
         headers = self.client.request.headers
         # Never allow GET to be an unsafe operation (i.e. data changing).
         # User must use POST to "tunnel" DELETE, PUT, OPTIONS etc.
-        override = headers.getheader('X-HTTP-Method-Override')
+        override = None
+        if 'X-HTTP-Method-Override' in headers:
+            override = headers['X-HTTP-Method-Override']
         output = None
         if override:
             if method.upper() != 'GET':
@@ -1314,7 +1325,9 @@
                     uri)
 
         # parse Accept header and get the content type
-        accept_header = parse_accept_header(headers.getheader('Accept'))
+        accept_header = []
+        if 'Accept' in headers:
+            accept_header = parse_accept_header(headers['Accept'])
         accept_type = "invalid"
         for part in accept_header:
             if part[0] in self.__accepted_content_type:
@@ -1350,7 +1363,9 @@
         # Is there an input.value with format json data?
         # If so turn it into an object that emulates enough
         # of the FieldStorge methods/props to allow a response.
-        content_type_header = headers.getheader('Content-Type', None)
+        content_type_header = None
+        if 'Content-Type' in headers:
+            content_type_header = headers['Content-Type']
         if type(input.value) == str and content_type_header:
             parsed_content_type_header = content_type_header
             # the structure of a content-type header
@@ -1404,7 +1419,7 @@
 
         # Make output json end in a newline to
         # separate from following text in logs etc..
-        return output + "\n"
+        return bs2b(output + "\n")
 
 
 class RoundupJSONEncoder(json.JSONEncoder):

Roundup Issue Tracker: http://roundup-tracker.org/