Port middleware to Python 3
* Fix integer division: status_code/100 => status_code//100 * HTTP body type is bytes. Decode the HTTP body from UTF-8 to use it. Encode XML and JSON to UTF-8 to produce the HTTP body (for error messages). * Factorize XML/JSON code a little bit more (ex: add the new content_type variable) * Fix test_app on Python 3: decode HTTP from UTF-8 to load JSON * tox.ini: add api.v2.test_app to Python 3.4 Change-Id: I4f916cba36306f776b01df915c55e314ade6b6ba
This commit is contained in:
parent
639a24dc95
commit
5977295daf
@ -23,6 +23,7 @@ import json
|
|||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from ceilometer.api import hooks
|
from ceilometer.api import hooks
|
||||||
@ -79,7 +80,7 @@ class ParsableErrorMiddleware(object):
|
|||||||
return start_response(status, headers, exc_info)
|
return start_response(status, headers, exc_info)
|
||||||
|
|
||||||
app_iter = self.app(environ, replacement_start_response)
|
app_iter = self.app(environ, replacement_start_response)
|
||||||
if (state['status_code'] / 100) not in (2, 3):
|
if (state['status_code'] // 100) not in (2, 3):
|
||||||
req = webob.Request(environ)
|
req = webob.Request(environ)
|
||||||
# Find the first TranslationHook in the array of hooks and use the
|
# Find the first TranslationHook in the array of hooks and use the
|
||||||
# translatable_error object from it
|
# translatable_error object from it
|
||||||
@ -91,32 +92,44 @@ class ParsableErrorMiddleware(object):
|
|||||||
user_locale = self.best_match_language(req.accept_language)
|
user_locale = self.best_match_language(req.accept_language)
|
||||||
if (req.accept.best_match(['application/json', 'application/xml'])
|
if (req.accept.best_match(['application/json', 'application/xml'])
|
||||||
== 'application/xml'):
|
== 'application/xml'):
|
||||||
|
content_type = 'application/xml'
|
||||||
try:
|
try:
|
||||||
# simple check xml is valid
|
# simple check xml is valid
|
||||||
fault = etree.fromstring('\n'.join(app_iter))
|
fault = etree.fromstring(b'\n'.join(app_iter))
|
||||||
# Add the translated error to the xml data
|
# Add the translated error to the xml data
|
||||||
if error is not None:
|
if error is not None:
|
||||||
for fault_string in fault.findall('faultstring'):
|
for fault_string in fault.findall('faultstring'):
|
||||||
fault_string.text = i18n.translate(error,
|
fault_string.text = i18n.translate(error,
|
||||||
user_locale)
|
user_locale)
|
||||||
body = ['<error_message>' + etree.tostring(fault)
|
error_message = etree.tostring(fault)
|
||||||
+ '</error_message>']
|
body = b''.join((b'<error_message>',
|
||||||
|
error_message,
|
||||||
|
b'</error_message>'))
|
||||||
except etree.XMLSyntaxError as err:
|
except etree.XMLSyntaxError as err:
|
||||||
LOG.error(_('Error parsing HTTP response: %s') % err)
|
LOG.error(_('Error parsing HTTP response: %s'), err)
|
||||||
body = ['<error_message>%s' % state['status_code']
|
error_message = state['status_code']
|
||||||
+ '</error_message>']
|
body = '<error_message>%s</error_message>' % error_message
|
||||||
state['headers'].append(('Content-Type', 'application/xml'))
|
if six.PY3:
|
||||||
|
body = body.encode('utf-8')
|
||||||
else:
|
else:
|
||||||
|
content_type = 'application/json'
|
||||||
|
app_data = b'\n'.join(app_iter)
|
||||||
|
if six.PY3:
|
||||||
|
app_data = app_data.decode('utf-8')
|
||||||
try:
|
try:
|
||||||
fault = json.loads('\n'.join(app_iter))
|
fault = json.loads(app_data)
|
||||||
if error is not None and 'faultstring' in fault:
|
if error is not None and 'faultstring' in fault:
|
||||||
fault['faultstring'] = i18n.translate(error,
|
fault['faultstring'] = i18n.translate(error,
|
||||||
user_locale)
|
user_locale)
|
||||||
body = [json.dumps({'error_message': fault})]
|
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
body = [json.dumps({'error_message': '\n'.join(app_iter)})]
|
fault = app_data
|
||||||
state['headers'].append(('Content-Type', 'application/json'))
|
body = json.dumps({'error_message': fault})
|
||||||
state['headers'].append(('Content-Length', str(len(body[0]))))
|
if six.PY3:
|
||||||
|
body = body.encode('utf-8')
|
||||||
|
|
||||||
|
state['headers'].append(('Content-Length', str(len(body))))
|
||||||
|
state['headers'].append(('Content-Type', content_type))
|
||||||
|
body = [body]
|
||||||
else:
|
else:
|
||||||
body = app_iter
|
body = app_iter
|
||||||
return body
|
return body
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import six
|
||||||
import wsme
|
import wsme
|
||||||
|
|
||||||
from ceilometer import i18n
|
from ceilometer import i18n
|
||||||
@ -159,8 +160,11 @@ class TestApiMiddleware(v2.FunctionalTest):
|
|||||||
def test_translated_then_untranslated_error(self):
|
def test_translated_then_untranslated_error(self):
|
||||||
resp = self.get_json('/alarms/alarm-id-3', expect_errors=True)
|
resp = self.get_json('/alarms/alarm-id-3', expect_errors=True)
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
|
body = resp.body
|
||||||
|
if six.PY3:
|
||||||
|
body = body.decode('utf-8')
|
||||||
self.assertEqual("Alarm alarm-id-3 not found",
|
self.assertEqual("Alarm alarm-id-3 not found",
|
||||||
json.loads(resp.body)['error_message']
|
json.loads(body)['error_message']
|
||||||
['faultstring'])
|
['faultstring'])
|
||||||
|
|
||||||
with mock.patch('ceilometer.api.controllers.'
|
with mock.patch('ceilometer.api.controllers.'
|
||||||
@ -170,6 +174,9 @@ class TestApiMiddleware(v2.FunctionalTest):
|
|||||||
resp = self.get_json('/alarms/alarm-id-5', expect_errors=True)
|
resp = self.get_json('/alarms/alarm-id-5', expect_errors=True)
|
||||||
|
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
|
body = resp.body
|
||||||
|
if six.PY3:
|
||||||
|
body = body.decode('utf-8')
|
||||||
self.assertEqual("untranslated_error",
|
self.assertEqual("untranslated_error",
|
||||||
json.loads(resp.body)['error_message']
|
json.loads(body)['error_message']
|
||||||
['faultstring'])
|
['faultstring'])
|
||||||
|
1
tox.ini
1
tox.ini
@ -47,6 +47,7 @@ commands =
|
|||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements-py3.txt
|
-r{toxinidir}/test-requirements-py3.txt
|
||||||
commands = python -m testtools.run \
|
commands = python -m testtools.run \
|
||||||
|
ceilometer.tests.api.v2.test_app \
|
||||||
ceilometer.tests.api.v2.test_query \
|
ceilometer.tests.api.v2.test_query \
|
||||||
ceilometer.tests.compute.virt.libvirt.test_inspector \
|
ceilometer.tests.compute.virt.libvirt.test_inspector \
|
||||||
ceilometer.tests.compute.virt.vmware.test_vsphere_operations \
|
ceilometer.tests.compute.virt.vmware.test_vsphere_operations \
|
||||||
|
Loading…
Reference in New Issue
Block a user