diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-07-29 21:55:48 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-07-29 21:55:48 +0000 |
commit | 10fde8e5a61a1f0f0a1388b80a504c5a4290a96f (patch) | |
tree | 4bb7b44fb04c75d3c1998a82462614a7d5ae43eb | |
parent | 6760289a077a0c39341de58d6bd3b639fffe4fa3 (diff) | |
parent | f976bbe697002367ff00a0588a3181fd42008e1c (diff) | |
download | keystone-10fde8e5a61a1f0f0a1388b80a504c5a4290a96f.tar.gz keystone-10fde8e5a61a1f0f0a1388b80a504c5a4290a96f.tar.xz keystone-10fde8e5a61a1f0f0a1388b80a504c5a4290a96f.zip |
Merge "Implement exception module i18n support"
-rw-r--r-- | keystone/common/wsgi.py | 2 | ||||
-rw-r--r-- | keystone/exception.py | 128 | ||||
-rw-r--r-- | tests/test_exception.py | 32 | ||||
-rw-r--r-- | tests/test_wsgi.py | 5 |
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): |