https://github.com/python/cpython/commit/93142c95c550e781bef5bec93100eca3c90be4f6
commit: 93142c95c550e781bef5bec93100eca3c90be4f6
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-27T18:58:30+03:00
summary:
gh-155496: Use Argument Clinic for more functions of the _csv module (GH-158133)
files:
M Modules/_csv.c
M Modules/clinic/_csv.c.h
diff --git a/Modules/_csv.c b/Modules/_csv.c
index 6af66c3f09a03b..34548b94385693 100644
--- a/Modules/_csv.c
+++ b/Modules/_csv.c
@@ -21,10 +21,9 @@ module instead.
/*[clinic input]
module _csv
+class _csv.Dialect "DialectObj *" "clinic_state()->dialect_type"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=385118b71aa43706]*/
-
-#include "clinic/_csv.c.h"
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=042fcc14c7d541cc]*/
#define NOT_SET ((Py_UCS4)-1)
#define EOL ((Py_UCS4)-2)
@@ -125,6 +124,8 @@ typedef struct {
bool nonascii_special;
} DialectObj;
+#include "clinic/_csv.c.h"
+
typedef struct {
PyObject_HEAD
@@ -631,43 +632,62 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject
*kwargs)
/* 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");
+/*[clinic input]
+_csv.Dialect.__reduce__
+
+Raise an exception to avoid pickling.
+[clinic start generated code]*/
static PyObject *
-Dialect_reduce(PyObject *self, PyObject *args) {
+_csv_Dialect___reduce___impl(DialectObj *self)
+/*[clinic end generated code: output=f728b34af509ed69 input=e46419b46279e480]*/
+{
PyErr_Format(PyExc_TypeError,
"cannot pickle '%.100s' instances", _PyType_Name(Py_TYPE(self)));
return NULL;
}
-PyDoc_STRVAR(dialect_replace_doc,
-"__replace__($self, /, **changes)\n"
-"--\n"
-"\n"
-"Return a copy of the dialect with the specified options replaced.");
+/*[clinic input]
+_csv.Dialect.__reduce_ex__
+
+ protocol: object
+ /
+
+Raise an exception to avoid pickling.
+[clinic start generated code]*/
static PyObject *
-Dialect_replace(PyObject *self, PyObject *args, PyObject *kwargs)
+_csv_Dialect___reduce_ex___impl(DialectObj *self, PyObject *protocol)
+/*[clinic end generated code: output=d45dec397da6575a input=c6ebd579e8959040]*/
+{
+ return _csv_Dialect___reduce___impl(self);
+}
+
+/*[clinic input]
+_csv.Dialect.__replace__
+
+ **changes: dict
+
+Return a copy of the dialect with the specified options replaced.
+[clinic start generated code]*/
+
+static PyObject *
+_csv_Dialect___replace___impl(DialectObj *self, PyObject *changes)
+/*[clinic end generated code: output=8f692c2c63a61b50 input=154beb565fbabd4c]*/
{
- if (PyTuple_GET_SIZE(args) != 0) {
- PyErr_SetString(PyExc_TypeError,
- "__replace__() takes no positional arguments");
- return NULL;
- }
PyObject *newargs = PyTuple_Pack(1, self);
if (newargs == NULL) {
return NULL;
}
- PyObject *result = dialect_new(Py_TYPE(self), newargs, kwargs);
+ PyObject *result = dialect_new(Py_TYPE(self), newargs, changes);
Py_DECREF(newargs);
return result;
}
static struct PyMethodDef dialect_methods[] = {
- {"__reduce__", Dialect_reduce, METH_VARARGS, dialect_reduce_doc},
- {"__reduce_ex__", Dialect_reduce, METH_VARARGS, dialect_reduce_doc},
- {"__replace__", _PyCFunction_CAST(Dialect_replace),
- METH_VARARGS | METH_KEYWORDS, dialect_replace_doc},
+ _CSV_DIALECT___REDUCE___METHODDEF
+ _CSV_DIALECT___REDUCE_EX___METHODDEF
+ _CSV_DIALECT___REPLACE___METHODDEF
{NULL, NULL}
};
@@ -1162,10 +1182,31 @@ PyType_Spec Reader_Type_spec = {
};
+/*[clinic input]
+_csv.reader
+
+ iterable: object
+ dialect: object(c_default='NULL') = 'excel'
+ /
+ **fmtparams: dict
+
+Return a reader object that will process lines from the given iterable.
+
+The "iterable" argument can be any object that returns a line
+of input for each iteration, such as a file object or a list. The
+optional "dialect" argument defines a CSV dialect. The function
+also accepts optional keyword arguments which override settings
+provided by the dialect.
+
+The returned object is an iterator. Each iteration returns a row
+of the CSV file (which can span multiple input lines).
+[clinic start generated code]*/
+
static PyObject *
-csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args)
+_csv_reader_impl(PyObject *module, PyObject *iterable, PyObject *dialect,
+ PyObject *fmtparams)
+/*[clinic end generated code: output=c7033323f4e82fae input=330c6d58878e33b7]*/
{
- PyObject * iterator, * dialect = NULL;
_csvstate *module_state = get_csv_state(module);
ReaderObj * self = PyObject_GC_New(
ReaderObj,
@@ -1186,17 +1227,13 @@ csv_reader(PyObject *module, PyObject *args, PyObject
*keyword_args)
return NULL;
}
- if (!PyArg_UnpackTuple(args, "reader", 1, 2, &iterator, &dialect)) {
- Py_DECREF(self);
- return NULL;
- }
- self->input_iter = PyObject_GetIter(iterator);
+ self->input_iter = PyObject_GetIter(iterable);
if (self->input_iter == NULL) {
Py_DECREF(self);
return NULL;
}
self->dialect = (DialectObj *)_call_dialect(module_state, dialect,
- keyword_args);
+ fmtparams);
if (self->dialect == NULL) {
Py_DECREF(self);
return NULL;
@@ -1624,10 +1661,27 @@ PyType_Spec Writer_Type_spec = {
};
+/*[clinic input]
+_csv.writer
+
+ fileobj as output_file: object
+ dialect: object(c_default='NULL') = 'excel'
+ /
+ **fmtparams: dict
+
+Return a writer object writing user data to the given file object.
+
+The "fileobj" argument can be any object that supports the file API.
+The optional "dialect" argument defines a CSV dialect. The function
+also accepts optional keyword arguments which override settings
+provided by the dialect.
+[clinic start generated code]*/
+
static PyObject *
-csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
+_csv_writer_impl(PyObject *module, PyObject *output_file, PyObject *dialect,
+ PyObject *fmtparams)
+/*[clinic end generated code: output=3f57919e03cca475 input=18d396d2aa8138f0]*/
{
- PyObject * output_file, * dialect = NULL;
_csvstate *module_state = get_csv_state(module);
WriterObj * self = PyObject_GC_New(WriterObj, module_state->writer_type);
@@ -1644,10 +1698,6 @@ csv_writer(PyObject *module, PyObject *args, PyObject
*keyword_args)
self->error_obj = Py_NewRef(module_state->error_obj);
- if (!PyArg_UnpackTuple(args, "writer", 1, 2, &output_file, &dialect)) {
- Py_DECREF(self);
- return NULL;
- }
if (PyObject_GetOptionalAttr(output_file,
module_state->str_write,
&self->write) < 0) {
@@ -1661,7 +1711,7 @@ csv_writer(PyObject *module, PyObject *args, PyObject
*keyword_args)
return NULL;
}
self->dialect = (DialectObj *)_call_dialect(module_state, dialect,
- keyword_args);
+ fmtparams);
if (self->dialect == NULL) {
Py_DECREF(self);
return NULL;
@@ -1687,21 +1737,36 @@ _csv_list_dialects_impl(PyObject *module)
return PyDict_Keys(get_csv_state(module)->dialects);
}
+/*[clinic input]
+_csv.register_dialect
+
+ name as name_obj: object
+ dialect as dialect_obj: object(c_default='NULL') = 'excel'
+ /
+ **fmtparams: dict
+
+Create a mapping from a string name to a CVS dialect.
+
+The optional "dialect" argument specifies the base dialect instance
+or the name of the registered dialect. The function also accepts
+optional keyword arguments which override settings provided by the
+dialect.
+[clinic start generated code]*/
+
static PyObject *
-csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
+_csv_register_dialect_impl(PyObject *module, PyObject *name_obj,
+ PyObject *dialect_obj, PyObject *fmtparams)
+/*[clinic end generated code: output=b00b54de5b950472 input=64e9180e18d88a97]*/
{
- PyObject *name_obj, *dialect_obj = NULL;
_csvstate *module_state = get_csv_state(module);
PyObject *dialect;
- if (!PyArg_UnpackTuple(args, "register_dialect", 1, 2, &name_obj,
&dialect_obj))
- return NULL;
if (!PyUnicode_Check(name_obj)) {
PyErr_SetString(PyExc_TypeError,
"dialect name must be a string");
return NULL;
}
- dialect = _call_dialect(module_state, dialect_obj, kwargs);
+ dialect = _call_dialect(module_state, dialect_obj, fmtparams);
if (dialect == NULL)
return NULL;
if (PyDict_SetItem(module_state->dialects, name_obj, dialect) < 0) {
@@ -1800,47 +1865,10 @@ PyType_Spec error_spec = {
PyDoc_STRVAR(csv_module_doc, "CSV parsing and writing.\n");
-PyDoc_STRVAR(csv_reader_doc,
-"reader($module, iterable, /, dialect='excel', **fmtparams)\n"
-"--\n\n"
-"Return a reader object that will process lines from the given iterable.\n"
-"\n"
-"The \"iterable\" argument can be any object that returns a line\n"
-"of input for each iteration, such as a file object or a list. The\n"
-"optional \"dialect\" argument defines a CSV dialect. The function\n"
-"also accepts optional keyword arguments which override settings\n"
-"provided by the dialect.\n"
-"\n"
-"The returned object is an iterator. Each iteration returns a row\n"
-"of the CSV file (which can span multiple input lines).\n");
-
-PyDoc_STRVAR(csv_writer_doc,
-"writer($module, fileobj, /, dialect='excel', **fmtparams)\n"
-"--\n\n"
-"Return a writer object that will write user data on the given file object.\n"
-"\n"
-"The \"fileobj\" argument can be any object that supports the file API.\n"
-"The optional \"dialect\" argument defines a CSV dialect. The function\n"
-"also accepts optional keyword arguments which override settings\n"
-"provided by the dialect.\n");
-
-PyDoc_STRVAR(csv_register_dialect_doc,
-"register_dialect($module, name, /, dialect='excel', **fmtparams)\n"
-"--\n\n"
-"Create a mapping from a string name to a CVS dialect.\n"
-"\n"
-"The optional \"dialect\" argument specifies the base dialect instance\n"
-"or the name of the registered dialect. The function also accepts\n"
-"optional keyword arguments which override settings provided by the\n"
-"dialect.\n");
-
static struct PyMethodDef csv_methods[] = {
- { "reader", _PyCFunction_CAST(csv_reader),
- METH_VARARGS | METH_KEYWORDS, csv_reader_doc},
- { "writer", _PyCFunction_CAST(csv_writer),
- METH_VARARGS | METH_KEYWORDS, csv_writer_doc},
- { "register_dialect", _PyCFunction_CAST(csv_register_dialect),
- METH_VARARGS | METH_KEYWORDS, csv_register_dialect_doc},
+ _CSV_READER_METHODDEF
+ _CSV_WRITER_METHODDEF
+ _CSV_REGISTER_DIALECT_METHODDEF
_CSV_LIST_DIALECTS_METHODDEF
_CSV_UNREGISTER_DIALECT_METHODDEF
_CSV_GET_DIALECT_METHODDEF
diff --git a/Modules/clinic/_csv.c.h b/Modules/clinic/_csv.c.h
index b8dd8ac35fac59..b25f30893224cd 100644
--- a/Modules/clinic/_csv.c.h
+++ b/Modules/clinic/_csv.c.h
@@ -6,7 +6,196 @@ preserve
# include "pycore_gc.h" // PyGC_Head
# include "pycore_runtime.h" // _Py_ID()
#endif
-#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
+#include "pycore_modsupport.h" // _PyArg_NoPositional()
+
+PyDoc_STRVAR(_csv_Dialect___reduce____doc__,
+"__reduce__($self, /)\n"
+"--\n"
+"\n"
+"Raise an exception to avoid pickling.");
+
+#define _CSV_DIALECT___REDUCE___METHODDEF \
+ {"__reduce__", (PyCFunction)_csv_Dialect___reduce__, METH_NOARGS,
_csv_Dialect___reduce____doc__},
+
+static PyObject *
+_csv_Dialect___reduce___impl(DialectObj *self);
+
+static PyObject *
+_csv_Dialect___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return _csv_Dialect___reduce___impl((DialectObj *)self);
+}
+
+PyDoc_STRVAR(_csv_Dialect___reduce_ex____doc__,
+"__reduce_ex__($self, protocol, /)\n"
+"--\n"
+"\n"
+"Raise an exception to avoid pickling.");
+
+#define _CSV_DIALECT___REDUCE_EX___METHODDEF \
+ {"__reduce_ex__", (PyCFunction)_csv_Dialect___reduce_ex__, METH_O,
_csv_Dialect___reduce_ex____doc__},
+
+static PyObject *
+_csv_Dialect___reduce_ex___impl(DialectObj *self, PyObject *protocol);
+
+static PyObject *
+_csv_Dialect___reduce_ex__(PyObject *self, PyObject *protocol)
+{
+ PyObject *return_value = NULL;
+
+ return_value = _csv_Dialect___reduce_ex___impl((DialectObj *)self,
protocol);
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_csv_Dialect___replace____doc__,
+"__replace__($self, /, **changes)\n"
+"--\n"
+"\n"
+"Return a copy of the dialect with the specified options replaced.");
+
+#define _CSV_DIALECT___REPLACE___METHODDEF \
+ {"__replace__", _PyCFunction_CAST(_csv_Dialect___replace__),
METH_VARARGS|METH_KEYWORDS, _csv_Dialect___replace____doc__},
+
+static PyObject *
+_csv_Dialect___replace___impl(DialectObj *self, PyObject *changes);
+
+static PyObject *
+_csv_Dialect___replace__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *changes = NULL;
+
+ if (!_PyArg_NoPositional("__replace__", args)) {
+ goto exit;
+ }
+ if (kwargs == NULL) {
+ changes = PyDict_New();
+ if (changes == NULL) {
+ goto exit;
+ }
+ }
+ else {
+ changes = Py_NewRef(kwargs);
+ }
+ return_value = _csv_Dialect___replace___impl((DialectObj *)self, changes);
+
+exit:
+ /* Cleanup for changes */
+ Py_XDECREF(changes);
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_csv_reader__doc__,
+"reader($module, iterable, dialect=\'excel\', /, **fmtparams)\n"
+"--\n"
+"\n"
+"Return a reader object that will process lines from the given iterable.\n"
+"\n"
+"The \"iterable\" argument can be any object that returns a line\n"
+"of input for each iteration, such as a file object or a list. The\n"
+"optional \"dialect\" argument defines a CSV dialect. The function\n"
+"also accepts optional keyword arguments which override settings\n"
+"provided by the dialect.\n"
+"\n"
+"The returned object is an iterator. Each iteration returns a row\n"
+"of the CSV file (which can span multiple input lines).");
+
+#define _CSV_READER_METHODDEF \
+ {"reader", _PyCFunction_CAST(_csv_reader), METH_VARARGS|METH_KEYWORDS,
_csv_reader__doc__},
+
+static PyObject *
+_csv_reader_impl(PyObject *module, PyObject *iterable, PyObject *dialect,
+ PyObject *fmtparams);
+
+static PyObject *
+_csv_reader(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *iterable;
+ PyObject *dialect = NULL;
+ PyObject *fmtparams = NULL;
+
+ if (!_PyArg_CheckPositional("reader", PyTuple_GET_SIZE(args), 1, 2)) {
+ goto exit;
+ }
+ iterable = PyTuple_GET_ITEM(args, 0);
+ if (PyTuple_GET_SIZE(args) < 2) {
+ goto skip_optional;
+ }
+ dialect = PyTuple_GET_ITEM(args, 1);
+skip_optional:
+ if (kwargs == NULL) {
+ fmtparams = PyDict_New();
+ if (fmtparams == NULL) {
+ goto exit;
+ }
+ }
+ else {
+ fmtparams = Py_NewRef(kwargs);
+ }
+ return_value = _csv_reader_impl(module, iterable, dialect, fmtparams);
+
+exit:
+ /* Cleanup for fmtparams */
+ Py_XDECREF(fmtparams);
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_csv_writer__doc__,
+"writer($module, fileobj, dialect=\'excel\', /, **fmtparams)\n"
+"--\n"
+"\n"
+"Return a writer object writing user data to the given file object.\n"
+"\n"
+"The \"fileobj\" argument can be any object that supports the file API.\n"
+"The optional \"dialect\" argument defines a CSV dialect. The function\n"
+"also accepts optional keyword arguments which override settings\n"
+"provided by the dialect.");
+
+#define _CSV_WRITER_METHODDEF \
+ {"writer", _PyCFunction_CAST(_csv_writer), METH_VARARGS|METH_KEYWORDS,
_csv_writer__doc__},
+
+static PyObject *
+_csv_writer_impl(PyObject *module, PyObject *output_file, PyObject *dialect,
+ PyObject *fmtparams);
+
+static PyObject *
+_csv_writer(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *output_file;
+ PyObject *dialect = NULL;
+ PyObject *fmtparams = NULL;
+
+ if (!_PyArg_CheckPositional("writer", PyTuple_GET_SIZE(args), 1, 2)) {
+ goto exit;
+ }
+ output_file = PyTuple_GET_ITEM(args, 0);
+ if (PyTuple_GET_SIZE(args) < 2) {
+ goto skip_optional;
+ }
+ dialect = PyTuple_GET_ITEM(args, 1);
+skip_optional:
+ if (kwargs == NULL) {
+ fmtparams = PyDict_New();
+ if (fmtparams == NULL) {
+ goto exit;
+ }
+ }
+ else {
+ fmtparams = Py_NewRef(kwargs);
+ }
+ return_value = _csv_writer_impl(module, output_file, dialect, fmtparams);
+
+exit:
+ /* Cleanup for fmtparams */
+ Py_XDECREF(fmtparams);
+
+ return return_value;
+}
PyDoc_STRVAR(_csv_list_dialects__doc__,
"list_dialects($module, /)\n"
@@ -26,6 +215,59 @@ _csv_list_dialects(PyObject *module, PyObject
*Py_UNUSED(ignored))
return _csv_list_dialects_impl(module);
}
+PyDoc_STRVAR(_csv_register_dialect__doc__,
+"register_dialect($module, name, dialect=\'excel\', /, **fmtparams)\n"
+"--\n"
+"\n"
+"Create a mapping from a string name to a CVS dialect.\n"
+"\n"
+"The optional \"dialect\" argument specifies the base dialect instance\n"
+"or the name of the registered dialect. The function also accepts\n"
+"optional keyword arguments which override settings provided by the\n"
+"dialect.");
+
+#define _CSV_REGISTER_DIALECT_METHODDEF \
+ {"register_dialect", _PyCFunction_CAST(_csv_register_dialect),
METH_VARARGS|METH_KEYWORDS, _csv_register_dialect__doc__},
+
+static PyObject *
+_csv_register_dialect_impl(PyObject *module, PyObject *name_obj,
+ PyObject *dialect_obj, PyObject *fmtparams);
+
+static PyObject *
+_csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *name_obj;
+ PyObject *dialect_obj = NULL;
+ PyObject *fmtparams = NULL;
+
+ if (!_PyArg_CheckPositional("register_dialect", PyTuple_GET_SIZE(args), 1,
2)) {
+ goto exit;
+ }
+ name_obj = PyTuple_GET_ITEM(args, 0);
+ if (PyTuple_GET_SIZE(args) < 2) {
+ goto skip_optional;
+ }
+ dialect_obj = PyTuple_GET_ITEM(args, 1);
+skip_optional:
+ if (kwargs == NULL) {
+ fmtparams = PyDict_New();
+ if (fmtparams == NULL) {
+ goto exit;
+ }
+ }
+ else {
+ fmtparams = Py_NewRef(kwargs);
+ }
+ return_value = _csv_register_dialect_impl(module, name_obj, dialect_obj,
fmtparams);
+
+exit:
+ /* Cleanup for fmtparams */
+ Py_XDECREF(fmtparams);
+
+ return return_value;
+}
+
PyDoc_STRVAR(_csv_unregister_dialect__doc__,
"unregister_dialect($module, /, name)\n"
"--\n"
@@ -207,4 +449,4 @@ _csv_field_size_limit(PyObject *module, PyObject *const
*args, Py_ssize_t nargs,
exit:
return return_value;
}
-/*[clinic end generated code: output=ed77cb69fad9f3b4 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=1a90b8a8ed82497d input=a9049054013a1b77]*/
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]