Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error handling in Python-MySQL

I am running a little webservice based on python flask, where I want to execute a small MySQL Query. When I get a valid input for my SQL query, everything is working as expected and I get the right value back. However, if the value is not stored in the database I receive a TypeError

    Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1478, in full_dispatch_request
    response = self.make_response(rv)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1566, in make_response
    raise ValueError('View function did not return a response')
ValueError: View function did not return a response

I tried to tap into error handling myself and use this code for my project, but it seems like this doesn't work properly.

#!/usr/bin/python

from flask import Flask, request
import MySQLdb

import json

app = Flask(__name__)


@app.route("/get_user", methods=["POST"])
def get_user():
    data = json.loads(request.data)
    email = data["email"]

    sql = "SELECT userid FROM oc_preferences WHERE configkey='email' AND configvalue LIKE '" + email + "%';";

    conn = MySQLdb.connect( host="localhost",
                            user="root",
                            passwd="ubuntu",
                            db="owncloud",
                            port=3306)
    curs = conn.cursor()

    try:
        curs.execute(sql)
        user = curs.fetchone()[0]
        return user
    except MySQLdb.Error, e:
        try:
            print "MySQL Error [%d]: %s" % (e.args[0], e.args[1])
            return None
        except IndexError:
            print "MySQL Error: %s" % str(e)
            return None
    except TypeError, e:
        print(e)
        return None
    except ValueError, e:
        print(e)
        return None
    finally:
        curs.close()
        conn.close()

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

Basically I just want to return a value, when everything is working properly and I want to return nothing if it isn't preferably with an error message on my server. How can I use error handling in a proper way?

EDIT Updated current code + error message.

like image 259
BoJack Horseman Avatar asked Jun 23 '15 07:06

BoJack Horseman


People also ask

How does Python handle exceptions in MySQL?

By default, MySQL Connector/Python neither fetch warnings nor raise an exception on warnings. But, we can change that using the following arguments of the connect() function. If set to True warnings are fetched automatically after each query without having to manually execute SHOW WARNINGS query.

What is error handling in MySQL?

Error Handling. If an expected error is specified and that error occurs, mysqltest continues reading input. If the command is successful or a different error occurs, mysqltest aborts. If no expected error is specified, mysqltest aborts unless the command is successful.

How do you handle errors in Python?

The try block lets you test a block of code for errors. The except block lets you handle the error. The else block lets you execute code when there is no error. The finally block lets you execute code, regardless of the result of the try- and except blocks.


Video Answer


1 Answers

First point: you have too much code in your try/except block. Better to use distinct try/except blocks when you have two statements (or two groups of statements) that may raise different errors:

try:
    try:
        curs.execute(sql)
        # NB : you won't get an IntegrityError when reading
    except (MySQLdb.Error, MySQLdb.Warning) as e:
        print(e)
        return None

    try: 
        user = curs.fetchone()[0]
        return user
    except TypeError as e:
        print(e)
        return None

finally:
    conn.close()

Now do you really have to catch a TypeError here ? If you read at the traceback, you'll notice that your error comes from calling __getitem__() on None (nb : __getitem__() is implementation for the subscript operator []), which means that if you have no matching rows cursor.fetchone() returns None, so you can just test the return of currsor.fetchone():

try:
    try:
        curs.execute(sql)
        # NB : you won't get an IntegrityError when reading
    except (MySQLdb.Error, MySQLdb.Warning) as e:
        print(e)
        return None

    row = curs.fetchone()
    if row:
        return row[0]
    return None

finally:
    conn.close()

Now do you really need to catch MySQL errors here ? Your query is supposed to be well tested and it's only a read operation so it should not crash - so if you have something going wrong here then you obviously have a bigger problem, and you don't want to hide it under the carpet. IOW: either log the exceptions (using the standard logging package and logger.exception()) and re-raise them or more simply let them propagate (and eventually have an higher level component take care of logging unhandled exceptions):

try:
    curs.execute(sql)
    row = curs.fetchone()
    if row:
        return row[0]
    return None

finally:
    conn.close()

And finally: the way you build your sql query is utterly unsafe. Use sql placeholders instead:

q = "%s%%" % data["email"].strip() 
sql = "select userid from oc_preferences where configkey='email' and configvalue like %s"
cursor.execute(sql, [q,])

Oh and yes: wrt/ the "View function did not return a response" ValueError, it's because, well, your view returns None in many places. A flask view is supposed to return something that can be used as a HTTP response, and None is not a valid option here.

like image 62
bruno desthuilliers Avatar answered Sep 19 '22 19:09

bruno desthuilliers