changeset 4972:537bbfff0c5c v7.3.1231

updated for version 7.3.1231 Problem: Python: use of numbers not consistent. Solution: Add support for Number protocol. (ZyX)
author Bram Moolenaar <bram@vim.org>
date Sun, 23 Jun 2013 14:16:57 +0200
parents 7faa16c489af
children c2b13a582971
files src/if_py_both.h src/if_python.c src/if_python3.c src/testdir/test86.ok src/testdir/test87.ok src/version.c
diffstat 6 files changed, 157 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -151,6 +151,95 @@ StringToChars(PyObject *object, PyObject
     return (char_u *) p;
 }
 
+#define NUMBER_LONG     1
+#define NUMBER_INT      2
+#define NUMBER_NATURAL  4
+#define NUMBER_UNSIGNED 8
+
+    static int
+NumberToLong(PyObject *obj, long *result, int flags)
+{
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check(obj))
+    {
+	*result = PyInt_AsLong(obj);
+	if (PyErr_Occurred())
+	    return -1;
+    }
+    else
+#endif
+    if (PyLong_Check(obj))
+    {
+	*result = PyLong_AsLong(obj);
+	if (PyErr_Occurred())
+	    return -1;
+    }
+    else if (PyNumber_Check(obj))
+    {
+	PyObject	*num;
+
+	if (!(num = PyNumber_Long(obj)))
+	    return -1;
+
+	*result = PyLong_AsLong(num);
+
+	Py_DECREF(num);
+
+	if (PyErr_Occurred())
+	    return -1;
+    }
+    else
+    {
+	PyErr_FORMAT(PyExc_TypeError,
+#if PY_MAJOR_VERSION < 3
+		"expected int(), long() or something supporting "
+		"coercing to long(), but got %s"
+#else
+		"expected int() or something supporting coercing to int(), "
+		"but got %s"
+#endif
+		, Py_TYPE_NAME(obj));
+	return -1;
+    }
+
+    if (flags & NUMBER_INT)
+    {
+	if (*result > INT_MAX)
+	{
+	    PyErr_SET_STRING(PyExc_OverflowError,
+		    "value is too large to fit into C int type");
+	    return -1;
+	}
+	else if (*result < INT_MIN)
+	{
+	    PyErr_SET_STRING(PyExc_OverflowError,
+		    "value is too small to fit into C int type");
+	    return -1;
+	}
+    }
+
+    if (flags & NUMBER_NATURAL)
+    {
+	if (*result <= 0)
+	{
+	    PyErr_SET_STRING(PyExc_ValueError,
+		    "number must be greater then zero");
+	    return -1;
+	}
+    }
+    else if (flags & NUMBER_UNSIGNED)
+    {
+	if (*result < 0)
+	{
+	    PyErr_SET_STRING(PyExc_ValueError,
+		    "number must be greater or equal to zero");
+	    return -1;
+	}
+    }
+
+    return 0;
+}
+
     static int
 add_string(PyObject *list, char *s)
 {
@@ -243,13 +332,8 @@ OutputSetattr(OutputObject *self, char *
 
     if (strcmp(name, "softspace") == 0)
     {
-	if (!PyInt_Check(val))
-	{
-	    PyErr_SET_STRING(PyExc_TypeError, "softspace must be an integer");
+	if (NumberToLong(val, &(self->softspace), NUMBER_UNSIGNED))
 	    return -1;
-	}
-
-	self->softspace = PyInt_AsLong(val);
 	return 0;
     }
 
@@ -2839,23 +2923,15 @@ OptionsAssItem(OptionsObject *self, PyOb
     }
     else if (flags & SOPT_NUM)
     {
-	int val;
-
-#if PY_MAJOR_VERSION < 3
-	if (PyInt_Check(valObject))
-	    val = PyInt_AsLong(valObject);
-	else
-#endif
-	if (PyLong_Check(valObject))
-	    val = PyLong_AsLong(valObject);
-	else
+	long	val;
+
+	if (NumberToLong(valObject, &val, NUMBER_INT))
 	{
-	    PyErr_SET_STRING(PyExc_TypeError, "object must be integer");
 	    Py_XDECREF(todecref);
 	    return -1;
 	}
 
-	r = set_option_value_for(key, val, NULL, opt_flags,
+	r = set_option_value_for(key, (int) val, NULL, opt_flags,
 				self->opt_type, self->from);
     }
     else
@@ -3265,10 +3341,10 @@ WindowSetattr(WindowObject *self, char *
     }
     else if (strcmp(name, "height") == 0)
     {
-	int	height;
+	long	height;
 	win_T	*savewin;
 
-	if (!PyArg_Parse(val, "i", &height))
+	if (NumberToLong(val, &height, NUMBER_INT))
 	    return -1;
 
 #ifdef FEAT_GUI
@@ -3278,7 +3354,7 @@ WindowSetattr(WindowObject *self, char *
 	curwin = self->win;
 
 	VimTryStart();
-	win_setheight(height);
+	win_setheight((int) height);
 	curwin = savewin;
 	if (VimTryEnd())
 	    return -1;
@@ -3288,10 +3364,10 @@ WindowSetattr(WindowObject *self, char *
 #ifdef FEAT_VERTSPLIT
     else if (strcmp(name, "width") == 0)
     {
-	int	width;
+	long	width;
 	win_T	*savewin;
 
-	if (!PyArg_Parse(val, "i", &width))
+	if (NumberToLong(val, &width, NUMBER_INT))
 	    return -1;
 
 #ifdef FEAT_GUI
@@ -3301,7 +3377,7 @@ WindowSetattr(WindowObject *self, char *
 	curwin = self->win;
 
 	VimTryStart();
-	win_setwidth(width);
+	win_setwidth((int) width);
 	curwin = savewin;
 	if (VimTryEnd())
 	    return -1;
@@ -4555,22 +4631,12 @@ BufMapLength(PyObject *self UNUSED)
 BufMapItem(PyObject *self UNUSED, PyObject *keyObject)
 {
     buf_T	*b;
-    int		bnr;
-
-#if PY_MAJOR_VERSION < 3
-    if (PyInt_Check(keyObject))
-	bnr = PyInt_AsLong(keyObject);
-    else
-#endif
-    if (PyLong_Check(keyObject))
-	bnr = PyLong_AsLong(keyObject);
-    else
-    {
-	PyErr_SET_STRING(PyExc_TypeError, "key must be integer");
+    long	bnr;
+
+    if (NumberToLong(keyObject, &bnr, NUMBER_INT|NUMBER_NATURAL))
 	return NULL;
-    }
-
-    b = buflist_findnr(bnr);
+
+    b = buflist_findnr((int) bnr);
 
     if (b)
 	return BufferNew(b);
@@ -5345,12 +5411,16 @@ ConvertFromPyObject(PyObject *obj, typva
     {
 	tv->v_type = VAR_NUMBER;
 	tv->vval.v_number = (varnumber_T) PyInt_AsLong(obj);
+	if (PyErr_Occurred())
+	    return -1;
     }
 #endif
     else if (PyLong_Check(obj))
     {
 	tv->v_type = VAR_NUMBER;
 	tv->vval.v_number = (varnumber_T) PyLong_AsLong(obj);
+	if (PyErr_Occurred())
+	    return -1;
     }
     else if (PyDict_Check(obj))
 	return convert_dl(obj, tv, pydict_to_tv, lookup_dict);
@@ -5367,6 +5437,18 @@ ConvertFromPyObject(PyObject *obj, typva
 	return convert_dl(obj, tv, pyseq_to_tv, lookup_dict);
     else if (PyMapping_Check(obj))
 	return convert_dl(obj, tv, pymap_to_tv, lookup_dict);
+    else if (PyNumber_Check(obj))
+    {
+	PyObject	*num;
+
+	if (!(num = PyNumber_Long(obj)))
+	    return -1;
+
+	tv->v_type = VAR_NUMBER;
+	tv->vval.v_number = (varnumber_T) PyLong_AsLong(num);
+
+	Py_DECREF(num);
+    }
     else
     {
 	PyErr_FORMAT(PyExc_TypeError,
--- a/src/if_python.c
+++ b/src/if_python.c
@@ -220,6 +220,7 @@ struct PyMethodDef { Py_ssize_t a; };
 # define PyObject_CallFunctionObjArgs dll_PyObject_CallFunctionObjArgs
 # define PyObject_CallFunction dll_PyObject_CallFunction
 # define PyObject_Call dll_PyObject_Call
+# define PyObject_Repr dll_PyObject_Repr
 # define PyString_AsString dll_PyString_AsString
 # define PyString_AsStringAndSize dll_PyString_AsStringAndSize
 # define PyString_FromString dll_PyString_FromString
@@ -233,6 +234,8 @@ struct PyMethodDef { Py_ssize_t a; };
 # define PyFloat_AsDouble dll_PyFloat_AsDouble
 # define PyFloat_FromDouble dll_PyFloat_FromDouble
 # define PyFloat_Type (*dll_PyFloat_Type)
+# define PyNumber_Check dll_PyNumber_Check
+# define PyNumber_Long dll_PyNumber_Long
 # define PyImport_AddModule (*dll_PyImport_AddModule)
 # define PySys_SetObject dll_PySys_SetObject
 # define PySys_GetObject dll_PySys_GetObject
@@ -360,6 +363,7 @@ static PyObject* (*dll_PyObject_SetAttrS
 static PyObject* (*dll_PyObject_CallFunctionObjArgs)(PyObject *, ...);
 static PyObject* (*dll_PyObject_CallFunction)(PyObject *, char *, ...);
 static PyObject* (*dll_PyObject_Call)(PyObject *, PyObject *, PyObject *);
+static PyObject* (*dll_PyObject_Repr)(PyObject *);
 static char*(*dll_PyString_AsString)(PyObject *);
 static int(*dll_PyString_AsStringAndSize)(PyObject *, char **, int *);
 static PyObject*(*dll_PyString_FromString)(const char *);
@@ -372,6 +376,8 @@ static PyObject *(*py_PyUnicode_AsEncode
 static double(*dll_PyFloat_AsDouble)(PyObject *);
 static PyObject*(*dll_PyFloat_FromDouble)(double);
 static PyTypeObject* dll_PyFloat_Type;
+static int(*dll_PyNumber_Check)(PyObject *);
+static PyObject*(*dll_PyNumber_Long)(PyObject *);
 static int(*dll_PySys_SetObject)(char *, PyObject *);
 static PyObject *(*dll_PySys_GetObject)(char *);
 static int(*dll_PySys_SetArgv)(int, char **);
@@ -440,6 +446,7 @@ static PyObject *imp_PyExc_TypeError;
 static PyObject *imp_PyExc_ValueError;
 static PyObject *imp_PyExc_RuntimeError;
 static PyObject *imp_PyExc_ImportError;
+static PyObject *imp_PyExc_OverflowError;
 
 # define PyExc_AttributeError imp_PyExc_AttributeError
 # define PyExc_IndexError imp_PyExc_IndexError
@@ -449,6 +456,7 @@ static PyObject *imp_PyExc_ImportError;
 # define PyExc_ValueError imp_PyExc_ValueError
 # define PyExc_RuntimeError imp_PyExc_RuntimeError
 # define PyExc_ImportError imp_PyExc_ImportError
+# define PyExc_OverflowError imp_PyExc_OverflowError
 
 /*
  * Table of name to function pointer of python.
@@ -533,6 +541,7 @@ static struct
     {"PyObject_CallFunctionObjArgs", (PYTHON_PROC*)&dll_PyObject_CallFunctionObjArgs},
     {"PyObject_CallFunction", (PYTHON_PROC*)&dll_PyObject_CallFunction},
     {"PyObject_Call", (PYTHON_PROC*)&dll_PyObject_Call},
+    {"PyObject_Repr", (PYTHON_PROC*)&dll_PyObject_Repr},
     {"PyString_AsString", (PYTHON_PROC*)&dll_PyString_AsString},
     {"PyString_AsStringAndSize", (PYTHON_PROC*)&dll_PyString_AsStringAndSize},
     {"PyString_FromString", (PYTHON_PROC*)&dll_PyString_FromString},
@@ -545,6 +554,8 @@ static struct
     {"PyFloat_AsDouble", (PYTHON_PROC*)&dll_PyFloat_AsDouble},
     {"PyFloat_FromDouble", (PYTHON_PROC*)&dll_PyFloat_FromDouble},
     {"PyImport_AddModule", (PYTHON_PROC*)&dll_PyImport_AddModule},
+    {"PyNumber_Check", (PYTHON_PROC*)&dll_PyNumber_Check},
+    {"PyNumber_Long", (PYTHON_PROC*)&dll_PyNumber_Long},
     {"PySys_SetObject", (PYTHON_PROC*)&dll_PySys_SetObject},
     {"PySys_GetObject", (PYTHON_PROC*)&dll_PySys_GetObject},
     {"PySys_SetArgv", (PYTHON_PROC*)&dll_PySys_SetArgv},
@@ -722,6 +733,7 @@ get_exceptions(void)
     imp_PyExc_ValueError = PyDict_GetItemString(exdict, "ValueError");
     imp_PyExc_RuntimeError = PyDict_GetItemString(exdict, "RuntimeError");
     imp_PyExc_ImportError = PyDict_GetItemString(exdict, "ImportError");
+    imp_PyExc_OverflowError = PyDict_GetItemString(exdict, "OverflowError");
     Py_XINCREF(imp_PyExc_AttributeError);
     Py_XINCREF(imp_PyExc_IndexError);
     Py_XINCREF(imp_PyExc_KeyError);
@@ -730,6 +742,7 @@ get_exceptions(void)
     Py_XINCREF(imp_PyExc_ValueError);
     Py_XINCREF(imp_PyExc_RuntimeError);
     Py_XINCREF(imp_PyExc_ImportError);
+    Py_XINCREF(imp_PyExc_OverflowError);
     Py_XDECREF(exmod);
 }
 #endif /* DYNAMIC_PYTHON */
--- a/src/if_python3.c
+++ b/src/if_python3.c
@@ -160,6 +160,7 @@
 # define PyMapping_Keys py3_PyMapping_Keys
 # define PyIter_Next py3_PyIter_Next
 # define PyObject_GetIter py3_PyObject_GetIter
+# define PyObject_Repr py3_PyObject_Repr
 # define PyObject_GetItem py3_PyObject_GetItem
 # define PyObject_IsTrue py3_PyObject_IsTrue
 # define PyModule_GetDict py3_PyModule_GetDict
@@ -211,6 +212,8 @@
 # define PyType_Type (*py3_PyType_Type)
 # define PySlice_Type (*py3_PySlice_Type)
 # define PyFloat_Type (*py3_PyFloat_Type)
+# define PyNumber_Check (*py3_PyNumber_Check)
+# define PyNumber_Long (*py3_PyNumber_Long)
 # define PyBool_Type (*py3_PyBool_Type)
 # define PyErr_NewException py3_PyErr_NewException
 # ifdef Py_DEBUG
@@ -310,6 +313,7 @@ static PyObject* (*py3_PyLong_FromLong)(
 static PyObject* (*py3_PyDict_New)(void);
 static PyObject* (*py3_PyIter_Next)(PyObject *);
 static PyObject* (*py3_PyObject_GetIter)(PyObject *);
+static PyObject* (*py3_PyObject_Repr)(PyObject *);
 static PyObject* (*py3_PyObject_GetItem)(PyObject *, PyObject *);
 static int (*py3_PyObject_IsTrue)(PyObject *);
 static PyObject* (*py3_Py_BuildValue)(char *, ...);
@@ -365,6 +369,8 @@ static PyTypeObject* py3_PyType_Type;
 static PyTypeObject* py3_PySlice_Type;
 static PyTypeObject* py3_PyFloat_Type;
 static PyTypeObject* py3_PyBool_Type;
+static int (*py3_PyNumber_Check)(PyObject *);
+static PyObject* (*py3_PyNumber_Long)(PyObject *);
 static PyObject* (*py3_PyErr_NewException)(char *name, PyObject *base, PyObject *dict);
 static PyObject* (*py3_PyCapsule_New)(void *, char *, PyCapsule_Destructor);
 static void* (*py3_PyCapsule_GetPointer)(PyObject *, char *);
@@ -399,6 +405,7 @@ static PyObject *p3imp_PyExc_TypeError;
 static PyObject *p3imp_PyExc_ValueError;
 static PyObject *p3imp_PyExc_RuntimeError;
 static PyObject *p3imp_PyExc_ImportError;
+static PyObject *p3imp_PyExc_OverflowError;
 
 # define PyExc_AttributeError p3imp_PyExc_AttributeError
 # define PyExc_IndexError p3imp_PyExc_IndexError
@@ -408,6 +415,7 @@ static PyObject *p3imp_PyExc_ImportError
 # define PyExc_ValueError p3imp_PyExc_ValueError
 # define PyExc_RuntimeError p3imp_PyExc_RuntimeError
 # define PyExc_ImportError p3imp_PyExc_ImportError
+# define PyExc_OverflowError p3imp_PyExc_OverflowError
 
 /*
  * Table of name to function pointer of python.
@@ -469,6 +477,7 @@ static struct
     {"PyMapping_Keys", (PYTHON_PROC*)&py3_PyMapping_Keys},
     {"PyIter_Next", (PYTHON_PROC*)&py3_PyIter_Next},
     {"PyObject_GetIter", (PYTHON_PROC*)&py3_PyObject_GetIter},
+    {"PyObject_Repr", (PYTHON_PROC*)&py3_PyObject_Repr},
     {"PyObject_GetItem", (PYTHON_PROC*)&py3_PyObject_GetItem},
     {"PyObject_IsTrue", (PYTHON_PROC*)&py3_PyObject_IsTrue},
     {"PyLong_FromLong", (PYTHON_PROC*)&py3_PyLong_FromLong},
@@ -518,6 +527,8 @@ static struct
     {"PySlice_Type", (PYTHON_PROC*)&py3_PySlice_Type},
     {"PyFloat_Type", (PYTHON_PROC*)&py3_PyFloat_Type},
     {"PyBool_Type", (PYTHON_PROC*)&py3_PyBool_Type},
+    {"PyNumber_Check", (PYTHON_PROC*)&py3_PyNumber_Check},
+    {"PyNumber_Long", (PYTHON_PROC*)&py3_PyNumber_Long},
     {"PyErr_NewException", (PYTHON_PROC*)&py3_PyErr_NewException},
 # ifdef Py_DEBUG
     {"_Py_NegativeRefcount", (PYTHON_PROC*)&py3__Py_NegativeRefcount},
@@ -672,6 +683,7 @@ get_py3_exceptions()
     p3imp_PyExc_ValueError = PyDict_GetItemString(exdict, "ValueError");
     p3imp_PyExc_RuntimeError = PyDict_GetItemString(exdict, "RuntimeError");
     p3imp_PyExc_ImportError = PyDict_GetItemString(exdict, "ImportError");
+    p3imp_PyExc_OverflowError = PyDict_GetItemString(exdict, "OverflowError");
     Py_XINCREF(p3imp_PyExc_AttributeError);
     Py_XINCREF(p3imp_PyExc_IndexError);
     Py_XINCREF(p3imp_PyExc_KeyError);
@@ -680,6 +692,7 @@ get_py3_exceptions()
     Py_XINCREF(p3imp_PyExc_ValueError);
     Py_XINCREF(p3imp_PyExc_RuntimeError);
     Py_XINCREF(p3imp_PyExc_ImportError);
+    Py_XINCREF(p3imp_PyExc_OverflowError);
     Py_XDECREF(exmod);
 }
 #endif /* DYNAMIC_PYTHON3 */
--- a/src/testdir/test86.ok
+++ b/src/testdir/test86.ok
@@ -438,7 +438,7 @@ test86.in
 > Output
 >> OutputSetattr
 del sys.stdout.softspace:AttributeError:("can't delete OutputObject attributes",)
-sys.stdout.softspace = []:TypeError:('softspace must be an integer',)
+sys.stdout.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',)
 sys.stdout.attr = None:AttributeError:('invalid attribute: attr',)
 >> OutputWrite
 sys.stdout.write(None):TypeError:('coercing to Unicode: need string or buffer, NoneType found',)
@@ -1037,8 +1037,8 @@ vim.current.window.xxx:AttributeError:('
 vim.current.window.buffer = 0:TypeError:('readonly attribute: buffer',)
 vim.current.window.cursor = (100000000, 100000000):error:('cursor position outside buffer',)
 vim.current.window.cursor = True:TypeError:('argument must be 2-item sequence, not bool',)
-vim.current.window.height = "abc":TypeError:('an integer is required',)
-vim.current.window.width  = "abc":TypeError:('an integer is required',)
+vim.current.window.height = "abc":TypeError:('expected int(), long() or something supporting coercing to long(), but got str',)
+vim.current.window.width  = "abc":TypeError:('expected int(), long() or something supporting coercing to long(), but got str',)
 vim.current.window.xxxxxx = True:AttributeError:('xxxxxx',)
 > WinList
 >> WinListItem
@@ -1072,7 +1072,7 @@ vim.current.buffer.mark("!"):error:('inv
 vim.current.buffer.range(1, 2, 3):TypeError:('function takes exactly 2 arguments (3 given)',)
 > BufMap
 >> BufMapItem
-vim.buffers[None]:TypeError:('key must be integer',)
+vim.buffers[None]:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',)
 vim.buffers[100000000]:KeyError:(100000000,)
 > Current
 >> CurrentGetattr
--- a/src/testdir/test87.ok
+++ b/src/testdir/test87.ok
@@ -427,7 +427,7 @@ test87.in
 > Output
 >> OutputSetattr
 del sys.stdout.softspace:(<class 'AttributeError'>, AttributeError("can't delete OutputObject attributes",))
-sys.stdout.softspace = []:(<class 'TypeError'>, TypeError('softspace must be an integer',))
+sys.stdout.softspace = []:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',))
 sys.stdout.attr = None:(<class 'AttributeError'>, AttributeError('invalid attribute: attr',))
 >> OutputWrite
 sys.stdout.write(None):(<class 'TypeError'>, TypeError("Can't convert 'NoneType' object to str implicitly",))
@@ -1046,8 +1046,8 @@ vim.current.window.xxx:(<class 'Attribut
 vim.current.window.buffer = 0:(<class 'TypeError'>, TypeError('readonly attribute: buffer',))
 vim.current.window.cursor = (100000000, 100000000):(<class 'vim.error'>, error('cursor position outside buffer',))
 vim.current.window.cursor = True:(<class 'TypeError'>, TypeError('argument must be 2-item sequence, not bool',))
-vim.current.window.height = "abc":(<class 'TypeError'>, TypeError('an integer is required',))
-vim.current.window.width  = "abc":(<class 'TypeError'>, TypeError('an integer is required',))
+vim.current.window.height = "abc":(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got str',))
+vim.current.window.width  = "abc":(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got str',))
 vim.current.window.xxxxxx = True:(<class 'AttributeError'>, AttributeError('xxxxxx',))
 > WinList
 >> WinListItem
@@ -1081,7 +1081,7 @@ vim.current.buffer.mark("!"):(<class 'vi
 vim.current.buffer.range(1, 2, 3):(<class 'TypeError'>, TypeError('function takes exactly 2 arguments (3 given)',))
 > BufMap
 >> BufMapItem
-vim.buffers[None]:(<class 'TypeError'>, TypeError('key must be integer',))
+vim.buffers[None]:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',))
 vim.buffers[100000000]:(<class 'KeyError'>, KeyError(100000000,))
 > Current
 >> CurrentGetattr
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1231,
+/**/
     1230,
 /**/
     1229,