Skip to content

Commit b7ccb20

Browse files
committed
Issue #20294: Argument Clinic now supports argument parsing for __new__ and
__init__ functions.
1 parent b470575 commit b7ccb20

File tree

6 files changed

+365
-239
lines changed

6 files changed

+365
-239
lines changed

Doc/howto/clinic.rst

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,8 +784,8 @@ Argument Clinic converters. On the left is the legacy converter,
784784
on the right is the text you'd replace it with.
785785

786786
========= =================================================================================
787-
``'B'`` ``byte(bitwise=True)``
788-
``'b'`` ``byte``
787+
``'B'`` ``unsigned_char(bitwise=True)``
788+
``'b'`` ``unsigned_char``
789789
``'c'`` ``char``
790790
``'C'`` ``int(types='str')``
791791
``'d'`` ``double``
@@ -1275,6 +1275,25 @@ any arguments.
12751275
You can still use a self converter, a return converter, and specify
12761276
a ``type`` argument to the object converter for ``METH_O``.
12771277

1278+
tp_new and tp_init functions
1279+
----------------------------------------------
1280+
1281+
You can convert ``tp_new`` and ``tp_init``
1282+
functions. Just name them ``__new__`` or
1283+
``__init__`` as appropriate. Notes:
1284+
1285+
* The function name generated for ``__new__`` doesn't end in ``__new__``
1286+
like it would by default. It's just the name of the class, converted
1287+
into a valid C identifier.
1288+
1289+
* No ``PyMethodDef`` ``#define`` is generated for these functions.
1290+
1291+
* ``__init__`` functions return ``int``, not ``PyObject *``.
1292+
1293+
* Argument Clinic supports any signature for these functions, even though
1294+
the parsing function is required to always take ``args`` and ``kwargs``
1295+
objects.
1296+
12781297
The #ifdef trick
12791298
----------------------------------------------
12801299

Include/modsupport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);
3636
#endif
3737
#ifndef Py_LIMITED_API
3838
PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw);
39+
PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args);
3940

4041
PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list);
4142
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ Tests
101101
Tools/Demos
102102
-----------
103103

104+
- Issue #20294: Argument Clinic now supports argument parsing for __new__ and
105+
__init__ functions.
106+
104107
- Issue #20299: Argument Clinic custom converters may now change the default
105108
value of c_default and py_default with a class member.
106109

Modules/_pickle.c

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4064,13 +4064,13 @@ PyDoc_STRVAR(_pickle_Pickler___init____doc__,
40644064
"to map the new Python 3 names to the old module names used in Python\n"
40654065
"2, so that the pickle data stream is readable with Python 2.");
40664066

4067-
static PyObject *
4067+
static int
40684068
_pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports);
40694069

4070-
static PyObject *
4070+
static int
40714071
_pickle_Pickler___init__(PyObject *self, PyObject *args, PyObject *kwargs)
40724072
{
4073-
PyObject *return_value = NULL;
4073+
int return_value = -1;
40744074
static char *_keywords[] = {"file", "protocol", "fix_imports", NULL};
40754075
PyObject *file;
40764076
PyObject *protocol = NULL;
@@ -4086,9 +4086,9 @@ _pickle_Pickler___init__(PyObject *self, PyObject *args, PyObject *kwargs)
40864086
return return_value;
40874087
}
40884088

4089-
static PyObject *
4089+
static int
40904090
_pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports)
4091-
/*[clinic end generated code: checksum=defa3d9e9f8b51fb257d4fdfca99db503db0e6df]*/
4091+
/*[clinic end generated code: checksum=10c8ea05194d08108471163d8202cf5e12975544]*/
40924092
{
40934093
_Py_IDENTIFIER(persistent_id);
40944094
_Py_IDENTIFIER(dispatch_table);
@@ -4098,24 +4098,24 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *pro
40984098
(void)Pickler_clear(self);
40994099

41004100
if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0)
4101-
return NULL;
4101+
return -1;
41024102

41034103
if (_Pickler_SetOutputStream(self, file) < 0)
4104-
return NULL;
4104+
return -1;
41054105

41064106
/* memo and output_buffer may have already been created in _Pickler_New */
41074107
if (self->memo == NULL) {
41084108
self->memo = PyMemoTable_New();
41094109
if (self->memo == NULL)
4110-
return NULL;
4110+
return -1;
41114111
}
41124112
self->output_len = 0;
41134113
if (self->output_buffer == NULL) {
41144114
self->max_output_len = WRITE_BUF_SIZE;
41154115
self->output_buffer = PyBytes_FromStringAndSize(NULL,
41164116
self->max_output_len);
41174117
if (self->output_buffer == NULL)
4118-
return NULL;
4118+
return -1;
41194119
}
41204120

41214121
self->fast = 0;
@@ -4126,31 +4126,20 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *pro
41264126
self->pers_func = _PyObject_GetAttrId((PyObject *)self,
41274127
&PyId_persistent_id);
41284128
if (self->pers_func == NULL)
4129-
return NULL;
4129+
return -1;
41304130
}
41314131
self->dispatch_table = NULL;
41324132
if (_PyObject_HasAttrId((PyObject *)self, &PyId_dispatch_table)) {
41334133
self->dispatch_table = _PyObject_GetAttrId((PyObject *)self,
41344134
&PyId_dispatch_table);
41354135
if (self->dispatch_table == NULL)
4136-
return NULL;
4136+
return -1;
41374137
}
41384138

4139-
Py_RETURN_NONE;
4140-
}
4141-
4142-
/* Wrap the Clinic generated signature to slot it in tp_init. */
4143-
static int
4144-
Pickler_init(PyObject *self, PyObject *args, PyObject *kwargs)
4145-
{
4146-
PyObject *result = _pickle_Pickler___init__(self, args, kwargs);
4147-
if (result == NULL) {
4148-
return -1;
4149-
}
4150-
Py_DECREF(result);
41514139
return 0;
41524140
}
41534141

4142+
41544143
/* Define a proxy object for the Pickler's internal memo object. This is to
41554144
* avoid breaking code like:
41564145
* pickler.memo.clear()
@@ -4543,7 +4532,7 @@ static PyTypeObject Pickler_Type = {
45434532
0, /*tp_descr_get*/
45444533
0, /*tp_descr_set*/
45454534
0, /*tp_dictoffset*/
4546-
Pickler_init, /*tp_init*/
4535+
_pickle_Pickler___init__, /*tp_init*/
45474536
PyType_GenericAlloc, /*tp_alloc*/
45484537
PyType_GenericNew, /*tp_new*/
45494538
PyObject_GC_Del, /*tp_free*/
@@ -6614,13 +6603,13 @@ PyDoc_STRVAR(_pickle_Unpickler___init____doc__,
66146603
"respectively. The *encoding* can be \'bytes\' to read these 8-bit\n"
66156604
"string instances as bytes objects.");
66166605

6617-
static PyObject *
6606+
static int
66186607
_pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors);
66196608

6620-
static PyObject *
6609+
static int
66216610
_pickle_Unpickler___init__(PyObject *self, PyObject *args, PyObject *kwargs)
66226611
{
6623-
PyObject *return_value = NULL;
6612+
int return_value = -1;
66246613
static char *_keywords[] = {"file", "fix_imports", "encoding", "errors", NULL};
66256614
PyObject *file;
66266615
int fix_imports = 1;
@@ -6637,9 +6626,9 @@ _pickle_Unpickler___init__(PyObject *self, PyObject *args, PyObject *kwargs)
66376626
return return_value;
66386627
}
66396628

6640-
static PyObject *
6629+
static int
66416630
_pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors)
6642-
/*[clinic end generated code: checksum=26c1d4a06841a8e51d29a0c244ba7f4607ff358a]*/
6631+
/*[clinic end generated code: checksum=6936e9188104e45b1b15e1c11fe77b3965409471]*/
66436632
{
66446633
_Py_IDENTIFIER(persistent_load);
66456634

@@ -6648,51 +6637,40 @@ _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_i
66486637
(void)Unpickler_clear(self);
66496638

66506639
if (_Unpickler_SetInputStream(self, file) < 0)
6651-
return NULL;
6640+
return -1;
66526641

66536642
if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0)
6654-
return NULL;
6643+
return -1;
66556644

66566645
self->fix_imports = fix_imports;
66576646
if (self->fix_imports == -1)
6658-
return NULL;
6647+
return -1;
66596648

66606649
if (_PyObject_HasAttrId((PyObject *)self, &PyId_persistent_load)) {
66616650
self->pers_func = _PyObject_GetAttrId((PyObject *)self,
66626651
&PyId_persistent_load);
66636652
if (self->pers_func == NULL)
6664-
return NULL;
6653+
return 1;
66656654
}
66666655
else {
66676656
self->pers_func = NULL;
66686657
}
66696658

66706659
self->stack = (Pdata *)Pdata_New();
66716660
if (self->stack == NULL)
6672-
return NULL;
6661+
return 1;
66736662

66746663
self->memo_size = 32;
66756664
self->memo = _Unpickler_NewMemo(self->memo_size);
66766665
if (self->memo == NULL)
6677-
return NULL;
6666+
return -1;
66786667

66796668
self->proto = 0;
66806669

6681-
Py_RETURN_NONE;
6682-
}
6683-
6684-
/* Wrap the Clinic generated signature to slot it in tp_init. */
6685-
static int
6686-
Unpickler_init(PyObject *self, PyObject *args, PyObject *kwargs)
6687-
{
6688-
PyObject *result = _pickle_Unpickler___init__(self, args, kwargs);
6689-
if (result == NULL) {
6690-
return -1;
6691-
}
6692-
Py_DECREF(result);
66936670
return 0;
66946671
}
66956672

6673+
66966674
/* Define a proxy object for the Unpickler's internal memo object. This is to
66976675
* avoid breaking code like:
66986676
* unpickler.memo.clear()
@@ -7096,7 +7074,7 @@ static PyTypeObject Unpickler_Type = {
70967074
0, /*tp_descr_get*/
70977075
0, /*tp_descr_set*/
70987076
0, /*tp_dictoffset*/
7099-
Unpickler_init, /*tp_init*/
7077+
_pickle_Unpickler___init__, /*tp_init*/
71007078
PyType_GenericAlloc, /*tp_alloc*/
71017079
PyType_GenericNew, /*tp_new*/
71027080
PyObject_GC_Del, /*tp_free*/

Python/getargs.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1805,7 +1805,7 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m
18051805

18061806
/* For type constructors that don't take keyword args
18071807
*
1808-
* Sets a TypeError and returns 0 if the kwds dict is
1808+
* Sets a TypeError and returns 0 if the args/kwargs is
18091809
* not empty, returns 1 otherwise
18101810
*/
18111811
int
@@ -1824,6 +1824,25 @@ _PyArg_NoKeywords(const char *funcname, PyObject *kw)
18241824
funcname);
18251825
return 0;
18261826
}
1827+
1828+
1829+
int
1830+
_PyArg_NoPositional(const char *funcname, PyObject *args)
1831+
{
1832+
if (args == NULL)
1833+
return 1;
1834+
if (!PyTuple_CheckExact(args)) {
1835+
PyErr_BadInternalCall();
1836+
return 0;
1837+
}
1838+
if (PyTuple_GET_SIZE(args) == 0)
1839+
return 1;
1840+
1841+
PyErr_Format(PyExc_TypeError, "%s does not take positional arguments",
1842+
funcname);
1843+
return 0;
1844+
}
1845+
18271846
#ifdef __cplusplus
18281847
};
18291848
#endif

0 commit comments

Comments
 (0)