comparison roundup/rest.py @ 5650:e8ca7072c629

Fix Python 3 issues in REST code. * Need to use .get not .getheader for HTTP headers (see hg commit fec18298ae02, "Python 3 preparation: HTTP headers handling in roundup_server.py."). * Need to use key not cmp with sort (see hg commit 3fa026621f69, "Python 3 preparation: comparisons."). * dispatch output must be bytes, not str, otherwise writing it to the socket (e.g. in roundup-server) will fail. This fixes issues shown up attempting to access the REST interface with a browser with Python 3 (as opposed to with the Roundup testsuite, which also has known REST issues with Python 3).
author Joseph Myers <jsm@polyomino.org.uk>
date Sun, 17 Mar 2019 16:25:36 +0000
parents d791c5ba5852
children 207e0f5d551c
comparison
equal deleted inserted replaced
5649:f8893e1cde0d 5650:e8ca7072c629
25 dicttoxml = None 25 dicttoxml = None
26 26
27 from roundup import hyperdb 27 from roundup import hyperdb
28 from roundup import date 28 from roundup import date
29 from roundup import actions 29 from roundup import actions
30 from roundup.anypy.strings import bs2b 30 from roundup.anypy.strings import bs2b, s2b
31 from roundup.exceptions import * 31 from roundup.exceptions import *
32 from roundup.cgi.exceptions import * 32 from roundup.cgi.exceptions import *
33 33
34 from hashlib import md5 34 from hashlib import md5
35 35
158 def obtain_etags(headers,input): 158 def obtain_etags(headers,input):
159 '''Get ETags value from headers or payload data''' 159 '''Get ETags value from headers or payload data'''
160 etags = [] 160 etags = []
161 if '@etag' in input: 161 if '@etag' in input:
162 etags.append(input['@etag'].value); 162 etags.append(input['@etag'].value);
163 etags.append(headers.getheader("ETag", None)) 163 etags.append(headers.get("ETag", None))
164 return etags 164 return etags
165 165
166 def parse_accept_header(accept): 166 def parse_accept_header(accept):
167 """ 167 """
168 Parse the Accept header *accept*, returning a list with 3-tuples of 168 Parse the Accept header *accept*, returning a list with 3-tuples of
218 if key == "q": 218 if key == "q":
219 q = float(value) 219 q = float(value)
220 else: 220 else:
221 media_params.append((key, value)) 221 media_params.append((key, value))
222 result.append((media_type, dict(media_params), q)) 222 result.append((media_type, dict(media_params), q))
223 result.sort(lambda x, y: -cmp(x[2], y[2])) 223 result.sort(key=lambda x: x[2], reverse=True)
224 return result 224 return result
225 225
226 226
227 class Routing(object): 227 class Routing(object):
228 __route_map = {} 228 __route_map = {}
1297 """format and process the request""" 1297 """format and process the request"""
1298 # if X-HTTP-Method-Override is set, follow the override method 1298 # if X-HTTP-Method-Override is set, follow the override method
1299 headers = self.client.request.headers 1299 headers = self.client.request.headers
1300 # Never allow GET to be an unsafe operation (i.e. data changing). 1300 # Never allow GET to be an unsafe operation (i.e. data changing).
1301 # User must use POST to "tunnel" DELETE, PUT, OPTIONS etc. 1301 # User must use POST to "tunnel" DELETE, PUT, OPTIONS etc.
1302 override = headers.getheader('X-HTTP-Method-Override') 1302 override = headers.get('X-HTTP-Method-Override')
1303 output = None 1303 output = None
1304 if override: 1304 if override:
1305 if method.upper() != 'GET': 1305 if method.upper() != 'GET':
1306 logger.debug( 1306 logger.debug(
1307 'Method overridden from %s to %s', method, override) 1307 'Method overridden from %s to %s', method, override)
1312 logger.info( 1312 logger.info(
1313 'Ignoring X-HTTP-Method-Override for GET request on %s', 1313 'Ignoring X-HTTP-Method-Override for GET request on %s',
1314 uri) 1314 uri)
1315 1315
1316 # parse Accept header and get the content type 1316 # parse Accept header and get the content type
1317 accept_header = parse_accept_header(headers.getheader('Accept')) 1317 accept_header = parse_accept_header(headers.get('Accept'))
1318 accept_type = "invalid" 1318 accept_type = "invalid"
1319 for part in accept_header: 1319 for part in accept_header:
1320 if part[0] in self.__accepted_content_type: 1320 if part[0] in self.__accepted_content_type:
1321 accept_type = self.__accepted_content_type[part[0]] 1321 accept_type = self.__accepted_content_type[part[0]]
1322 1322
1348 ) 1348 )
1349 1349
1350 # Is there an input.value with format json data? 1350 # Is there an input.value with format json data?
1351 # If so turn it into an object that emulates enough 1351 # If so turn it into an object that emulates enough
1352 # of the FieldStorge methods/props to allow a response. 1352 # of the FieldStorge methods/props to allow a response.
1353 content_type_header = headers.getheader('Content-Type', None) 1353 content_type_header = headers.get('Content-Type', None)
1354 if type(input.value) == str and content_type_header: 1354 if type(input.value) == str and content_type_header:
1355 parsed_content_type_header = content_type_header 1355 parsed_content_type_header = content_type_header
1356 # the structure of a content-type header 1356 # the structure of a content-type header
1357 # is complex: mime-type; options(charset ...) 1357 # is complex: mime-type; options(charset ...)
1358 # for now we just accept application/json. 1358 # for now we just accept application/json.
1402 self.client.response_code = 406 1402 self.client.response_code = 406
1403 output = "Content type is not accepted by client" 1403 output = "Content type is not accepted by client"
1404 1404
1405 # Make output json end in a newline to 1405 # Make output json end in a newline to
1406 # separate from following text in logs etc.. 1406 # separate from following text in logs etc..
1407 return output + "\n" 1407 return s2b(output + "\n")
1408 1408
1409 1409
1410 class RoundupJSONEncoder(json.JSONEncoder): 1410 class RoundupJSONEncoder(json.JSONEncoder):
1411 """RoundupJSONEncoder overrides the default JSONEncoder to handle all 1411 """RoundupJSONEncoder overrides the default JSONEncoder to handle all
1412 types of the object without returning any error""" 1412 types of the object without returning any error"""

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