summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjiataotj <jiataotj@cn.ibm.com>2013-05-31 00:18:28 +0800
committerjiataotj <jiataotj@cn.ibm.com>2013-07-26 13:43:27 +0800
commitf976bbe697002367ff00a0588a3181fd42008e1c (patch)
treea5763c4d4725ec29bb631044bd071e1ff77248c7
parent0a401529947c98ab761256e233b157071a57ab70 (diff)
downloadkeystone-f976bbe697002367ff00a0588a3181fd42008e1c.tar.gz
keystone-f976bbe697002367ff00a0588a3181fd42008e1c.tar.xz
keystone-f976bbe697002367ff00a0588a3181fd42008e1c.zip
Implement exception module i18n support
The doc string in exception.py of Keystone will be returned with __doc__ method, but cannot realize the internationalization.Change exception module to enable i18n support. Changes in the patch are: 1, useing class variable msg_fmt to replace class __doc__ 2, modify wsgi.render_exception function using unicode function to replace str function 3, modify/add UT test cases Fixes: bug # 1179425 Change-Id: I75c1229c905a2625d2f6961d1a8dd3958eac51a5
-rw-r--r--keystone/common/wsgi.py2
-rw-r--r--keystone/exception.py128
-rw-r--r--tests/test_exception.py32
-rw-r--r--tests/test_wsgi.py5
4 files changed, 84 insertions, 83 deletions
diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py
index c7e30576..381f1ff0 100644
--- a/keystone/common/wsgi.py
+++ b/keystone/common/wsgi.py
@@ -583,7 +583,7 @@ def render_exception(error):
body = {'error': {
'code': error.code,
'title': error.title,
- 'message': str(error)
+ 'message': unicode(error)
}}
if isinstance(error, exception.AuthPluginException):
body['error']['identity'] = error.authentication
diff --git a/keystone/exception.py b/keystone/exception.py
index db5f5005..5e1defba 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -13,10 +13,10 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import re
from keystone.common import config
from keystone.common import logging
+from keystone.openstack.common.gettextutils import _ # noqa
CONF = config.CONF
@@ -29,15 +29,15 @@ _FATAL_EXCEPTION_FORMAT_ERRORS = False
class Error(StandardError):
"""Base error class.
- Child classes should define an HTTP status code, title, and a doc string.
+ Child classes should define an HTTP status code, title, and a
+ message_format.
"""
code = None
title = None
+ message_format = None
def __init__(self, message=None, **kwargs):
- """Use the doc string as the error message by default."""
-
try:
message = self._build_message(message, **kwargs)
except KeyError:
@@ -45,8 +45,8 @@ class Error(StandardError):
if _FATAL_EXCEPTION_FORMAT_ERRORS:
raise
else:
- LOG.warning('missing exception kwargs (programmer error)')
- message = self.__doc__
+ LOG.warning(_('missing exception kwargs (programmer error)'))
+ message = self.message_format
super(Error, self).__init__(message)
@@ -57,42 +57,31 @@ class Error(StandardError):
"""
if not message:
- message = re.sub('[ \n]+', ' ', self.__doc__ % kwargs)
- message = message.strip()
+ message = self.message_format % kwargs
return message
class ValidationError(Error):
- """Expecting to find %(attribute)s in %(target)s.
-
- The server could not comply with the request since it is either malformed
- or otherwise incorrect.
-
- The client is assumed to be in error.
-
- """
+ message_format = _("Expecting to find %(attribute)s in %(target)s."
+ " The server could not comply with the request"
+ " since it is either malformed or otherwise"
+ " incorrect. The client is assumed to be in error.")
code = 400
title = 'Bad Request'
class StringLengthExceeded(ValidationError):
- """String length exceeded.
-
- The length of string "%(string)s" exceeded the limit of column
- %(type)s(CHAR(%(length)d)).
-
- """
+ message_format = _("String length exceeded.The length of"
+ " string '%(string)s' exceeded the limit"
+ " of column %(type)s(CHAR(%(length)d)).")
class ValidationSizeError(Error):
- """Request attribute %(attribute)s must be less than or equal to %(size)i.
-
- The server could not comply with the request because the attribute
- size is invalid (too large).
-
- The client is assumed to be in error.
-
- """
+ message_format = _("Request attribute %(attribute)s must be"
+ " less than or equal to %(size)i. The server"
+ " could not comply with the request because"
+ " the attribute size is invalid (too large)."
+ " The client is assumed to be in error.")
code = 400
title = 'Bad Request'
@@ -103,19 +92,19 @@ class SecurityError(Error):
def _build_message(self, message, **kwargs):
"""Only returns detailed messages in debug mode."""
if CONF.debug:
- return message or self.__doc__ % kwargs
+ return message or self.message_format % kwargs
else:
- return self.__doc__ % kwargs
+ return self.message_format % kwargs
class Unauthorized(SecurityError):
- """The request you have made requires authentication."""
+ message_format = _("The request you have made requires authentication.")
code = 401
title = 'Unauthorized'
class AuthPluginException(Unauthorized):
- """Authentication plugin error."""
+ message_format = _("Authentication plugin error.")
def __init__(self, *args, **kwargs):
super(AuthPluginException, self).__init__(*args, **kwargs)
@@ -123,7 +112,7 @@ class AuthPluginException(Unauthorized):
class AuthMethodNotSupported(AuthPluginException):
- """Attempted to authenticate with an unsupported method."""
+ message_format = _("Attempted to authenticate with an unsupported method.")
def __init__(self, *args, **kwargs):
super(AuthMethodNotSupported, self).__init__(*args, **kwargs)
@@ -131,7 +120,7 @@ class AuthMethodNotSupported(AuthPluginException):
class AdditionalAuthRequired(AuthPluginException):
- """Additional authentications steps required."""
+ message_format = _("Additional authentications steps required.")
def __init__(self, auth_response=None, **kwargs):
super(AdditionalAuthRequired, self).__init__(message=None, **kwargs)
@@ -139,112 +128,111 @@ class AdditionalAuthRequired(AuthPluginException):
class Forbidden(SecurityError):
- """You are not authorized to perform the requested action."""
+ message_format = _("You are not authorized to perform the"
+ " requested action.")
code = 403
title = 'Forbidden'
class ForbiddenAction(Forbidden):
- """You are not authorized to perform the requested action, %(action)s."""
+ message_format = _("You are not authorized to perform the"
+ " requested action, %(action)s.")
class NotFound(Error):
- """Could not find, %(target)s."""
+ message_format = _("Could not find, %(target)s.")
code = 404
title = 'Not Found'
class EndpointNotFound(NotFound):
- """Could not find endpoint, %(endpoint_id)s."""
+ message_format = _("Could not find endpoint, %(endpoint_id)s.")
class MetadataNotFound(NotFound):
- """An unhandled exception has occurred: Could not find metadata."""
- # (dolph): metadata is not a user-facing concept,
- # so this exception should not be exposed
+ """(dolph): metadata is not a user-facing concept,
+ so this exception should not be exposed
+ """
+ message_format = _("An unhandled exception has occurred:"
+ " Could not find metadata.")
class PolicyNotFound(NotFound):
- """Could not find policy, %(policy_id)s."""
+ message_format = _("Could not find policy, %(policy_id)s.")
class RoleNotFound(NotFound):
- """Could not find role, %(role_id)s."""
+ message_format = _("Could not find role, %(role_id)s.")
class ServiceNotFound(NotFound):
- """Could not find service, %(service_id)s."""
+ message_format = _("Could not find service, %(service_id)s.")
class DomainNotFound(NotFound):
- """Could not find domain, %(domain_id)s."""
+ message_format = _("Could not find domain, %(domain_id)s.")
class ProjectNotFound(NotFound):
- """Could not find project, %(project_id)s."""
+ message_format = _("Could not find project, %(project_id)s.")
class TokenNotFound(NotFound):
- """Could not find token, %(token_id)s."""
+ message_format = _("Could not find token, %(token_id)s.")
class UserNotFound(NotFound):
- """Could not find user, %(user_id)s."""
+ message_format = _("Could not find user, %(user_id)s.")
class GroupNotFound(NotFound):
- """Could not find group, %(group_id)s."""
+ message_format = _("Could not find group, %(group_id)s.")
class TrustNotFound(NotFound):
- """Could not find trust, %(trust_id)s."""
+ message_format = _("Could not find trust, %(trust_id)s.")
class CredentialNotFound(NotFound):
- """Could not find credential, %(credential_id)s."""
+ message_format = _("Could not find credential, %(credential_id)s.")
class VersionNotFound(NotFound):
- """Could not find version, %(version)s."""
+ message_format = _("Could not find version, %(version)s.")
class Conflict(Error):
- """Conflict occurred attempting to store %(type)s.
-
- %(details)s
-
- """
+ message_format = _("Conflict occurred attempting to store %(type)s."
+ " %(details)s")
code = 409
title = 'Conflict'
class RequestTooLarge(Error):
- """Request is too large."""
+ message_format = _("Request is too large.")
code = 413
title = 'Request is too large.'
class UnexpectedError(Error):
- """An unexpected error prevented the server from fulfilling your request.
-
- %(exception)s
-
- """
+ message_format = _("An unexpected error prevented the server"
+ " from fulfilling your request. %(exception)s")
code = 500
title = 'Internal Server Error'
class MalformedEndpoint(UnexpectedError):
- """Malformed endpoint URL (%(endpoint)s), see ERROR log for details."""
+ message_format = _("Malformed endpoint URL (%(endpoint)s),"
+ " see ERROR log for details.")
class NotImplemented(Error):
- """The action you have requested has not been implemented."""
+ message_format = _("The action you have requested has not"
+ " been implemented.")
code = 501
title = 'Not Implemented'
class PasteConfigNotFound(UnexpectedError):
- """The Keystone paste configuration file %(config_file)s could not be
- found.
- """
+ message_format = _("The Keystone paste configuration file"
+ " %(config_file)s could not be found.")
diff --git a/tests/test_exception.py b/tests/test_exception.py
index 33250835..d442d572 100644
--- a/tests/test_exception.py
+++ b/tests/test_exception.py
@@ -67,14 +67,14 @@ class ExceptionTestCase(test.TestCase):
attribute = uuid.uuid4().hex
e = exception.ValidationError(target=target, attribute=attribute)
self.assertValidJsonRendering(e)
- self.assertIn(target, str(e))
- self.assertIn(attribute, str(e))
+ self.assertIn(target, unicode(e))
+ self.assertIn(attribute, unicode(e))
def test_not_found(self):
target = uuid.uuid4().hex
e = exception.NotFound(target=target)
self.assertValidJsonRendering(e)
- self.assertIn(target, str(e))
+ self.assertIn(target, unicode(e))
def test_403_title(self):
e = exception.Forbidden()
@@ -101,7 +101,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Unauthorized(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
+ self.assertNotIn(risky_info, unicode(e))
def test_unauthorized_exposure_in_debug(self):
self.opt(debug=True)
@@ -109,7 +109,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Unauthorized(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_exposure(self):
self.opt(debug=False)
@@ -117,7 +117,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Forbidden(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
+ self.assertNotIn(risky_info, unicode(e))
def test_forbidden_exposure_in_debug(self):
self.opt(debug=True)
@@ -125,7 +125,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
risky_info = uuid.uuid4().hex
e = exception.Forbidden(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_action_exposure(self):
self.opt(debug=False)
@@ -134,12 +134,12 @@ class SecurityErrorTestCase(ExceptionTestCase):
action = uuid.uuid4().hex
e = exception.ForbiddenAction(message=risky_info, action=action)
self.assertValidJsonRendering(e)
- self.assertNotIn(risky_info, str(e))
- self.assertIn(action, str(e))
+ self.assertNotIn(risky_info, unicode(e))
+ self.assertIn(action, unicode(e))
e = exception.ForbiddenAction(action=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
def test_forbidden_action_exposure_in_debug(self):
self.opt(debug=True)
@@ -148,8 +148,16 @@ class SecurityErrorTestCase(ExceptionTestCase):
e = exception.ForbiddenAction(message=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
e = exception.ForbiddenAction(action=risky_info)
self.assertValidJsonRendering(e)
- self.assertIn(risky_info, str(e))
+ self.assertIn(risky_info, unicode(e))
+
+ def test_unicode_argument_message(self):
+ self.opt(debug=False)
+
+ risky_info = u'\u7ee7\u7eed\u884c\u7f29\u8fdb\u6216'
+ e = exception.Forbidden(message=risky_info)
+ self.assertValidJsonRendering(e)
+ self.assertNotIn(risky_info, unicode(e))
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index 8ac594a8..369dd952 100644
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -134,6 +134,11 @@ class ApplicationTest(BaseWSGITest):
self.assertIn("testkey", app.kwargs)
self.assertEquals("test", app.kwargs["testkey"])
+ def test_render_exception(self):
+ e = exception.Unauthorized(message=u'\u7f51\u7edc')
+ resp = wsgi.render_exception(e)
+ self.assertEqual(resp.status_int, 401)
+
class ExtensionRouterTest(BaseWSGITest):
def test_extensionrouter_local_config(self):