raminfp · GitHub

Bug report

Bug description:

In Modules/_csv.c, the DIALECT_GETATTR macro in dialect_new() calls PyErr_Clear() unconditionally when PyObject_GetAttrString() fails. This silently swallows all exception types including MemoryError, KeyboardInterrupt, RuntimeError, etc.

https://github.com/python/cpython/blob/main/Modules/_csv.c#L500-L507

#define DIALECT_GETATTR(v, n)                            \
    do {                                                 \
        if (v == NULL) {                                 \
            v = PyObject_GetAttrString(dialect, n);      \
            if (v == NULL)                               \
                PyErr_Clear();                           \
        }                                                \
    } while (0)

Only AttributeError (attribute not found) should be cleared. Other exceptions should propagate.

Reproducer

import csv, io
class MyDialect:
    @property
    def delimiter(self):
        raise MemoryError("OOM")
    quotechar = '"'
    escapechar = None
    doublequote = True
    skipinitialspace = False
    quoting = csv.QUOTE_MINIMAL
    lineterminator = '\r\n'
    strict = False
# Expected: raises MemoryError (was swallowed)
w = csv.writer(io.StringIO(), dialect=MyDialect())
w.writerow(['a', 'b'])
print(w.dialect.delimiter)  # ',' 

All 8 dialect attributes are affected: delimiter, doublequote, escapechar, lineterminator, quotechar, quoting, skipinitialspace, strict.

Fix

#define DIALECT_GETATTR(v, n)                            \
    do {                                                 \
        if (v == NULL) {                                 \
            v = PyObject_GetAttrString(dialect, n);      \
            if (v == NULL) {                             \
                if (PyErr_ExceptionMatches(PyExc_AttributeError)) \
                    PyErr_Clear();                       \
                else                                     \
                    goto err;                            \
            }                                            \
        }                                                \
    } while (0)

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Read the original on github.com ↗