Unable to catch SQLAlchemy IntegrityError - python

If I try to commit a record to my database and it already exists then I want to use a try/except block to handle it:
from sqlalchemy.exc import IntegrityError
try:
session.commit()
except IntegrityError:
# do something else
However, I can't seem to catch the error. Here's a typical error that occurs on session.commit():
Exception has occurred: IntegrityError
(mysql.connector.errors.IntegrityError) 1062 (23000): Duplicate entry '88306-1' for key 'match_feat.idx_match_feat__composite'
I wondered if I was trying to catch the wrong error so I tried using:
from mysql.connector.errors import IntegrityError
But I get the same error message.
The answers to the question here focus on adding session.rollback() to the except: block however this doesn't help me (or the OP?) as I'm getting the error in the try: block and so the except: block isn't ever triggered.

Worked this out. The answers to the question here were correct. A rollback() is needed at the start of the except block. I'm not sure on the exact mechanics but the error was being caught on the first iteration of the code however if the transaction wasn't then rolled back then the second time through the error occurred in the try block and wasn't caught. This solved the issue:
try:
session.commit()
except IntegrityError:
session.rollback()
# do something else

I assume that the pass statement was used in the except block to tell Python to do nothing, for example:
try:
session.commit()
except IntegrityError:
pass
As the example in the question would result in an IndentationError being raised.
Based on your answer, it seems that the IntegrityError is being raised elsewhere in the code and not by session.commit() inside the try block. I suggest reviewing the stack trace generated when the exception is raised to find out where in your code this exception is being raised. The Python debugger can also be used to step through the code line-by-line.

Related

python ignore whichever line that causes error and continue running the code after that line

I understand the try/except method. What I'm trying to do is:
try:
some_func1() #potentially raises error too
do_something_else() #error was raised
continue_doing_something_else() #continues here after handling error
except:
pass
In the above code, when the error is raised at do_something_else(), the error is handled but then the try statement is exited.
What I want is for python to continue whatever code that is after the line that causes the error. Assuming that an error can happen anywhere in the try statement so I can't just wrap the try/except around do_something_else() itself, is there a way to do this in python?
What you want to do (try with restart) is not possible in Python. Lisp can do it (http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html), and you can implement it in Scheme using call/cc.
Just put the code that you want to do after the possible exception after the except. You may want to use finally (see https://docs.python.org/3/tutorial/errors.html for the documentation):
try:
some_func1()
do_something_else() #error was raised
except:
handle_exception()
finally:
continue_doing_something_else() #continues here after handling error or if no error occurs
if continue_doing_something_else() can also throw exceptions, then put that in a try/except too:
try:
some_func1()
do_something_else() #error was raised
except:
handle_exception1()
try:
continue_doing_something_else()
except:
handle_exception2()
finally:
any_cleanup()
as a general rule, your exception handling should be kept as small in scope as possible while remaining sensible, consider excepting only the 'expected' exceptions and not all of them (e.g. except OSError: when attempting to open files)

Is it ok to catch and reraise an exception inside Django transaction.atomic()?

Django's docs say this about transaction.atomic() and exceptions:
https://docs.djangoproject.com/en/1.10/topics/db/transactions/#django.db.transaction.atomic
Avoid catching exceptions inside atomic!
When exiting an atomic block, Django looks at whether it’s exited normally or with an exception to determine whether to commit or roll back. If you catch and handle exceptions inside an atomic block, you may hide from Django the fact that a problem has happened. This can result in unexpected behavior.
This is mostly a concern for DatabaseError and its subclasses such as IntegrityError. After such an error, the transaction is broken and Django will perform a rollback at the end of the atomic block. If you attempt to run database queries before the rollback happens, Django will raise a TransactionManagementError. You may also encounter this behavior when an ORM-related signal handler raises an exception.
The correct way to catch database errors is around an atomic block as shown above. If necessary, add an extra atomic block for this purpose. This pattern has another advantage: it delimits explicitly which operations will be rolled back if an exception occurs.
If you catch exceptions raised by raw SQL queries, Django’s behavior is unspecified and database-dependent.
Is it okay to do this or does this cause "unexpected behavior"?
with transaction.atomic():
# something
try:
# something
except:
logger.exception("Report error here.")
raise
Based on the docs, I would ensure that the correct Exception gets re-raised, others errors you might handle independently. For django it is only necessary that it gets notified regarding things that went wrong when talking to the database.
with transaction.atomic():
# something
try:
# something
except DatabaseError as db_err:
logger.exception("Report error here.")
raise db_err
except Exception:
# do something else
# no need to reraise
# as long as you handle it properly and db integrity is guaranteed
Is it okay to do this or does this cause "unexpected behavior"?
with transaction.atomic():
# something
try:
# something
except:
logger.exception("Report error here.")
raise
Except for the bare except clause (you want at least except Exception:), this is ok, assuming your logger doesn't touch the database (which would be a very bad idea anyway) and the logger call doesn't raise another exception (in which case I have no idea what will actually happen).
But you'd get the same result inverting the transaction.atomic() block and the try/except, ie:
try:
with transaction.atomic():
# something
# something
except Exception:
logger.exception("Report error here.")
raise
This example will clear your doubt.
with transaction.atomic():
try:
# if you do something that raises ONLY db error. ie. Integrity error
except Exception:
# and you catch that integrity error or any DB error like this here.
# now if you try to update your DB
model = Model.objects.get(id=1)
model.description = "new description"
model.save()
# This will be illegal to do and Django in this case
# will raise TransactionManagementError suggesting
# you cannot execute any queries until the end of atomic block.
Now in case you are raising your custom exception like this:
with transaction.atomic():
try:
# raising custom exception
raise Exception("some custom exception")
except Exception:
# and you catch that exception here
# now if you try to update your DB
model = Model.objects.get(id=1)
model.description = "new description"
model.save()
# Django will allow you to update the DB.

finally and rethowing of exception in except, raise in python

try:
#error code
except Exception as e:
print 'error',e
raise miexp("malicious error")
#userdefined exception, miexp
finally:
print 'finally'
Why the output is in the following formats?
Output:
error
finally
malicious error
Actually I expected as:
error
malicious error
finally
Why so?
miexp("malicious error") isn't handled, therefore it will end the execution of the program. On the other hand, the finally block is guaranteed to be executed.
To ensure this Python executes the finally block before actually raising the exception. From the documentation:
If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause.

creepy design in python

can somebody help me with the following piece of the code:
try:
#do some things
myClass.close()
except Exception, error:
myClass.close()
raise error
as You see I can't use here finally because in that case I will not know if an error actually occured, and I need to raise this error (raise error)
my question is how can I avoid using this two times myClass.close()? thanks in advance for any help
This is equivalent to your code:
try:
#do some things
finally:
myClass.close()
First it calls myClass.close() and if there was a error it is raised again.
You can also have this to handle specific exceptions:
try:
#do some things
except Exception, error:
raise # re-raise the original exception
finally:
myClass.close()
You can use finally :)
This should do what you want:
try:
#do some things
finally:
myClass.close()
You can chain try, except and finally since Python 2.5:
try:
# Do some things...
pass
except Exception, error:
# Log the error...
raise
finally:
myClass.close()
Next to a finally (that would work) you could also use a context manager that calls your myClass.close() method on exit.
Nest it.
try:
try:
...
except ...:
raise error
finally:
myClass.close()
Regardless this will not work.
If you already raised an exception/error the first time you invoked myClass.close(), calling it again will be of no help.
The error handling keywords are for implementing fallbacks, reporting and routing of the error-flow.
See here for error handling:
Can I get the exception from the finally block in python?

Trapping MySQL Warnings In Python

I would like to catch and log MySQL warnings in Python. For example, MySQL issues a warning to standard error if you submit 'DROP DATABASE IF EXISTS database_of_armaments' when no such database exists. I would like to catch this and log it, but even in the try/else syntax the warning message still appears.
The try/except syntax does catch MySQL errors (eg, submission of a typo like 'DRP DATABASE database_of_armaments').
I have experimented with <<except.MySQLdb.Warning>> -- no luck. I've looked at the warnings module, but don't understand how to incorporate it into the try/else syntax.
To be concrete, how do I get the following (or something like it) to work.
GIVEN: database 'database_of_armaments' does not exist.
try:
cursor.execute('DROP DATABASE IF EXISTS database_of_armaments')
except: <<WHAT DO I PUT HERE?>>
print 'There was a MySQL warning.'
<<AND what goes here if I want to get and manipulate information about the warning?>>
UPDATE:
Thanks for the comments. I had tried these and they didn't work -- but I had been using a DatabaseConnection class that I wrote for a connection, and its runQuery() method to execute. When I created a connection and cursor outside the class, the try/except Exception caught the "Programming Error", and except MySQLdb.ProgrammingError worked as advertised.
So now I have to figure out what is wrong with my class coding.
Thank you for your help.
Follow these steps.
Run it with except Exception, e: print repr(e).
See what exception you get.
Change the Exception to the exception you actually got.
Also, remember that the exception, e, is an object. You can print dir(e), e.__class__.__name__, etc.to see what attributes it has.
Also, you can do this interactively at the >>> prompt in Python. You can then manipulate the object directly -- no guessing.
Have you tried something like this?
try:
cursor.execute(some_statement)
except MySQLdb.IntegrityError, e:
# handle a specific error condition
except MySQLdb.Error, e:
# handle a generic error condition
except MySQLdb.Warning, e:
# handle warnings, if the cursor you're using raises them
I think the exception you want to catch is a MySQLdb.ProgrammingError, and to get information about it, just add a variable to store the error data (a tuple) in after that i.e:
try:
cursor.execute('DROP DATABASE IF EXISTS database_of_armaments')
except MySQLdb.ProgrammingError, e:
print 'There was a MySQL warning. This is the info we have about it: %s' %(e)
I managed to trap the mysql warning like this:
import _mysql_exceptions
try:
foo.bar()
except _mysql_exceptions.Warning, e:
pass
first you should turn on warnings to treat as exceptions, and only then you can catch them.
see "error" warnings filter - https://docs.python.org/3/library/warnings.html#the-warnings-filter
Plain and simple
def log_warnings(curs):
for msg in curs.messages:
if msg[0] == MySQLdb.Warning:
logging.warn(msg[1])
cursor.execute('DROP DATABASE IF EXISTS database_of_armaments')
log_warnings(cursor)
msg[1] example :- (u'Warning', 1366L, u"Incorrect integer value: '\xa3' for column 'in_userid' at row 1")

Categories