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]

Reply via email to