9

I'm trying to catch mysql/sqlalchemy OperationalErrors and replace handle access denied (1045) differently from connection refused (2003)

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user … (Background on this error at: http://sqlalche.me/e/e3q8)
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)") (Background on this error at: http://sqlalche.me/e/e3q8)

I just can't seem to find any documentation on how to tell these apart programmatically. I dived into the sources and thought I could check the value of err.orig.original_exception.errno but that was not the case.

Edit: err.orig doesn't seem to be defined for access denied which might be a bug.

try:
  engine.scalar(select([1]))
except sqlalchemy.exc.OperationalError as err:
  if err_______:
    print("Access Denied")
  elifif err_______:
    print("Connection Refused")
  else:
    raise
Gamification
  • 787
  • 5
  • 20
  • 1
    Similar question from 2016 without an answer: https://stackoverflow.com/q/39106138/3080094 – vanOekel Jan 21 '19 at 18:50
  • How could I not find that, thank you so much! – Gamification Jan 21 '19 at 22:12
  • have you tried `e.errno`? Reading this: https://github.com/sqlalchemy/sqlalchemy/blob/6f270fb0e344016ce526e0a1ecb3e3de9ffd1e3b/lib/sqlalchemy/dialects/mysql/mysqlconnector.py#L233 I think OperationalErrors from `mysqlconnector.py` should have the property `errno` containing the error number. – spaniard Jan 23 '19 at 17:38

2 Answers2

4

After some more research, I found the mysql error code to be in err.orig.args[0]. So the Answer is:

try:
  engine.scalar(select([1]))
except sqlalchemy.exc.OperationalError as err:
  if err.orig.args[0]==1045:
    print("Access Denied")
  elif err.orig.args[0]==2003:
    print("Connection Refused")
  else:
    raise
Gamification
  • 787
  • 5
  • 20
2

Try err.args[0]

try:
  engine.scalar(select([1]))
except sqlalchemy.exc.OperationalError as err:
  if err.args[0] == 1045:
    print("Access Denied")
  elif err.args[0] == 2003:
    print("Connection Refused")
  else:
    raise

This should be what you're looking for. Refer to the documentation for more reading

Edit

As OperationalError wraps DBAPIError, that has a code argument. Most likely just replace args[0] with code. Like so:

try:
  engine.scalar(select([1]))
except sqlalchemy.exc.OperationalError as err:
  if err.code == 1045:
    print("Access Denied")
  elif err.code == 2003:
    print("Connection Refused")
  else:
    raise
Jab
  • 26,853
  • 21
  • 75
  • 114
  • args[0] is a string containing the above error messages. But comparing error message strings can't be the right solution here – Gamification Jan 09 '19 at 00:10
  • This is a sqlalchemy code and only means operational error, which becomes obvious when looking at err.py – Gamification Jan 09 '19 at 00:21
  • I've been at this for a while and it's much harder than it looks. I found out isinstance(err.orig.original_exception,ConnectionRefusedError) works for the connection refused case only. Otherwise orig is None apparently – Gamification Jan 09 '19 at 00:22
  • I read a lot of source and documentation, the links aren't helping. I assume that I'm supposed to be looking at err.orig and the fact that that isn't defined for permission denied might be a bug I'll have to hack around – and end up comparing error message strings. I'll investigate further tomorrow, until that, I hope someone familiar with this looks into it. – Gamification Jan 09 '19 at 00:55
  • I hope you find your answer, I'm not proficient with SQLAlchemy – Jab Jan 09 '19 at 01:17