Skip to content

Commit 9032a78

Browse files
authored
Raise NumPy wrong argument error details (pyccel#1964)
When an argument of the wrong type is passed to a Pyccel function the following error is raised: ``` TypeError: Expected an argument of type int32[:] for argument x ``` for an array this error is not very detailed as it does not specify if the problem is due to the rank, the data type, or the order. The error message containing these details is actually set in `pyarray_check`, however the error is overwritten by the code in the cwrapper (which is added to also raise errors for other types, e.g scalars). The only additional information in the error message is the name of the variable which has the incorrect type. This PR adds the name as an argument to the function `pyarray_check` and stops the wrapper from overwriting the generated error message. The error message is now: ``` TypeError: Wrong argument type for argument x : argument dtype must be Int32, not Int64, argument rank must be 2, not 3, argument does not have the expected ordering (C) ``` **Commit Summary** - Add the `name` argument to `pyarray_check` - Allow the use of a `LiteralString` as a `char*` argument in the C wrapper - Stop overwriting the `PyErr` string if the type check is carried out for an ND array
1 parent 37527f1 commit 9032a78

File tree

6 files changed

+32
-17
lines changed

6 files changed

+32
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ All notable changes to this project will be documented in this file.
6363
- #1836 : Move `epyccel` module to `pyccel.commands.epyccel` and add support for shortcut import `from pyccel import epyccel`.
6464
- #1720 : functions with the `@inline` decorator are no longer exposed to Python in the shared library.
6565
- #1720 : Error raised when incompatible arguments are passed to an `inlined` function is now fatal.
66+
- #1964 : Improve the error message when the wrong type is passed as a NumPy array argument.
6667
- \[INTERNALS\] `FunctionDef` is annotated when it is called, or at the end of the `CodeBlock` if it is never called.
6768
- \[INTERNALS\] `InlinedFunctionDef` is only annotated if it is called.
6869
- \[INTERNALS\] Build `utilities.metaclasses.ArgumentSingleton` on the fly to ensure correct docstrings.

pyccel/ast/numpy_wrapper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from .bind_c import BindCPointer
1313

14-
from .datatypes import PythonNativeBool, GenericType, VoidType, FixedSizeType
14+
from .datatypes import PythonNativeBool, GenericType, VoidType, FixedSizeType, CharType
1515

1616
from .cwrapper import PyccelPyObject, check_type_registry, c_to_py_registry, pytype_parse_registry
1717

@@ -101,6 +101,7 @@ def get_numpy_max_acceptable_version_file():
101101
pyarray_check = FunctionDef(
102102
name = 'pyarray_check',
103103
arguments = [
104+
FunctionDefArgument(Variable(CharType(), 'name', memory_handling='alias')),
104105
FunctionDefArgument(Variable(PyccelPyObject(), 'a', memory_handling='alias')),
105106
FunctionDefArgument(Variable(CNativeInt(), 'dtype')),
106107
FunctionDefArgument(Variable(CNativeInt(), 'rank')),

pyccel/codegen/printing/cwrappercode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def is_c_pointer(self, a):
8888
"""
8989
if isinstance(a.dtype, (WrapperCustomDataType, BindCPointer)):
9090
return True
91-
elif isinstance(a, (PyBuildValueNode, PyCapsule_New, PyCapsule_Import, PyModule_Create)):
91+
elif isinstance(a, (PyBuildValueNode, PyCapsule_New, PyCapsule_Import, PyModule_Create, LiteralString)):
9292
return True
9393
else:
9494
return CCodePrinter.is_c_pointer(self,a)

pyccel/codegen/wrapper/c_to_python_wrapper.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def _get_check_function(self, py_obj, arg, raise_error):
282282
results = [FunctionDefResult(Variable(dtype, name = 'v'))])
283283

284284
func_call = FunctionCall(func, [py_obj])
285-
else:
285+
elif isinstance(arg.class_type, NumpyNDArrayType):
286286
try :
287287
type_ref = numpy_dtype_registry[dtype]
288288
except KeyError:
@@ -297,12 +297,19 @@ def _get_check_function(self, py_obj, arg, raise_error):
297297
else:
298298
flag = numpy_flag_c_contig
299299

300-
check_func = pyarray_check if raise_error else is_numpy_array
301-
# No error code required as the error is raised inside pyarray_check
300+
if raise_error:
301+
check_func = pyarray_check
302+
func_call = FunctionCall(check_func, [ObjectAddress(LiteralString(arg.name)), py_obj, type_ref, LiteralInteger(rank), flag])
303+
else:
304+
check_func = is_numpy_array
302305

303-
func_call = FunctionCall(check_func, [py_obj, type_ref, LiteralInteger(rank), flag])
306+
func_call = FunctionCall(check_func, [py_obj, type_ref, LiteralInteger(rank), flag])
307+
else:
308+
errors.report(f"Can't check the type of an array of {dtype}\n"+PYCCEL_RESTRICTION_TODO,
309+
symbol=arg, severity='fatal')
304310

305-
if raise_error:
311+
if raise_error and not isinstance(arg.class_type, NumpyNDArrayType):
312+
# No error code required for arrays as the error is raised inside pyarray_check
306313
message = LiteralString(f"Expected an argument of type {arg.class_type} for argument {arg.name}")
307314
python_error = FunctionCall(PyErr_SetString, [PyTypeError, message])
308315
error_code = (python_error,)

pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.c

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -334,14 +334,15 @@ PyObject* ndarray_to_pyarray(t_ndarray o)
334334
* Check Python Object (DataType, Rank, Order):
335335
*
336336
* Parameters :
337+
* name : the name of the argument (used for error output)
337338
* a : python array object
338339
* dtype : desired data type enum
339340
* rank : desired rank
340341
* flag : desired order flag
341342
* Returns :
342343
* return true if no error occurred otherwise it will return false
343344
*/
344-
bool pyarray_check(PyObject *o, int dtype, int rank, int flag)
345+
bool pyarray_check(char* name, PyObject *o, int dtype, int rank, int flag)
345346
{
346347
char* array_type = _check_pyarray_type(o);
347348
if (array_type != NULL) {
@@ -352,37 +353,42 @@ bool pyarray_check(PyObject *o, int dtype, int rank, int flag)
352353

353354
PyArrayObject* a = (PyArrayObject*)o;
354355

355-
char error[600];
356-
error[0] = '\0';
356+
bool correct_type = true;
357+
char error[800];
358+
sprintf(error, "Wrong argument type for argument %s : ", name);
357359

358360
// check array element type / rank / order
359361
char* array_dtype = _check_pyarray_dtype(a, dtype);
360362
if (array_dtype != NULL) {
361363
strcat(error, array_dtype);
362364
free(array_dtype);
365+
correct_type = false;
363366
}
364367

365368
char* array_rank = _check_pyarray_rank(a, rank);
366369
if (array_rank != NULL) {
370+
if (!correct_type)
371+
strcat(error, ", ");
367372
strcat(error, array_rank);
368373
free(array_rank);
374+
correct_type = false;
369375
}
370376

371377
if (rank > 1) {
372378
char* array_order = _check_pyarray_order(a, flag);
373379
if (array_order != NULL) {
380+
if (!correct_type)
381+
strcat(error, ", ");
374382
strcat(error, array_order);
375383
free(array_order);
384+
correct_type = false;
376385
}
377386
}
378387

379-
if (error[0] != '\0') {
380-
PyErr_Format(PyExc_TypeError, error);
381-
return false;
382-
}
383-
else {
384-
return true;
388+
if (!correct_type) {
389+
PyErr_SetString(PyExc_TypeError, error);
385390
}
391+
return correct_type;
386392
}
387393

388394
bool is_numpy_array(PyObject *o, int dtype, int rank, int flag)

pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ PyObject* fortran_ndarray_to_pyarray(t_ndarray o);
4444

4545

4646
/* arrays checkers and helpers */
47-
bool pyarray_check(PyObject *o, int dtype, int rank, int flag);
47+
bool pyarray_check(char* name, PyObject *o, int dtype, int rank, int flag);
4848
bool is_numpy_array(PyObject *o, int dtype, int rank, int flag);
4949

5050
void *nd_data(t_ndarray *a);

0 commit comments

Comments
 (0)