Pytest raises not working for custom exception - python

I have the following defined in an exceptions.py file:
class Error(Exception):
"""Base exception raised by api wrapper"""
def __init__(self, message: str):
self.message = message
super().__init__(self.message)
# HTTP response exceptions
class ApiBadRequestError(Error):
"""Bad Request –- Incorrect parameters."""
def __init__(self, message: str):
self.message = message
super().__init__(self.message)
I then have a function that correctly raises an ApiBadRequestError exception.
In pytest I'm doing the following:
def test_handle_request_response_raises_correct_exception_for_response_code(
self, status_code, exception_type, client, create_response
):
response = create_response(status_code=status_code)
with pytest.raises(ApiBadRequestError) as e:
a = client._check_response_codes(response)
which is failing the test because inside pytest.raises it's doing isintance(e, ApiBadRequestError) which is returning False. If I however change the test to the following:
def test_handle_request_response_raises_correct_exception_for_response_code(
self, status_code, exception_type, client, create_response
):
response = create_response(status_code=status_code)
with pytest.raises(Exception) as e:
a = client._check_response_codes(response)
it passes as the raised exception is seen as an instance of Exception even though it's an ApiBadRequestError
Any help would be really appreciated as I'm completely stumped here.

Related

How to test Fastapi Exception Handler

I created a custom exception handler and wanted to write test cases for it.
This is my Testcase:
def test_is_exception_raised(app_client):
exception = CustomException(InvalidSQLStatement())
with pytest.raises(exception):
raise exception
try:
raise exception
except Exception as e:
assert e.message
assert e.status_code
This is the error I get:
My Code looks like this:
main.py
#app.exception_handler(CustomException)
async def custom_exception_handler(request: Request, exc: CustomException):
log.error(f"{exc}")
return JSONResponse(
status_code=exc.status_code_number,
content=jsonable_encoder({exc.status_code_number: exc.message}),
)
exceptions.py
class CustomException(Exception):
"""
All Custom Exceptions are defined in xyz\exceptions.py
"""
def __init__(self, exception):
if not check_if_exception_exists(exception):
raise KeyError(f"Custom Exception: {exception.__class__.__name__} does not exist.")
self.message = exception.message
self.status_code_number = exception.status_code_number
You need to use raises in the class, like this:
with pytest.raises(CustomException):
Also, you declare status_code_number (in exceptions.py), but use status_code in test case.

How to avoid messy error handling in Tornado RequestHandler

Say I have a simple RequestHandler like this.
class RequestHandler(web.RequestHandler):
def get(self, id, status):
obj = self.retrieve_object(id)
obj.update({"status": status})
self.write(json.dumps(obj))
Problem is, whenever there's an error in the handler, it's gonna return an error 500 (Internal server error). Obviously I want to return an error 400 instead when the user has inputted something invalid.
So I have to add a bunch of error checking, like this:
class RequestHandler(web.RequestHandler):
def get(self, id, status):
try:
id = int(id)
except ValueError:
raise web.HTTPError(400, "Invalid id")
if status not in ("open", "closed"):
raise web.HTTPError(400, "Invalid status")
try:
obj = self.retrieve_object(id)
except ObjDoesntExistError:
raise web.HTTPError(400, "Object doesn't exist")
obj.update({"status": status})
self.write(json.dumps(obj))
The issue is that this adds a lot of bloat to the function. Is there a cleaner way to do this? Or is it unavoidable?
If you want to perform the same checks in multiple handlers, you can just create a base class:
class BaseHandler(web.RequestHandler):
def prepare(self):
id = self.path_args[0]
status = self.path_args[1]
try:
id = int(id)
except ValueError:
raise web.HTTPError(400, "Invalid id")
# ... and so on ...
Now, just inherit from this base class and the code will be reused.

Python AsyncHttpClient inside tornado RequestHandler throws exception

I'm going to call an endpoint by tornado AsyncHttpClient in RequestHandler, but it throws runtime exception This event loop is already running
class RegistrationHandler(tornado.web.RequestHandler):
def post(self, *args, **kwargs):
call_async_register("some params")
def call_async_register(parameters):
def call():
http_client = AsyncHTTPClient()
future = Future()
http_request = HTTPRequest(url, request_type.name, headers={'X-Peering': '1'}, body=body)
def handle_future(f: Future):
future.set_result(f.result())
fetched_future = http_client.fetch(http_request)
fetched_future.add_done_callback(handle_future)
return future
try:
instance = io_loop.IOLoop.current()
response = instance.run_sync(call)
return response.body.decode()
except Exception as err:
self.logger.exception("Account Request Failed: {}".format(err))
return None
Here's the problem:
instance = io_loop.IOLoop.current()
response = instance.run_sync(call)
run_sync itself tries to start the ioloop. But as apparent from your code, instance is already running. So you get the error.
If you want to send the value returned by call() method back to the user, convert your methods to coroutines (use async/await syntax).
Example:
class RegistrationHandler(tornado.web.RequestHandler):
async def post(self, *args, **kwargs):
response = await call_async_register("some params")
self.write(response)
async def call_async_register(parameters):
http_client = AsyncHTTPClient()
http_request = HTTPRequest(url, request_type.name, headers={'X-Peering': '1'}, body=body)
try:
response = await http_client.fetch(http_request)
return response.body.decode()
except Exception as err:
self.logger.exception("Account Request Failed: {}".format(err))
return None

How to create a custom Exception with error message and status code in python3

I am trying to create the following exception and call it in another function:
### The exception
class GoogleAuthError(Exception):
def __init__(self, message, code=403):
self.code = code
self.message = message
### Generating the exception
raise GoogleAuthError(message="There was an error authenticating")
### printing the exception
try:
do_something()
except GoogleAuthError as e:
print(e.message)
Basically, I want it to print "There was an error authenticating". How would I do this properly, or is the above the correct way to do it?
Remove the code argument from your __init__. You aren't using it.
You can also delegate the handling of the error message to the parent Exception class, which already knows about messages
class GoogleAuthError(Exception):
def __init__(self, message):
super().__init__(message)
self.code = 403
try:
raise GoogleAuthError('There was an error authenticating')
except GoogleAuthError as e:
print(e)
# There was an error authenticating

Flask: Custom Error handler resulting in TypeError: isinstance() arg 2

I have an issue using Flask and creating a custom error handler. I want to offload my error handling to a separate class and have followed the documentation here: http://flask.pocoo.org/docs/0.10/patterns/apierrors/
Upon testing my new error handler the following message is returned.
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
I believe the above error message is coming directly from this check in Flask: https://github.com/mitsuhiko/flask/blob/master/flask/app.py#L1407 but I don't understand the actual problem or how to fix it.
The custom error class is:
class ErrorHandler(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
and the code that is being run to call this class is:
#app.errorhandler(ErrorHandler)
def bad_request(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
#app.route('/', methods=['GET', 'POST'])
def testing():
if request.method == 'GET':
raise ErrorHandler('test', 400)
elif request.methods == 'POST':
pass
else:
pass
SOLVED:
Changed my imports from
import ErrorHandler
to
from ErrorHandler import ErrorHandler
The exception indicates that what you are passing to the #app.errorhandler() decorator is not a class.
The isinstance() test is given the exception raised (ErrorHandler('test', 400) in your case), and whatever you passed to #app.errorhandler(). The exception tells you that latter value is not a class:
>>> class Foo(Exception): pass
...
>>> isinstance(Foo(), Foo)
True
>>> isinstance(Foo(), 'something else')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
Double-check and triple-check you have the right object there.

Categories

Resources