Skip to content

Commit dc24b8a

Browse files
raminfppicnixz
andauthored
gh-145966: Fix _csv DIALECT_GETATTR macro silently masking non-AttributeError exceptions (GH-145974)
The DIALECT_GETATTR macro in dialect_new() unconditionally called PyErr_Clear() when PyObject_GetAttrString() failed, which suppressed all exceptions including MemoryError, KeyboardInterrupt, and RuntimeError. Now only AttributeError is cleared; other exceptions propagate via the existing error handling path. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 1efd993 commit dc24b8a

File tree

3 files changed

+22
-7
lines changed

3 files changed

+22
-7
lines changed

Lib/test/test_csv.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,19 @@ class mydialect(csv.Dialect):
12801280
self.assertRaises(ValueError, create_invalid, field_name, " ",
12811281
skipinitialspace=True)
12821282

1283+
def test_dialect_getattr_non_attribute_error_propagates(self):
1284+
# gh-145966: non-AttributeError exceptions raised by __getattr__
1285+
# during dialect attribute lookup must propagate, not be silenced.
1286+
class BadDialect:
1287+
def __getattr__(self, name):
1288+
raise RuntimeError("boom")
1289+
1290+
with self.assertRaises(RuntimeError):
1291+
csv.reader([], dialect=BadDialect())
1292+
1293+
with self.assertRaises(RuntimeError):
1294+
csv.writer(StringIO(), dialect=BadDialect())
1295+
12831296

12841297
class TestSniffer(unittest.TestCase):
12851298
sample1 = """\
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Non-:exc:`AttributeError` exceptions raised during dialect attribute lookup
2+
in :mod:`csv` are no longer silently suppressed.

Modules/_csv.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -497,13 +497,13 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
497497
Py_XINCREF(skipinitialspace);
498498
Py_XINCREF(strict);
499499
if (dialect != NULL) {
500-
#define DIALECT_GETATTR(v, n) \
501-
do { \
502-
if (v == NULL) { \
503-
v = PyObject_GetAttrString(dialect, n); \
504-
if (v == NULL) \
505-
PyErr_Clear(); \
506-
} \
500+
#define DIALECT_GETATTR(v, n) \
501+
do { \
502+
if (v == NULL) { \
503+
if (PyObject_GetOptionalAttrString(dialect, n, &v) < 0) { \
504+
goto err; \
505+
} \
506+
} \
507507
} while (0)
508508
DIALECT_GETATTR(delimiter, "delimiter");
509509
DIALECT_GETATTR(doublequote, "doublequote");

0 commit comments

Comments
 (0)