summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorJaime Fernandez <jaime.frio@gmail.com>2015-03-09 20:56:14 -0700
committerJaime Fernandez <jaime.frio@gmail.com>2015-03-09 21:36:19 -0700
commit3a39a0e6ac0d85d3365c0671d8010441180a7d95 (patch)
tree9a3c0eeb04182098ab4d9a5e37eb09177439373c /numpy
parent8e026a2225996e9b790e4c63816c5f6c985a5d20 (diff)
downloadnumpy-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.c25
-rw-r--r--numpy/core/tests/test_ufunc.py23
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