diff options
author | Jaime Fernandez <jaime.frio@gmail.com> | 2015-03-09 20:56:14 -0700 |
---|---|---|
committer | Jaime Fernandez <jaime.frio@gmail.com> | 2015-03-09 21:36:19 -0700 |
commit | 3a39a0e6ac0d85d3365c0671d8010441180a7d95 (patch) | |
tree | 9a3c0eeb04182098ab4d9a5e37eb09177439373c /numpy | |
parent | 8e026a2225996e9b790e4c63816c5f6c985a5d20 (diff) | |
download | numpy-3a39a0e6ac0d85d3365c0671d8010441180a7d95.tar.gz |
ENH: Strict checking of ufunc keyword argument names
Raises a TypeError if any of the keyword arguments supplied to a
ufunc does not exactly match the name in the signature. Prior to
this, trailing characters were discarded, e.g. 'out2' would be
treated as if it where 'out'.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 25 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 23 |
2 files changed, 39 insertions, 9 deletions
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 5e4a49553..3e52aa764 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -808,6 +808,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, int type_num; int any_flexible = 0, any_object = 0, any_flexible_userloops = 0; + int has_sig = 0; ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>"; @@ -945,7 +946,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, switch (str[0]) { case 'c': /* Provides a policy for allowed casting */ - if (strncmp(str, "casting", 7) == 0) { + if (strcmp(str, "casting") == 0) { if (!PyArray_CastingConverter(value, out_casting)) { goto fail; } @@ -954,7 +955,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, break; case 'd': /* Another way to specify 'sig' */ - if (strncmp(str, "dtype", 5) == 0) { + if (strcmp(str, "dtype") == 0) { /* Allow this parameter to be None */ PyArray_Descr *dtype; if (!PyArray_DescrConverter2(value, &dtype)) { @@ -976,7 +977,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * Overrides the global parameters buffer size, * error mask, and error object */ - if (strncmp(str, "extobj", 6) == 0) { + if (strcmp(str, "extobj") == 0) { *out_extobj = value; bad_arg = 0; } @@ -987,7 +988,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * either as a single array or None for single output * ufuncs, or as a tuple of arrays and Nones. */ - if (strncmp(str, "out", 3) == 0) { + if (strcmp(str, "out") == 0) { if (nargs > nin) { PyErr_SetString(PyExc_ValueError, "cannot specify 'out' as both a " @@ -1048,7 +1049,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, bad_arg = 0; } /* Allows the default output layout to be overridden */ - else if (strncmp(str,"order",5) == 0) { + else if (strcmp(str, "order") == 0) { if (!PyArray_OrderConverter(value, out_order)) { goto fail; } @@ -1057,7 +1058,13 @@ get_ufunc_arguments(PyUFuncObject *ufunc, break; case 's': /* Allows a specific function inner loop to be selected */ - if (strncmp(str,"sig",3) == 0) { + if (strcmp(str, "sig") == 0 || + strcmp(str, "signature") == 0) { + if (has_sig == 1) { + PyErr_SetString(PyExc_ValueError, + "cannot specify both 'sig' and 'signature'"); + goto fail; + } if (*out_typetup != NULL) { PyErr_SetString(PyExc_RuntimeError, "cannot specify both 'sig' and 'dtype'"); @@ -1066,8 +1073,9 @@ get_ufunc_arguments(PyUFuncObject *ufunc, *out_typetup = value; Py_INCREF(value); bad_arg = 0; + has_sig = 1; } - else if (strncmp(str,"subok",5) == 0) { + else if (strcmp(str, "subok") == 0) { if (!PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "'subok' must be a boolean"); @@ -1082,8 +1090,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * Provides a boolean array 'where=' mask if * out_wheremask is supplied. */ - if (out_wheremask != NULL && - strncmp(str,"where",5) == 0) { + if (out_wheremask != NULL && strcmp(str, "where") == 0) { PyArray_Descr *dtype; dtype = PyArray_DescrFromType(NPY_BOOL); if (dtype == NULL) { diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index a285d5334..ab885bbf0 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -9,6 +9,29 @@ import numpy.core.operand_flag_tests as opflag_tests from numpy.compat import asbytes from numpy.core.test_rational import * + +class TestUfuncKwargs(TestCase): + def test_kwarg_exact(self): + assert_raises(TypeError, np.add, 1, 2, castingx='safe') + assert_raises(TypeError, np.add, 1, 2, dtypex=np.int) + assert_raises(TypeError, np.add, 1, 2, extobjx=[4096]) + assert_raises(TypeError, np.add, 1, 2, outx=None) + assert_raises(TypeError, np.add, 1, 2, sigx='ii->i') + assert_raises(TypeError, np.add, 1, 2, signaturex='ii->i') + assert_raises(TypeError, np.add, 1, 2, subokx=False) + assert_raises(TypeError, np.add, 1, 2, wherex=[True]) + + def test_sig_signature(self): + assert_raises(ValueError, np.add, 1, 2, sig='ii->i', + signature='ii->i') + + def test_sig_dtype(self): + assert_raises(RuntimeError, np.add, 1, 2, sig='ii->i', + dtype=np.int) + assert_raises(RuntimeError, np.add, 1, 2, signature='ii->i', + dtype=np.int) + + class TestUfunc(TestCase): def test_pickle(self): import pickle |