decorators.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import sys
  2. from functools import wraps
  3. from .exceptions import TransactionAborted
  4. from .helpers import can_reconnect
  5. def auto_reconnect_cursor(func):
  6. """
  7. Attempt to safely reconnect when an error is hit that resembles the
  8. bouncer disconnecting the client due to a timeout/etc during a cursor
  9. execution.
  10. """
  11. @wraps(func)
  12. def inner(self, *args, **kwargs):
  13. try:
  14. return func(self, *args, **kwargs)
  15. except Exception as e:
  16. if not can_reconnect(e):
  17. raise
  18. self.db.close(reconnect=True)
  19. self.cursor = self.db._cursor()
  20. return func(self, *args, **kwargs)
  21. return inner
  22. def auto_reconnect_connection(func):
  23. """
  24. Attempt to safely reconnect when an error is hit that resembles the
  25. bouncer disconnecting the client due to a timeout/etc.
  26. """
  27. @wraps(func)
  28. def inner(self, *args, **kwargs):
  29. try:
  30. return func(self, *args, **kwargs)
  31. except Exception as e:
  32. if not can_reconnect(e):
  33. raise
  34. self.close(reconnect=True)
  35. return func(self, *args, **kwargs)
  36. return inner
  37. def capture_transaction_exceptions(func):
  38. """
  39. Catches database errors and reraises them on subsequent errors that throw
  40. some cruft about transaction aborted.
  41. """
  42. def raise_the_exception(conn, exc):
  43. if (
  44. "current transaction is aborted, commands ignored until end of transaction block"
  45. in str(exc)
  46. ):
  47. exc_info = getattr(conn, "_last_exception", None)
  48. if exc_info is None:
  49. raise
  50. new_exc = TransactionAborted(sys.exc_info(), exc_info)
  51. raise new_exc.with_traceback(exc_info[2])
  52. conn._last_exception = sys.exc_info()
  53. raise
  54. @wraps(func)
  55. def inner(self, *args, **kwargs):
  56. try:
  57. return func(self, *args, **kwargs)
  58. except Exception as e:
  59. raise_the_exception(self.db, e)
  60. return inner
  61. def more_better_error_messages(func):
  62. """
  63. Wraps functions where the first param is a SQL statement and enforces
  64. any exceptions thrown will also contain the statement in the message.
  65. """
  66. @wraps(func)
  67. def inner(self, sql, *args, **kwargs):
  68. try:
  69. return func(self, sql, *args, **kwargs)
  70. except Exception as e:
  71. exc_info = sys.exc_info()
  72. msg = f"{e!r}\nSQL: {sql}"
  73. raise exc_info[0](msg).with_traceback(exc_info[2])
  74. return inner