Mercurial > p > roundup > code
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""" |
