Skip to content

Commit

Permalink
pythongh-129354: Use PyErr_FormatUnraisable() function
Browse files Browse the repository at this point in the history
Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().

Update test_sqlite3 tests.
  • Loading branch information
vstinner committed Jan 31, 2025
1 parent 79f85a0 commit 13da83e
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Lib/test/test_sqlite3/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def progress():
con.execute("select 1 union select 2 union select 3").fetchall()
self.assertEqual(action, 0, "progress handler was not cleared")

@with_tracebacks(ZeroDivisionError, name="bad_progress")
@with_tracebacks(ZeroDivisionError, msg_regex="bad_progress")
def test_error_in_progress_handler(self):
def bad_progress():
1 / 0
Expand All @@ -206,7 +206,7 @@ def bad_progress():
create table foo(a, b)
""")

@with_tracebacks(ZeroDivisionError, name="bad_progress")
@with_tracebacks(ZeroDivisionError, msg_regex="bad_progress")
def test_error_in_progress_handler_result(self):
class BadBool:
def __bool__(self):
Expand Down
20 changes: 10 additions & 10 deletions Lib/test/test_sqlite3/test_userfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,22 +254,22 @@ def test_func_return_nan(self):
cur.execute("select returnnan()")
self.assertIsNone(cur.fetchone()[0])

@with_tracebacks(ZeroDivisionError, name="func_raiseexception")
@with_tracebacks(ZeroDivisionError, msg_regex="func_raiseexception")
def test_func_exception(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.OperationalError) as cm:
cur.execute("select raiseexception()")
cur.fetchone()
self.assertEqual(str(cm.exception), 'user-defined function raised exception')

@with_tracebacks(MemoryError, name="func_memoryerror")
@with_tracebacks(MemoryError, msg_regex="func_memoryerror")
def test_func_memory_error(self):
cur = self.con.cursor()
with self.assertRaises(MemoryError):
cur.execute("select memoryerror()")
cur.fetchone()

@with_tracebacks(OverflowError, name="func_overflowerror")
@with_tracebacks(OverflowError, msg_regex="func_overflowerror")
def test_func_overflow_error(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.DataError):
Expand Down Expand Up @@ -389,7 +389,7 @@ def test_func_return_too_large_int(self):
with self.assertRaisesRegex(sqlite.DataError, msg):
cur.execute("select largeint()")

@with_tracebacks(UnicodeEncodeError, "surrogates not allowed", "chr")
@with_tracebacks(UnicodeEncodeError, "surrogates not allowed")
def test_func_return_text_with_surrogates(self):
cur = self.con.cursor()
self.con.create_function("pychr", 1, chr)
Expand Down Expand Up @@ -641,7 +641,7 @@ def test_aggr_error_on_create(self):
with self.assertRaises(sqlite.OperationalError):
self.con.create_function("bla", -100, AggrSum)

@with_tracebacks(AttributeError, name="AggrNoStep")
@with_tracebacks(AttributeError, msg_regex="AggrNoStep")
def test_aggr_no_step(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.OperationalError) as cm:
Expand All @@ -656,23 +656,23 @@ def test_aggr_no_finalize(self):
cur.execute("select nofinalize(t) from test")
val = cur.fetchone()[0]

@with_tracebacks(ZeroDivisionError, name="AggrExceptionInInit")
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInInit")
def test_aggr_exception_in_init(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.OperationalError) as cm:
cur.execute("select excInit(t) from test")
val = cur.fetchone()[0]
self.assertEqual(str(cm.exception), "user-defined aggregate's '__init__' method raised error")

@with_tracebacks(ZeroDivisionError, name="AggrExceptionInStep")
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInStep")
def test_aggr_exception_in_step(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.OperationalError) as cm:
cur.execute("select excStep(t) from test")
val = cur.fetchone()[0]
self.assertEqual(str(cm.exception), "user-defined aggregate's 'step' method raised error")

@with_tracebacks(ZeroDivisionError, name="AggrExceptionInFinalize")
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInFinalize")
def test_aggr_exception_in_finalize(self):
cur = self.con.cursor()
with self.assertRaises(sqlite.OperationalError) as cm:
Expand Down Expand Up @@ -822,11 +822,11 @@ def authorizer_cb(action, arg1, arg2, dbname, source):
raise ValueError
return sqlite.SQLITE_OK

@with_tracebacks(ValueError, name="authorizer_cb")
@with_tracebacks(ValueError, msg_regex="authorizer_cb")
def test_table_access(self):
super().test_table_access()

@with_tracebacks(ValueError, name="authorizer_cb")
@with_tracebacks(ValueError, msg_regex="authorizer_cb")
def test_column_access(self):
super().test_table_access()

Expand Down
16 changes: 10 additions & 6 deletions Lib/test/test_sqlite3/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ def cx_limit(cx, category=sqlite3.SQLITE_LIMIT_SQL_LENGTH, limit=128):
cx.setlimit(category, _prev)


def with_tracebacks(exc, regex="", name=""):
def with_tracebacks(exc, regex="", name="", msg_regex=""):
"""Convenience decorator for testing callback tracebacks."""
def decorator(func):
_regex = re.compile(regex) if regex else None
exc_regex = re.compile(regex) if regex else None
_msg_regex = re.compile(msg_regex) if msg_regex else None
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
with test.support.catch_unraisable_exception() as cm:
# First, run the test with traceback enabled.
with check_tracebacks(self, cm, exc, _regex, name):
with check_tracebacks(self, cm, exc, exc_regex, _msg_regex, name):
func(self, *args, **kwargs)

# Then run the test with traceback disabled.
Expand All @@ -40,7 +41,7 @@ def wrapper(self, *args, **kwargs):


@contextlib.contextmanager
def check_tracebacks(self, cm, exc, regex, obj_name):
def check_tracebacks(self, cm, exc, exc_regex, msg_regex, obj_name):
"""Convenience context manager for testing callback tracebacks."""
sqlite3.enable_callback_tracebacks(True)
try:
Expand All @@ -49,9 +50,12 @@ def check_tracebacks(self, cm, exc, regex, obj_name):
yield

self.assertEqual(cm.unraisable.exc_type, exc)
if regex:
if exc_regex:
msg = str(cm.unraisable.exc_value)
self.assertIsNotNone(regex.search(msg))
self.assertIsNotNone(exc_regex.search(msg), (exc_regex, msg))
if msg_regex:
msg = cm.unraisable.err_msg
self.assertIsNotNone(msg_regex.search(msg), (msg_regex, msg))
if obj_name:
self.assertEqual(cm.unraisable.object.__name__, obj_name)
finally:
Expand Down
9 changes: 6 additions & 3 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ connection_finalize(PyObject *self)
if (PyErr_ResourceWarning(self, 1, "unclosed database in %R", self)) {
/* Spurious errors can appear at shutdown */
if (PyErr_ExceptionMatches(PyExc_Warning)) {
PyErr_WriteUnraisable(self);
PyErr_FormatUnraisable("Exception ignored while finalizing "
"database %R", self);
}
}
}
Expand All @@ -506,7 +507,8 @@ connection_finalize(PyObject *self)
PyErr_Clear();
}
else {
PyErr_WriteUnraisable((PyObject *)self);
PyErr_FormatUnraisable("Exception ignored while closing database %R",
self);
}
}

Expand Down Expand Up @@ -893,7 +895,8 @@ print_or_clear_traceback(callback_context *ctx)
assert(ctx != NULL);
assert(ctx->state != NULL);
if (ctx->state->enable_callback_tracebacks) {
PyErr_WriteUnraisable(ctx->callable);
PyErr_FormatUnraisable("Exception ignored on sqlite callable %R",
ctx->callable);
}
else {
PyErr_Clear();
Expand Down

0 comments on commit 13da83e

Please sign in to comment.