Skip to content

Commit

Permalink
Implement basic subset of Response features in HTTPException class.
Browse files Browse the repository at this point in the history
  • Loading branch information
hannosch committed Jul 22, 2016
1 parent 421c36b commit 142b2b5
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
3.2 (unreleased)
----------------

- Implement basic subset of Response features in HTTPException class.

3.1 (2016-07-22)
----------------
Expand Down
118 changes: 111 additions & 7 deletions src/zExceptions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,143 @@
from ._compat import string_types


status_reasons = {
# Informational
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing',

# Successful
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non-Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status',
226: 'IM Used',

# Redirection
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Found',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
307: 'Temporary Redirect',
308: 'Permanent Redirect',

# Client Error
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Time-out',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Request Entity Too Large',
414: 'Request-URI Too Large',
415: 'Unsupported Media Type',
416: 'Requested Range Not Satisfiable',
417: 'Expectation Failed',
418: "I'm a teapot",
422: 'Unprocessable Entity',
423: 'Locked',
424: 'Failed Dependency',
426: 'Upgrade Required',
428: 'Precondition Required',
429: 'Too Many Requests',
451: 'Unavailable for Legal Reasons',
431: 'Request Header Fields Too Large',

# Server Error
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Time-out',
505: 'HTTP Version not supported',
507: 'Insufficient Storage',
510: 'Not Extended',
511: 'Network Authentication Required',
}


@implementer(IHTTPException)
class HTTPException(Exception):
pass
body = None
errmsg = 'Internal Server Error'
status = 500

def setBody(self, body):
self.body = body

def getStatus(self):
return self.status

def setStatus(self, status, reason=None):
self.status = status
if reason is None:
reason = status_reasons.get(status, 'Unknown')
else:
reason = reason
self.errmsg = reason

def __call__(self, environ, start_response):
headers = [('content-type', 'text/html;charset=utf-8')]
if self.errmsg is not None:
reason = self.errmsg
reason = status_reasons[self.getStatus()]
start_response(
'%d %s' % (self.getStatus(), reason),
headers)
body = self.body
if body is None:
body = str(self)
return [body]


@implementer(IBadRequest)
class BadRequest(HTTPException):
pass
errmsg = 'Bad Request'
status = 400


@implementer(IException)
class InternalError(HTTPException):
pass
errmsg = 'Internal Server Error'
status = 500


@implementer(INotFound)
class NotFound(HTTPException):
pass
errmsg = 'Not Found'
status = 404


@implementer(IForbidden)
class Forbidden(HTTPException):
pass
errmsg = 'Forbidden'
status = 403


@implementer(IMethodNotAllowed)
class MethodNotAllowed(HTTPException):
pass
errmsg = 'Method Not Allowed'
status = 405


@implementer(IRedirect)
class Redirect(HTTPException):
pass
errmsg = 'Found'
status = 302


def convertExceptionType(name):
Expand Down
55 changes: 55 additions & 0 deletions src/zExceptions/tests/test___init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
import unittest


class TestHTTPException(unittest.TestCase):

def _getTargetClass(self):
from zExceptions import HTTPException
return HTTPException

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_body(self):
exc = self._makeOne()
self.assertEqual(exc.body, None)
exc.setBody('Foo')
self.assertEqual(exc.body, 'Foo')

def test_status(self):
exc = self._makeOne()
self.assertEqual(exc.getStatus(), 500)
self.assertEqual(exc.errmsg, 'Internal Server Error')
exc.setStatus(503)
self.assertEqual(exc.getStatus(), 503)
self.assertEqual(exc.errmsg, 'Service Unavailable')

def test_call(self):
exc = self._makeOne('Foo Error')
called = []

def start_response(status, headers):
called.append((status, headers))

response = exc({'Foo': 1}, start_response)
self.assertEqual(called, [(
'500 Internal Server Error',
[('content-type', 'text/html;charset=utf-8')]
)])
self.assertEqual(response, ['Foo Error'])

def test_call_custom(self):
exc = self._makeOne('Foo Error')
exc.setBody('<html>Foo</html>')
exc.setStatus(503)

called = []

def start_response(status, headers):
called.append((status, headers))

response = exc({'Foo': 1}, start_response)
self.assertEqual(called, [(
'503 Service Unavailable',
[('content-type', 'text/html;charset=utf-8')]
)])
self.assertEqual(response, ['<html>Foo</html>'])


class TestConvertExceptionType(unittest.TestCase):

def _callFUT(self, name):
Expand Down

0 comments on commit 142b2b5

Please sign in to comment.