diff --git a/Include/longobject.h b/Include/longobject.h --- a/Include/longobject.h +++ b/Include/longobject.h @@ -198,6 +198,34 @@ PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int); PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int); +#ifndef Py_LIMITED_API +/* Converters used by Argument Clinic */ +typedef struct nullable_int_t +{ + int error; + int is_null; + int i; +} nullable_int_t; + +#define NULLABLE_INT_T_INITIALIZE(i, is_null) \ + { 0, is_null, i } + +PyAPI_FUNC(int) _PyLong_NullableIntConverter(PyObject *arg, void *p); + +typedef struct nullable_Py_ssize_t +{ + int error; + int is_null; + Py_ssize_t i; +} nullable_Py_ssize_t; + +#define NULLABLE_PY_SSIZE_T_INITIALIZE(i, is_null) \ + { 0, is_null, i } + +PyAPI_FUNC(int) _PyLong_NullablePy_ssize_tConverter(PyObject *arg, void *p); +#endif /* Py_LIMITED_API */ + + #ifdef __cplusplus } #endif diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -4085,27 +4085,61 @@ static PyTypeObject repeat_type; +/*[clinic input] +class repeat + +@classmethod +repeat.__new__ as repeat_new + object: object + times: Py_ssize_t(nullable=True) = None + +Returns an iterator which returns the object the specified number of times. + +If times is None, returns the object endlessly. +[clinic start generated code]*/ + +PyDoc_STRVAR(repeat_new__doc__, +"repeat(object, times=None)\n" +"Returns an iterator which returns the object the specified number of times.\n" +"\n" +"If times is None, returns the object endlessly."); + static PyObject * -repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +repeat_new_impl(PyTypeObject *type, PyObject *object, nullable_Py_ssize_t *times); + +static PyObject * +repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - repeatobject *ro; - PyObject *element; - Py_ssize_t cnt = -1; - static char *kwargs[] = {"object", "times", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:repeat", kwargs, - &element, &cnt)) - return NULL; - - if (PyTuple_Size(args) == 2 && cnt < 0) - cnt = 0; - - ro = (repeatobject *)type->tp_alloc(type, 0); + PyObject *return_value = NULL; + static char *_keywords[] = {"object", "times", NULL}; + PyObject *object; + nullable_Py_ssize_t times = NULLABLE_PY_SSIZE_T_INITIALIZE(0, 1); + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "O|O&:__new__", _keywords, + &object, _PyLong_NullablePy_ssize_tConverter, ×)) + goto exit; + return_value = repeat_new_impl(type, object, ×); + +exit: + return return_value; +} + +static PyObject * +repeat_new_impl(PyTypeObject *type, PyObject *object, nullable_Py_ssize_t *times) +/*[clinic end generated code: checksum=fb3768b86075969f41124e75e7cfceebc6edd263]*/ +{ + repeatobject *ro = (repeatobject *)type->tp_alloc(type, 0); if (ro == NULL) return NULL; - Py_INCREF(element); - ro->element = element; - ro->cnt = cnt; + Py_INCREF(object); + ro->element = object; + if (times->is_null) + ro->cnt = -1; + else if (times->i < 0) + ro->cnt = 0; + else + ro->cnt = times->i; return (PyObject *)ro; } @@ -4174,11 +4208,6 @@ {NULL, NULL} /* sentinel */ }; -PyDoc_STRVAR(repeat_doc, -"repeat(object [,times]) -> create an iterator which returns the object\n\ -for the specified number of times. If not specified, returns the object\n\ -endlessly."); - static PyTypeObject repeat_type = { PyVarObject_HEAD_INIT(NULL, 0) "itertools.repeat", /* tp_name */ @@ -4202,7 +4231,7 @@ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ - repeat_doc, /* tp_doc */ + repeat_new__doc__, /* tp_doc */ (traverseproc)repeat_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/Objects/longobject.c b/Objects/longobject.c --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1418,6 +1418,90 @@ #endif /* HAVE_LONG_LONG */ + +static int +float_argument_error(PyObject *arg) +{ + if (PyFloat_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + return 1; + } + else + return 0; +} + +int +_PyLong_NullableIntConverter(PyObject *arg, void *p) +{ + nullable_int_t *ni = (nullable_int_t *)p; + int i; + + if (arg == Py_None) { + ni->is_null = 1; + return 0; + } + + if (float_argument_error(arg)) + goto fail; + + i = PyLong_AsLong(arg); + + if (i == -1 && PyErr_Occurred()) + goto fail; + if (i > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "signed integer is greater than maximum"); + goto fail; + } + if (i < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "signed integer is less than minimum"); + goto fail; + } + + /* success */ + Py_DECREF(index); + ni->i = i; + ni->is_null = 0; + return 1; + +fail: + Py_XDECREF(index); + ni->error = 1; + return 0; +} + +int +_PyLong_NullablePy_ssize_tConverter(PyObject *arg, void *p) +{ + nullable_Py_ssize_t *ni = (nullable_Py_ssize_t *)p; + PyObject *index; + + if (arg == Py_None) { + ni->is_null = 1; + return 0; + } + + if (float_argument_error(arg)) + goto fail; + + index = PyNumber_Index(arg); + if (index == NULL) + goto fail; + + /* success */ + ni->i = PyLong_AsSsize_t(index); + ni->is_null = 0; + Py_DECREF(index); + return 1; + +fail: + Py_XDECREF(index); + ni->error = 1; + return 0; +} + #define CHECK_BINOP(v,w) \ do { \ if (!PyLong_Check(v) || !PyLong_Check(w)) \ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -2184,16 +2184,34 @@ @add_legacy_c_converter('C', types='str') class int_converter(CConverter): type = 'int' - default_type = int + default_type = (int, NoneType) format_unit = 'i' c_ignored_default = "0" - def converter_init(self, *, types='int'): + def converter_init(self, *, types='int', nullable=False): + if nullable: + if types != 'int': + fail("int_converter: unsupported 'types' argument when nullable=True") + self.type = 'nullable_int_t' + self.format_unit = 'O&' + self.converter = '_PyLong_NullableIntConverter' + self.impl_by_reference = self.parse_by_reference = True + if self.default is None: + self.c_default = "NULLABLE_INT_T_INITIALIZE(0, 1)" + elif self.default is not unspecified: + self.c_default = "NULLABLE_INT_T_INITIALIZE({}, 0)".format(self.default) + else: + self.c_default = "NULLABLE_PY_SSIZE_T_INITIALIZE(0, 0)" + return + + if not isinstance(self.default, int): + fail("Illegal default value for int_converter") if types == 'str': self.format_unit = 'C' elif types != 'int': fail("int_converter: illegal 'types' argument") + class unsigned_int_converter(CConverter): type = 'unsigned int' default_type = int @@ -2238,10 +2256,27 @@ class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t' - default_type = int format_unit = 'n' + default_type = (int, NoneType) c_ignored_default = "0" + def converter_init(self, *, nullable=False): + if nullable: + self.type = 'nullable_Py_ssize_t' + self.format_unit = 'O&' + self.converter = '_PyLong_NullablePy_ssize_tConverter' + self.impl_by_reference = self.parse_by_reference = True + if self.default is None: + self.c_default = "NULLABLE_PY_SSIZE_T_INITIALIZE(0, 1)" + elif self.default is not unspecified: + self.c_default = "NULLABLE_PY_SSIZE_T_INITIALIZE({}, 0)".format(self.default) + else: + self.c_default = "NULLABLE_PY_SSIZE_T_INITIALIZE(0, 0)" + return + + if not isinstance(self.default, int): + fail("Illegal default value for Py_ssize_t_converter") + class float_converter(CConverter): type = 'float' @@ -2263,6 +2298,8 @@ c_ignored_default = "{0.0, 0.0}" + + class object_converter(CConverter): type = 'PyObject *' format_unit = 'O' @@ -3401,7 +3438,11 @@ ## docstring first line ## - add(f.name) + if f.kind in (METHOD_NEW, METHOD_INIT): + assert f.cls + add(f.cls.name) + else: + add(f.name) add('(') # populate "right_bracket_count" field for every parameter