summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Modules/_csv.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/src/Modules/_csv.c')
-rw-r--r--contrib/tools/python3/src/Modules/_csv.c566
1 files changed, 314 insertions, 252 deletions
diff --git a/contrib/tools/python3/src/Modules/_csv.c b/contrib/tools/python3/src/Modules/_csv.c
index 029f473ae87..72f0791a439 100644
--- a/contrib/tools/python3/src/Modules/_csv.c
+++ b/contrib/tools/python3/src/Modules/_csv.c
@@ -18,9 +18,14 @@ module instead.
typedef struct {
PyObject *error_obj; /* CSV exception */
PyObject *dialects; /* Dialect registry */
+ PyTypeObject *dialect_type;
+ PyTypeObject *reader_type;
+ PyTypeObject *writer_type;
long field_limit; /* max parsed field size */
} _csvstate;
+static struct PyModuleDef _csvmodule;
+
static inline _csvstate*
get_csv_state(PyObject *module)
{
@@ -30,31 +35,35 @@ get_csv_state(PyObject *module)
}
static int
-_csv_clear(PyObject *m)
+_csv_clear(PyObject *module)
{
- Py_CLEAR(get_csv_state(m)->error_obj);
- Py_CLEAR(get_csv_state(m)->dialects);
+ _csvstate *module_state = PyModule_GetState(module);
+ Py_CLEAR(module_state->error_obj);
+ Py_CLEAR(module_state->dialects);
+ Py_CLEAR(module_state->dialect_type);
+ Py_CLEAR(module_state->reader_type);
+ Py_CLEAR(module_state->writer_type);
return 0;
}
static int
-_csv_traverse(PyObject *m, visitproc visit, void *arg)
+_csv_traverse(PyObject *module, visitproc visit, void *arg)
{
- Py_VISIT(get_csv_state(m)->error_obj);
- Py_VISIT(get_csv_state(m)->dialects);
+ _csvstate *module_state = PyModule_GetState(module);
+ Py_VISIT(module_state->error_obj);
+ Py_VISIT(module_state->dialects);
+ Py_VISIT(module_state->dialect_type);
+ Py_VISIT(module_state->reader_type);
+ Py_VISIT(module_state->writer_type);
return 0;
}
static void
-_csv_free(void *m)
+_csv_free(void *module)
{
- _csv_clear((PyObject *)m);
+ _csv_clear((PyObject *)module);
}
-static struct PyModuleDef _csvmodule;
-
-#define _csvstate_global ((_csvstate *)PyModule_GetState(PyState_FindModule(&_csvmodule)))
-
typedef enum {
START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD,
IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD,
@@ -92,8 +101,6 @@ typedef struct {
} DialectObj;
-static PyTypeObject Dialect_Type;
-
typedef struct {
PyObject_HEAD
@@ -110,10 +117,6 @@ typedef struct {
unsigned long line_num; /* Source-file line number */
} ReaderObj;
-static PyTypeObject Reader_Type;
-
-#define ReaderObject_Check(v) Py_IS_TYPE(v, &Reader_Type)
-
typedef struct {
PyObject_HEAD
@@ -125,34 +128,28 @@ typedef struct {
Py_ssize_t rec_size; /* size of allocated record */
Py_ssize_t rec_len; /* length of record */
int num_fields; /* number of fields in record */
-} WriterObj;
-static PyTypeObject Writer_Type;
+ PyObject *error_obj; /* cached error object */
+} WriterObj;
/*
* DIALECT class
*/
static PyObject *
-get_dialect_from_registry(PyObject * name_obj)
+get_dialect_from_registry(PyObject *name_obj, _csvstate *module_state)
{
PyObject *dialect_obj;
- dialect_obj = PyDict_GetItemWithError(_csvstate_global->dialects, name_obj);
+ dialect_obj = PyDict_GetItemWithError(module_state->dialects, name_obj);
if (dialect_obj == NULL) {
if (!PyErr_Occurred())
- PyErr_Format(_csvstate_global->error_obj, "unknown dialect");
+ PyErr_Format(module_state->error_obj, "unknown dialect");
}
else
Py_INCREF(dialect_obj);
- return dialect_obj;
-}
-static PyObject *
-get_string(PyObject *str)
-{
- Py_XINCREF(str);
- return str;
+ return dialect_obj;
}
static PyObject *
@@ -168,7 +165,8 @@ get_nullchar_as_None(Py_UCS4 c)
static PyObject *
Dialect_get_lineterminator(DialectObj *self, void *Py_UNUSED(ignored))
{
- return get_string(self->lineterminator);
+ Py_XINCREF(self->lineterminator);
+ return self->lineterminator;
}
static PyObject *
@@ -353,8 +351,11 @@ static PyGetSetDef Dialect_getsetlist[] = {
static void
Dialect_dealloc(DialectObj *self)
{
- Py_XDECREF(self->lineterminator);
- Py_TYPE(self)->tp_free((PyObject *)self);
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ tp->tp_clear((PyObject *)self);
+ PyObject_GC_Del(self);
+ Py_DECREF(tp);
}
static char *dialect_kws[] = {
@@ -370,6 +371,22 @@ static char *dialect_kws[] = {
NULL
};
+static _csvstate *
+_csv_state_from_type(PyTypeObject *type, const char *name)
+{
+ PyObject *module = _PyType_GetModuleByDef(type, &_csvmodule);
+ if (module == NULL) {
+ return NULL;
+ }
+ _csvstate *module_state = PyModule_GetState(module);
+ if (module_state == NULL) {
+ PyErr_Format(PyExc_SystemError,
+ "%s: No _csv module state found", name);
+ return NULL;
+ }
+ return module_state;
+}
+
static PyObject *
dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
@@ -398,16 +415,21 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
&strict))
return NULL;
+ _csvstate *module_state = _csv_state_from_type(type, "dialect_new");
+ if (module_state == NULL) {
+ return NULL;
+ }
+
if (dialect != NULL) {
if (PyUnicode_Check(dialect)) {
- dialect = get_dialect_from_registry(dialect);
+ dialect = get_dialect_from_registry(dialect, module_state);
if (dialect == NULL)
return NULL;
}
else
Py_INCREF(dialect);
/* Can we reuse this instance? */
- if (PyObject_TypeCheck(dialect, &Dialect_Type) &&
+ if (PyObject_TypeCheck(dialect, module_state->dialect_type) &&
delimiter == NULL &&
doublequote == NULL &&
escapechar == NULL &&
@@ -421,7 +443,7 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
self = (DialectObj *)type->tp_alloc(type, 0);
if (self == NULL) {
- Py_XDECREF(dialect);
+ Py_CLEAR(dialect);
return NULL;
}
self->lineterminator = NULL;
@@ -489,76 +511,86 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
ret = (PyObject *)self;
Py_INCREF(self);
err:
- Py_XDECREF(self);
- Py_XDECREF(dialect);
- Py_XDECREF(delimiter);
- Py_XDECREF(doublequote);
- Py_XDECREF(escapechar);
- Py_XDECREF(lineterminator);
- Py_XDECREF(quotechar);
- Py_XDECREF(quoting);
- Py_XDECREF(skipinitialspace);
- Py_XDECREF(strict);
+ Py_CLEAR(self);
+ Py_CLEAR(dialect);
+ Py_CLEAR(delimiter);
+ Py_CLEAR(doublequote);
+ Py_CLEAR(escapechar);
+ Py_CLEAR(lineterminator);
+ Py_CLEAR(quotechar);
+ Py_CLEAR(quoting);
+ Py_CLEAR(skipinitialspace);
+ Py_CLEAR(strict);
return ret;
}
+/* Since dialect is now a heap type, it inherits pickling method for
+ * protocol 0 and 1 from object, therefore it needs to be overridden */
+
+PyDoc_STRVAR(dialect_reduce_doc, "raises an exception to avoid pickling");
+
+static PyObject *
+Dialect_reduce(PyObject *self, PyObject *args) {
+ PyErr_Format(PyExc_TypeError,
+ "cannot pickle '%.100s' instances", _PyType_Name(Py_TYPE(self)));
+ return NULL;
+}
+
+static struct PyMethodDef dialect_methods[] = {
+ {"__reduce__", Dialect_reduce, METH_VARARGS, dialect_reduce_doc},
+ {"__reduce_ex__", Dialect_reduce, METH_VARARGS, dialect_reduce_doc},
+ {NULL, NULL}
+};
PyDoc_STRVAR(Dialect_Type_doc,
"CSV dialect\n"
"\n"
"The Dialect type records CSV parsing and generation options.\n");
-static PyTypeObject Dialect_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_csv.Dialect", /* tp_name */
- sizeof(DialectObj), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)Dialect_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- (getattrfunc)0, /* tp_getattr */
- (setattrfunc)0, /* tp_setattr */
- 0, /* tp_as_async */
- (reprfunc)0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- (hashfunc)0, /* tp_hash */
- (ternaryfunc)0, /* tp_call */
- (reprfunc)0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
- Dialect_Type_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- Dialect_memberlist, /* tp_members */
- Dialect_getsetlist, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- dialect_new, /* tp_new */
- 0, /* tp_free */
+static int
+Dialect_clear(DialectObj *self)
+{
+ Py_CLEAR(self->lineterminator);
+ return 0;
+}
+
+static int
+Dialect_traverse(DialectObj *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->lineterminator);
+ Py_VISIT(Py_TYPE(self));
+ return 0;
+}
+
+static PyType_Slot Dialect_Type_slots[] = {
+ {Py_tp_doc, (char*)Dialect_Type_doc},
+ {Py_tp_members, Dialect_memberlist},
+ {Py_tp_getset, Dialect_getsetlist},
+ {Py_tp_new, dialect_new},
+ {Py_tp_methods, dialect_methods},
+ {Py_tp_dealloc, Dialect_dealloc},
+ {Py_tp_clear, Dialect_clear},
+ {Py_tp_traverse, Dialect_traverse},
+ {0, NULL}
+};
+
+PyType_Spec Dialect_Type_spec = {
+ .name = "_csv.Dialect",
+ .basicsize = sizeof(DialectObj),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = Dialect_Type_slots,
};
+
/*
* Return an instance of the dialect type, given a Python instance or kwarg
* description of the dialect
*/
static PyObject *
-_call_dialect(PyObject *dialect_inst, PyObject *kwargs)
+_call_dialect(_csvstate *module_state, PyObject *dialect_inst, PyObject *kwargs)
{
- PyObject *type = (PyObject *)&Dialect_Type;
+ PyObject *type = (PyObject *)module_state->dialect_type;
if (dialect_inst) {
return PyObject_VectorcallDict(type, &dialect_inst, 1, kwargs);
}
@@ -616,11 +648,12 @@ parse_grow_buff(ReaderObj *self)
}
static int
-parse_add_char(ReaderObj *self, Py_UCS4 c)
+parse_add_char(ReaderObj *self, _csvstate *module_state, Py_UCS4 c)
{
- if (self->field_len >= _csvstate_global->field_limit) {
- PyErr_Format(_csvstate_global->error_obj, "field larger than field limit (%ld)",
- _csvstate_global->field_limit);
+ if (self->field_len >= module_state->field_limit) {
+ PyErr_Format(module_state->error_obj,
+ "field larger than field limit (%ld)",
+ module_state->field_limit);
return -1;
}
if (self->field_len == self->field_size && !parse_grow_buff(self))
@@ -630,7 +663,7 @@ parse_add_char(ReaderObj *self, Py_UCS4 c)
}
static int
-parse_process_char(ReaderObj *self, Py_UCS4 c)
+parse_process_char(ReaderObj *self, _csvstate *module_state, Py_UCS4 c)
{
DialectObj *dialect = self->dialect;
@@ -676,7 +709,7 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
/* begin new unquoted field */
if (dialect->quoting == QUOTE_NONNUMERIC)
self->numeric_field = 1;
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = IN_FIELD;
}
@@ -684,14 +717,14 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
case ESCAPED_CHAR:
if (c == '\n' || c=='\r') {
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = AFTER_ESCAPED_CRNL;
break;
}
if (c == '\0')
c = '\n';
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = IN_FIELD;
break;
@@ -721,7 +754,7 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
}
else {
/* normal character - save in field */
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
}
break;
@@ -747,7 +780,7 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
}
else {
/* normal character - save in field */
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
}
break;
@@ -755,7 +788,7 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
case ESCAPE_IN_QUOTED_FIELD:
if (c == '\0')
c = '\n';
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = IN_QUOTED_FIELD;
break;
@@ -765,7 +798,7 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
if (dialect->quoting != QUOTE_NONE &&
c == dialect->quotechar) {
/* save "" as " */
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = IN_QUOTED_FIELD;
}
@@ -782,13 +815,13 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
self->state = (c == '\0' ? START_RECORD : EAT_CRNL);
}
else if (!dialect->strict) {
- if (parse_add_char(self, c) < 0)
+ if (parse_add_char(self, module_state, c) < 0)
return -1;
self->state = IN_FIELD;
}
else {
/* illegal */
- PyErr_Format(_csvstate_global->error_obj, "'%c' expected after '%c'",
+ PyErr_Format(module_state->error_obj, "'%c' expected after '%c'",
dialect->delimiter,
dialect->quotechar);
return -1;
@@ -801,7 +834,8 @@ parse_process_char(ReaderObj *self, Py_UCS4 c)
else if (c == '\0')
self->state = START_RECORD;
else {
- PyErr_Format(_csvstate_global->error_obj, "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");
+ PyErr_Format(module_state->error_obj,
+ "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");
return -1;
}
break;
@@ -832,6 +866,12 @@ Reader_iternext(ReaderObj *self)
const void *data;
PyObject *lineobj;
+ _csvstate *module_state = _csv_state_from_type(Py_TYPE(self),
+ "Reader.__next__");
+ if (module_state == NULL) {
+ return NULL;
+ }
+
if (parse_reset(self) < 0)
return NULL;
do {
@@ -841,7 +881,7 @@ Reader_iternext(ReaderObj *self)
if (!PyErr_Occurred() && (self->field_len != 0 ||
self->state == IN_QUOTED_FIELD)) {
if (self->dialect->strict)
- PyErr_SetString(_csvstate_global->error_obj,
+ PyErr_SetString(module_state->error_obj,
"unexpected end of data");
else if (parse_save_field(self) >= 0)
break;
@@ -849,10 +889,10 @@ Reader_iternext(ReaderObj *self)
return NULL;
}
if (!PyUnicode_Check(lineobj)) {
- PyErr_Format(_csvstate_global->error_obj,
+ PyErr_Format(module_state->error_obj,
"iterator should return strings, "
"not %.200s "
- "(did you open the file in text mode?)",
+ "(the file should be opened in text mode)",
Py_TYPE(lineobj)->tp_name
);
Py_DECREF(lineobj);
@@ -871,18 +911,18 @@ Reader_iternext(ReaderObj *self)
c = PyUnicode_READ(kind, data, pos);
if (c == '\0') {
Py_DECREF(lineobj);
- PyErr_Format(_csvstate_global->error_obj,
+ PyErr_Format(module_state->error_obj,
"line contains NUL");
goto err;
}
- if (parse_process_char(self, c) < 0) {
+ if (parse_process_char(self, module_state, c) < 0) {
Py_DECREF(lineobj);
goto err;
}
pos++;
}
Py_DECREF(lineobj);
- if (parse_process_char(self, 0) < 0)
+ if (parse_process_char(self, module_state, 0) < 0)
goto err;
} while (self->state != START_RECORD);
@@ -895,13 +935,15 @@ err:
static void
Reader_dealloc(ReaderObj *self)
{
+ PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
- Py_XDECREF(self->dialect);
- Py_XDECREF(self->input_iter);
- Py_XDECREF(self->fields);
- if (self->field != NULL)
+ tp->tp_clear((PyObject *)self);
+ if (self->field != NULL) {
PyMem_Free(self->field);
+ self->field = NULL;
+ }
PyObject_GC_Del(self);
+ Py_DECREF(tp);
}
static int
@@ -910,6 +952,7 @@ Reader_traverse(ReaderObj *self, visitproc visit, void *arg)
Py_VISIT(self->dialect);
Py_VISIT(self->input_iter);
Py_VISIT(self->fields);
+ Py_VISIT(Py_TYPE(self));
return 0;
}
@@ -941,47 +984,35 @@ static struct PyMemberDef Reader_memberlist[] = {
};
-static PyTypeObject Reader_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_csv.reader", /*tp_name*/
- sizeof(ReaderObj), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)Reader_dealloc, /*tp_dealloc*/
- 0, /*tp_vectorcall_offset*/
- (getattrfunc)0, /*tp_getattr*/
- (setattrfunc)0, /*tp_setattr*/
- 0, /*tp_as_async*/
- (reprfunc)0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- (hashfunc)0, /*tp_hash*/
- (ternaryfunc)0, /*tp_call*/
- (reprfunc)0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_HAVE_GC, /*tp_flags*/
- Reader_Type_doc, /*tp_doc*/
- (traverseproc)Reader_traverse, /*tp_traverse*/
- (inquiry)Reader_clear, /*tp_clear*/
- 0, /*tp_richcompare*/
- 0, /*tp_weaklistoffset*/
- PyObject_SelfIter, /*tp_iter*/
- (getiterfunc)Reader_iternext, /*tp_iternext*/
- Reader_methods, /*tp_methods*/
- Reader_memberlist, /*tp_members*/
- 0, /*tp_getset*/
+static PyType_Slot Reader_Type_slots[] = {
+ {Py_tp_doc, (char*)Reader_Type_doc},
+ {Py_tp_traverse, Reader_traverse},
+ {Py_tp_iter, PyObject_SelfIter},
+ {Py_tp_iternext, Reader_iternext},
+ {Py_tp_methods, Reader_methods},
+ {Py_tp_members, Reader_memberlist},
+ {Py_tp_clear, Reader_clear},
+ {Py_tp_dealloc, Reader_dealloc},
+ {0, NULL}
+};
+PyType_Spec Reader_Type_spec = {
+ .name = "_csv.reader",
+ .basicsize = sizeof(ReaderObj),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = Reader_Type_slots
};
+
static PyObject *
csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args)
{
PyObject * iterator, * dialect = NULL;
- ReaderObj * self = PyObject_GC_New(ReaderObj, &Reader_Type);
+ _csvstate *module_state = get_csv_state(module);
+ ReaderObj * self = PyObject_GC_New(
+ ReaderObj,
+ module_state->reader_type);
if (!self)
return NULL;
@@ -1007,7 +1038,8 @@ csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args)
Py_DECREF(self);
return NULL;
}
- self->dialect = (DialectObj *)_call_dialect(dialect, keyword_args);
+ self->dialect = (DialectObj *)_call_dialect(module_state, dialect,
+ keyword_args);
if (self->dialect == NULL) {
Py_DECREF(self);
return NULL;
@@ -1088,12 +1120,15 @@ join_append_data(WriterObj *self, unsigned int field_kind, const void *field_dat
else
want_escape = 1;
}
+ else if (c == dialect->escapechar) {
+ want_escape = 1;
+ }
if (!want_escape)
*quoted = 1;
}
if (want_escape) {
if (!dialect->escapechar) {
- PyErr_Format(_csvstate_global->error_obj,
+ PyErr_Format(self->error_obj,
"need to escape, but no escapechar set");
return -1;
}
@@ -1211,7 +1246,7 @@ csv_writerow(WriterObj *self, PyObject *seq)
iter = PyObject_GetIter(seq);
if (iter == NULL) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
- PyErr_Format(_csvstate_global->error_obj,
+ PyErr_Format(self->error_obj,
"iterable expected, not %.200s",
Py_TYPE(seq)->tp_name);
}
@@ -1268,7 +1303,7 @@ csv_writerow(WriterObj *self, PyObject *seq)
if (self->num_fields > 0 && self->rec_len == 0) {
if (dialect->quoting == QUOTE_NONE) {
- PyErr_Format(_csvstate_global->error_obj,
+ PyErr_Format(self->error_obj,
"single empty field record must be quoted");
return NULL;
}
@@ -1337,22 +1372,13 @@ static struct PyMemberDef Writer_memberlist[] = {
{ NULL }
};
-static void
-Writer_dealloc(WriterObj *self)
-{
- PyObject_GC_UnTrack(self);
- Py_XDECREF(self->dialect);
- Py_XDECREF(self->write);
- if (self->rec != NULL)
- PyMem_Free(self->rec);
- PyObject_GC_Del(self);
-}
-
static int
Writer_traverse(WriterObj *self, visitproc visit, void *arg)
{
Py_VISIT(self->dialect);
Py_VISIT(self->write);
+ Py_VISIT(self->error_obj);
+ Py_VISIT(Py_TYPE(self));
return 0;
}
@@ -1361,9 +1387,23 @@ Writer_clear(WriterObj *self)
{
Py_CLEAR(self->dialect);
Py_CLEAR(self->write);
+ Py_CLEAR(self->error_obj);
return 0;
}
+static void
+Writer_dealloc(WriterObj *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ tp->tp_clear((PyObject *)self);
+ if (self->rec != NULL) {
+ PyMem_Free(self->rec);
+ }
+ PyObject_GC_Del(self);
+ Py_DECREF(tp);
+}
+
PyDoc_STRVAR(Writer_Type_doc,
"CSV writer\n"
"\n"
@@ -1371,46 +1411,31 @@ PyDoc_STRVAR(Writer_Type_doc,
"in CSV format from sequence input.\n"
);
-static PyTypeObject Writer_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_csv.writer", /*tp_name*/
- sizeof(WriterObj), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)Writer_dealloc, /*tp_dealloc*/
- 0, /*tp_vectorcall_offset*/
- (getattrfunc)0, /*tp_getattr*/
- (setattrfunc)0, /*tp_setattr*/
- 0, /*tp_as_async*/
- (reprfunc)0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- (hashfunc)0, /*tp_hash*/
- (ternaryfunc)0, /*tp_call*/
- (reprfunc)0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_HAVE_GC, /*tp_flags*/
- Writer_Type_doc,
- (traverseproc)Writer_traverse, /*tp_traverse*/
- (inquiry)Writer_clear, /*tp_clear*/
- 0, /*tp_richcompare*/
- 0, /*tp_weaklistoffset*/
- (getiterfunc)0, /*tp_iter*/
- (getiterfunc)0, /*tp_iternext*/
- Writer_methods, /*tp_methods*/
- Writer_memberlist, /*tp_members*/
- 0, /*tp_getset*/
+static PyType_Slot Writer_Type_slots[] = {
+ {Py_tp_doc, (char*)Writer_Type_doc},
+ {Py_tp_traverse, Writer_traverse},
+ {Py_tp_clear, Writer_clear},
+ {Py_tp_dealloc, Writer_dealloc},
+ {Py_tp_methods, Writer_methods},
+ {Py_tp_members, Writer_memberlist},
+ {0, NULL}
};
+PyType_Spec Writer_Type_spec = {
+ .name = "_csv.writer",
+ .basicsize = sizeof(WriterObj),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = Writer_Type_slots,
+};
+
+
static PyObject *
csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
{
PyObject * output_file, * dialect = NULL;
- WriterObj * self = PyObject_GC_New(WriterObj, &Writer_Type);
+ _csvstate *module_state = get_csv_state(module);
+ WriterObj * self = PyObject_GC_New(WriterObj, module_state->writer_type);
_Py_IDENTIFIER(write);
if (!self)
@@ -1424,6 +1449,8 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
self->rec_len = 0;
self->num_fields = 0;
+ self->error_obj = Py_NewRef(module_state->error_obj);
+
if (!PyArg_UnpackTuple(args, "", 1, 2, &output_file, &dialect)) {
Py_DECREF(self);
return NULL;
@@ -1438,7 +1465,8 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
Py_DECREF(self);
return NULL;
}
- self->dialect = (DialectObj *)_call_dialect(dialect, keyword_args);
+ self->dialect = (DialectObj *)_call_dialect(module_state, dialect,
+ keyword_args);
if (self->dialect == NULL) {
Py_DECREF(self);
return NULL;
@@ -1453,13 +1481,14 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
static PyObject *
csv_list_dialects(PyObject *module, PyObject *args)
{
- return PyDict_Keys(_csvstate_global->dialects);
+ return PyDict_Keys(get_csv_state(module)->dialects);
}
static PyObject *
csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
{
PyObject *name_obj, *dialect_obj = NULL;
+ _csvstate *module_state = get_csv_state(module);
PyObject *dialect;
if (!PyArg_UnpackTuple(args, "", 1, 2, &name_obj, &dialect_obj))
@@ -1471,10 +1500,10 @@ csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
}
if (PyUnicode_READY(name_obj) == -1)
return NULL;
- dialect = _call_dialect(dialect_obj, kwargs);
+ dialect = _call_dialect(module_state, dialect_obj, kwargs);
if (dialect == NULL)
return NULL;
- if (PyDict_SetItem(_csvstate_global->dialects, name_obj, dialect) < 0) {
+ if (PyDict_SetItem(module_state->dialects, name_obj, dialect) < 0) {
Py_DECREF(dialect);
return NULL;
}
@@ -1485,9 +1514,10 @@ csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
static PyObject *
csv_unregister_dialect(PyObject *module, PyObject *name_obj)
{
- if (PyDict_DelItem(_csvstate_global->dialects, name_obj) < 0) {
+ _csvstate *module_state = get_csv_state(module);
+ if (PyDict_DelItem(module_state->dialects, name_obj) < 0) {
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
- PyErr_Format(_csvstate_global->error_obj, "unknown dialect");
+ PyErr_Format(module_state->error_obj, "unknown dialect");
}
return NULL;
}
@@ -1497,14 +1527,15 @@ csv_unregister_dialect(PyObject *module, PyObject *name_obj)
static PyObject *
csv_get_dialect(PyObject *module, PyObject *name_obj)
{
- return get_dialect_from_registry(name_obj);
+ return get_dialect_from_registry(name_obj, get_csv_state(module));
}
static PyObject *
csv_field_size_limit(PyObject *module, PyObject *args)
{
PyObject *new_limit = NULL;
- long old_limit = _csvstate_global->field_limit;
+ _csvstate *module_state = get_csv_state(module);
+ long old_limit = module_state->field_limit;
if (!PyArg_UnpackTuple(args, "field_size_limit", 0, 1, &new_limit))
return NULL;
@@ -1514,15 +1545,25 @@ csv_field_size_limit(PyObject *module, PyObject *args)
"limit must be an integer");
return NULL;
}
- _csvstate_global->field_limit = PyLong_AsLong(new_limit);
- if (_csvstate_global->field_limit == -1 && PyErr_Occurred()) {
- _csvstate_global->field_limit = old_limit;
+ module_state->field_limit = PyLong_AsLong(new_limit);
+ if (module_state->field_limit == -1 && PyErr_Occurred()) {
+ module_state->field_limit = old_limit;
return NULL;
}
}
return PyLong_FromLong(old_limit);
}
+static PyType_Slot error_slots[] = {
+ {0, NULL},
+};
+
+PyType_Spec error_spec = {
+ .name = "_csv.Error",
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = error_slots,
+};
+
/*
* MODULE
*/
@@ -1655,68 +1696,89 @@ static struct PyMethodDef csv_methods[] = {
{ NULL, NULL }
};
-static struct PyModuleDef _csvmodule = {
- PyModuleDef_HEAD_INIT,
- "_csv",
- csv_module_doc,
- sizeof(_csvstate),
- csv_methods,
- NULL,
- _csv_traverse,
- _csv_clear,
- _csv_free
-};
-
-PyMODINIT_FUNC
-PyInit__csv(void)
-{
- PyObject *module;
+static int
+csv_exec(PyObject *module) {
const StyleDesc *style;
+ PyObject *temp;
+ _csvstate *module_state = get_csv_state(module);
- if (PyType_Ready(&Reader_Type) < 0)
- return NULL;
+ temp = PyType_FromModuleAndSpec(module, &Dialect_Type_spec, NULL);
+ module_state->dialect_type = (PyTypeObject *)temp;
+ if (PyModule_AddObjectRef(module, "Dialect", temp) < 0) {
+ return -1;
+ }
- if (PyType_Ready(&Writer_Type) < 0)
- return NULL;
+ temp = PyType_FromModuleAndSpec(module, &Reader_Type_spec, NULL);
+ module_state->reader_type = (PyTypeObject *)temp;
+ if (PyModule_AddObjectRef(module, "Reader", temp) < 0) {
+ return -1;
+ }
- /* Create the module and add the functions */
- module = PyModule_Create(&_csvmodule);
- if (module == NULL)
- return NULL;
+ temp = PyType_FromModuleAndSpec(module, &Writer_Type_spec, NULL);
+ module_state->writer_type = (PyTypeObject *)temp;
+ if (PyModule_AddObjectRef(module, "Writer", temp) < 0) {
+ return -1;
+ }
/* Add version to the module. */
if (PyModule_AddStringConstant(module, "__version__",
- MODULE_VERSION) == -1)
- return NULL;
+ MODULE_VERSION) == -1) {
+ return -1;
+ }
/* Set the field limit */
- get_csv_state(module)->field_limit = 128 * 1024;
- /* Do I still need to add this var to the Module Dict? */
+ module_state->field_limit = 128 * 1024;
/* Add _dialects dictionary */
- get_csv_state(module)->dialects = PyDict_New();
- if (get_csv_state(module)->dialects == NULL)
- return NULL;
- Py_INCREF(get_csv_state(module)->dialects);
- if (PyModule_AddObject(module, "_dialects", get_csv_state(module)->dialects))
- return NULL;
+ module_state->dialects = PyDict_New();
+ if (PyModule_AddObjectRef(module, "_dialects", module_state->dialects) < 0) {
+ return -1;
+ }
/* Add quote styles into dictionary */
for (style = quote_styles; style->name; style++) {
if (PyModule_AddIntConstant(module, style->name,
style->style) == -1)
- return NULL;
+ return -1;
}
- if (PyModule_AddType(module, &Dialect_Type)) {
- return NULL;
+ /* Add the CSV exception object to the module. */
+ PyObject *bases = PyTuple_Pack(1, PyExc_Exception);
+ if (bases == NULL) {
+ return -1;
+ }
+ module_state->error_obj = PyType_FromModuleAndSpec(module, &error_spec,
+ bases);
+ Py_DECREF(bases);
+ if (module_state->error_obj == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(module, (PyTypeObject *)module_state->error_obj) != 0) {
+ return -1;
}
- /* Add the CSV exception object to the module. */
- get_csv_state(module)->error_obj = PyErr_NewException("_csv.Error", NULL, NULL);
- if (get_csv_state(module)->error_obj == NULL)
- return NULL;
- Py_INCREF(get_csv_state(module)->error_obj);
- PyModule_AddObject(module, "Error", get_csv_state(module)->error_obj);
- return module;
+ return 0;
+}
+
+static PyModuleDef_Slot csv_slots[] = {
+ {Py_mod_exec, csv_exec},
+ {0, NULL}
+};
+
+static struct PyModuleDef _csvmodule = {
+ PyModuleDef_HEAD_INIT,
+ "_csv",
+ csv_module_doc,
+ sizeof(_csvstate),
+ csv_methods,
+ csv_slots,
+ _csv_traverse,
+ _csv_clear,
+ _csv_free
+};
+
+PyMODINIT_FUNC
+PyInit__csv(void)
+{
+ return PyModuleDef_Init(&_csvmodule);
}