diff options
-rw-r--r-- | doc/release/1.17.0-notes.rst | 21 | ||||
-rw-r--r-- | doc/source/dev/development_environment.rst | 4 | ||||
-rw-r--r-- | doc/source/f2py/run_main_session.dat | 2 | ||||
-rw-r--r-- | doc/source/f2py/usage.rst | 29 | ||||
-rw-r--r-- | numpy/core/_exceptions.py | 100 | ||||
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 20 | ||||
-rw-r--r-- | numpy/core/numeric.py | 10 | ||||
-rw-r--r-- | numpy/core/records.py | 5 | ||||
-rw-r--r-- | numpy/core/src/multiarray/compiled_base.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 5 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.c | 199 | ||||
-rw-r--r-- | numpy/core/tests/test_errstate.py | 8 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 56 | ||||
-rw-r--r-- | numpy/distutils/fcompiler/pg.py | 6 | ||||
-rw-r--r-- | numpy/f2py/__init__.py | 22 | ||||
-rwxr-xr-x | numpy/f2py/f2py2e.py | 21 | ||||
-rw-r--r-- | numpy/f2py/tests/test_compile_function.py | 17 | ||||
-rw-r--r-- | numpy/lib/nanfunctions.py | 27 | ||||
-rw-r--r-- | numpy/lib/tests/test_nanfunctions.py | 28 |
19 files changed, 466 insertions, 116 deletions
diff --git a/doc/release/1.17.0-notes.rst b/doc/release/1.17.0-notes.rst index 5d13fd5ff..4bdb812d7 100644 --- a/doc/release/1.17.0-notes.rst +++ b/doc/release/1.17.0-notes.rst @@ -86,6 +86,27 @@ new function, ``np.ctypeslib.as_ctypes`` now supports a much wider range of array types, including structures, booleans, and integers of non-native endianness. +`numpy.errstate` is now also function decorator +----------------------------------------------- + +Currently, if you have a function like:: + + def foo(): + pass + +and you want to wrap the whole thing in `errstate`, you have to rewrite it like so:: + + def foo(): + with np.errstate(...): + pass + +but with this change, you can do:: + + @np.errstate(...) + def foo(): + pass + +thereby saving a level of indentation Changes ======= diff --git a/doc/source/dev/development_environment.rst b/doc/source/dev/development_environment.rst index aa4326f63..f9b438bfd 100644 --- a/doc/source/dev/development_environment.rst +++ b/doc/source/dev/development_environment.rst @@ -8,7 +8,9 @@ Recommended development setup Since NumPy contains parts written in C and Cython that need to be compiled before use, make sure you have the necessary compilers and Python -development headers installed - see :ref:`building-from-source`. +development headers installed - see :ref:`building-from-source`. Building +NumPy as of version ``1.17`` requires a C99 compliant compiler. For +some older compilers this may require ``export CFLAGS='-std=c99'``. Having compiled code also means that importing NumPy from the development sources needs some additional steps, which are explained below. For the rest diff --git a/doc/source/f2py/run_main_session.dat b/doc/source/f2py/run_main_session.dat index b9a7e1b0d..be6cacd22 100644 --- a/doc/source/f2py/run_main_session.dat +++ b/doc/source/f2py/run_main_session.dat @@ -8,7 +8,7 @@ Post-processing... Building modules... Building module "scalar"... Wrote C/API module "scalar" to file "./scalarmodule.c" ->>> printr(r) +>>> print(r) {'scalar': {'h': ['/home/users/pearu/src_cvs/f2py/src/fortranobject.h'], 'csrc': ['./scalarmodule.c', '/home/users/pearu/src_cvs/f2py/src/fortranobject.c']}} diff --git a/doc/source/f2py/usage.rst b/doc/source/f2py/usage.rst index 0f5068e0e..5043ec430 100644 --- a/doc/source/f2py/usage.rst +++ b/doc/source/f2py/usage.rst @@ -214,32 +214,7 @@ Python module ``numpy.f2py`` The current Python interface to the ``f2py`` module is not mature and may change in the future. -The following functions are provided by the ``numpy.f2py`` module: -``run_main(<list>)`` - Equivalent to running:: +.. automodule:: numpy.f2py + :members: - f2py <args> - - where ``<args>=string.join(<list>,' ')``, but in Python. Unless - ``-h`` is used, this function returns a dictionary containing - information on generated modules and their dependencies on source - files. For example, the command ``f2py -m scalar scalar.f`` can be - executed from Python as follows - - .. include:: run_main_session.dat - :literal: - - You cannot build extension modules with this function, that is, - using ``-c`` is not allowed. Use ``compile`` command instead, see - below. - -``compile(source, modulename='untitled', extra_args='', verbose=1, source_fn=None)`` - Build extension module from Fortran 77 source string ``source``. - Return 0 if successful. - Note that this function actually calls ``f2py -c ..`` from shell to - ensure safety of the current Python process. - For example, - - .. include:: compile_session.dat - :literal: diff --git a/numpy/core/_exceptions.py b/numpy/core/_exceptions.py new file mode 100644 index 000000000..5e0105beb --- /dev/null +++ b/numpy/core/_exceptions.py @@ -0,0 +1,100 @@ +""" +Various richly-typed exceptions, that also help us deal with string formatting +in python where it's easier. + +By putting the formatting in `__str__`, we also avoid paying the cost for +users who silence the exceptions. +""" +from numpy.core.overrides import set_module + + +def _unpack_tuple(tup): + if len(tup) == 1: + return tup[0] + else: + return tup + + +def _display_as_base(cls): + """ + A decorator that makes an exception class look like its base. + + We use this to hide subclasses that are implementation details - the user + should catch the base type, which is what the traceback will show them. + + Classes decorated with this decorator are subject to removal without a + deprecation warning. + """ + assert issubclass(cls, Exception) + cls.__name__ = cls.__base__.__name__ + cls.__qualname__ = cls.__base__.__qualname__ + return cls + + +class UFuncTypeError(TypeError): + """ Base class for all ufunc exceptions """ + def __init__(self, ufunc): + self.ufunc = ufunc + + +@_display_as_base +class _UFuncNoLoopError(UFuncTypeError): + """ Thrown when a ufunc loop cannot be found """ + def __init__(self, ufunc, dtypes): + super().__init__(ufunc) + self.dtypes = tuple(dtypes) + + def __str__(self): + return ( + "ufunc {!r} did not contain a loop with signature matching types " + "{!r} -> {!r}" + ).format( + self.ufunc.__name__, + _unpack_tuple(self.dtypes[:self.ufunc.nin]), + _unpack_tuple(self.dtypes[self.ufunc.nin:]) + ) + + +@_display_as_base +class _UFuncCastingError(UFuncTypeError): + def __init__(self, ufunc, casting, from_, to): + super().__init__(ufunc) + self.casting = casting + self.from_ = from_ + self.to = to + + +@_display_as_base +class _UFuncInputCastingError(_UFuncCastingError): + """ Thrown when a ufunc input cannot be casted """ + def __init__(self, ufunc, casting, from_, to, i): + super().__init__(ufunc, casting, from_, to) + self.in_i = i + + def __str__(self): + # only show the number if more than one input exists + i_str = "{} ".format(self.in_i) if self.ufunc.nin != 1 else "" + return ( + "Cannot cast ufunc {!r} input {}from {!r} to {!r} with casting " + "rule {!r}" + ).format( + self.ufunc.__name__, i_str, self.from_, self.to, self.casting + ) + + +@_display_as_base +class _UFuncOutputCastingError(_UFuncCastingError): + """ Thrown when a ufunc output cannot be casted """ + def __init__(self, ufunc, casting, from_, to, i): + super().__init__(ufunc, casting, from_, to) + self.out_i = i + + def __str__(self): + # only show the number if more than one output exists + i_str = "{} ".format(self.out_i) if self.ufunc.nout != 1 else "" + return ( + "Cannot cast ufunc {!r} output {}from {!r} to {!r} with casting " + "rule {!r}" + ).format( + self.ufunc.__name__, i_str, self.from_, self.to, self.casting + ) diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index b0b749c80..cfa1dba6a 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -950,12 +950,12 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); */ -#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) -#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS(m, NPY_ARRAY_WRITEABLE) -#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS(m, NPY_ARRAY_ALIGNED) +#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS((m), NPY_ARRAY_WRITEABLE) +#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS((m), NPY_ARRAY_ALIGNED) -#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) -#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS) +#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_F_CONTIGUOUS) /* the variable is used in some places, so always define it */ #define NPY_BEGIN_THREADS_DEF PyThreadState *_save=NULL; @@ -965,15 +965,15 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); #define NPY_BEGIN_THREADS do {_save = PyEval_SaveThread();} while (0); #define NPY_END_THREADS do { if (_save) \ { PyEval_RestoreThread(_save); _save = NULL;} } while (0); -#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if (loop_size > 500) \ +#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if ((loop_size) > 500) \ { _save = PyEval_SaveThread();} } while (0); #define NPY_BEGIN_THREADS_DESCR(dtype) \ - do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \ + do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \ NPY_BEGIN_THREADS;} while (0); #define NPY_END_THREADS_DESCR(dtype) \ - do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \ + do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \ NPY_END_THREADS; } while (0); #define NPY_ALLOW_C_API_DEF PyGILState_STATE __save__; @@ -1110,7 +1110,7 @@ struct PyArrayIterObject_tag { /* Iterator API */ -#define PyArrayIter_Check(op) PyObject_TypeCheck(op, &PyArrayIter_Type) +#define PyArrayIter_Check(op) PyObject_TypeCheck((op), &PyArrayIter_Type) #define _PyAIT(it) ((PyArrayIterObject *)(it)) #define PyArray_ITER_RESET(it) do { \ @@ -1188,7 +1188,7 @@ struct PyArrayIterObject_tag { #define PyArray_ITER_GOTO1D(it, ind) do { \ int __npy_i; \ - npy_intp __npy_ind = (npy_intp) (ind); \ + npy_intp __npy_ind = (npy_intp)(ind); \ if (__npy_ind < 0) __npy_ind += _PyAIT(it)->size; \ _PyAIT(it)->index = __npy_ind; \ if (_PyAIT(it)->nd_m1 == 0) { \ diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 8a8efddf3..1b8f36c3e 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -12,6 +12,7 @@ import operator import sys import warnings import numbers +import contextlib import numpy as np from . import multiarray @@ -2990,7 +2991,7 @@ _Unspecified = _unspecified() @set_module('numpy') -class errstate(object): +class errstate(contextlib.ContextDecorator): """ errstate(**kwargs) @@ -3000,7 +3001,12 @@ class errstate(object): that context to execute with a known error handling behavior. Upon entering the context the error handling is set with `seterr` and `seterrcall`, and upon exiting it is reset to what it was before. - + + .. versionchanged:: 1.17.0 + `errstate` is also usable as a function decorator, saving + a level of indentation if an entire function is wrapped. + See :py:class:`contextlib.ContextDecorator` for more information. + Parameters ---------- kwargs : {divide, over, under, invalid} diff --git a/numpy/core/records.py b/numpy/core/records.py index 4ea83accc..42aca5b60 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -721,7 +721,7 @@ def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None, a string""" if dtype is None and formats is None: - raise ValueError("Must have dtype= or formats=") + raise TypeError("fromstring() needs a 'dtype' or 'formats' argument") if dtype is not None: descr = sb.dtype(dtype) @@ -768,6 +768,9 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, >>> r.shape (10,) """ + + if dtype is None and formats is None: + raise TypeError("fromfile() needs a 'dtype' or 'formats' argument") if (shape is None or shape == 0): shape = (-1,) diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 88924a860..625028bfb 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -328,6 +328,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) } else { Py_XDECREF(values); Py_XDECREF(mask); + PyArray_ResolveWritebackIfCopy(array); Py_XDECREF(array); Py_RETURN_NONE; } @@ -358,6 +359,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) fail: Py_XDECREF(mask); + PyArray_ResolveWritebackIfCopy(array); Py_XDECREF(array); Py_XDECREF(values); return NULL; diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 63bf5377a..b2e329d45 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -2500,6 +2500,11 @@ PyArray_FromInterface(PyObject *origin) &PyArray_Type, dtype, n, dims, NULL, data, dataflags, NULL, base); + /* + * Ref to dtype was stolen by PyArray_NewFromDescrAndBase + * Prevent DECREFing dtype in fail codepath by setting to NULL + */ + dtype = NULL; if (ret == NULL) { goto fail; } diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index c2d81fc5d..a4a59faa9 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -16,6 +16,7 @@ #include "npy_config.h" #include "npy_pycompat.h" +#include "npy_import.h" #include "numpy/ufuncobject.h" #include "ufunc_type_resolution.h" @@ -27,6 +28,26 @@ #include "cblasfuncs.h" #endif +static PyObject * +npy_casting_to_py_object(NPY_CASTING casting) +{ + switch (casting) { + case NPY_NO_CASTING: + return PyUString_FromString("no"); + case NPY_EQUIV_CASTING: + return PyUString_FromString("equiv"); + case NPY_SAFE_CASTING: + return PyUString_FromString("safe"); + case NPY_SAME_KIND_CASTING: + return PyUString_FromString("same_kind"); + case NPY_UNSAFE_CASTING: + return PyUString_FromString("unsafe"); + default: + return PyInt_FromLong(casting); + } +} + + static const char * npy_casting_to_string(NPY_CASTING casting) { @@ -46,6 +67,9 @@ npy_casting_to_string(NPY_CASTING casting) } } +/** + * Always returns -1 to indicate the exception was raised, for convenience + */ static int raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) { PyObject *errmsg; @@ -63,6 +87,126 @@ raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) { return -1; } +/** Helper function to raise UFuncNoLoopError + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_no_loop_found_error( + PyUFuncObject *ufunc, PyArray_Descr **dtypes, npy_intp n_dtypes) +{ + static PyObject *exc_type = NULL; + PyObject *exc_value; + PyObject *dtypes_tup; + npy_intp i; + + npy_cache_import( + "numpy.core._exceptions", "_UFuncNoLoopError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + /* convert dtypes to a tuple */ + dtypes_tup = PyTuple_New(n_dtypes); + if (dtypes_tup == NULL) { + return -1; + } + for (i = 0; i < n_dtypes; ++i) { + Py_INCREF(dtypes[i]); + PyTuple_SET_ITEM(dtypes_tup, i, (PyObject *)dtypes[i]); + } + + /* produce an error object */ + exc_value = PyTuple_Pack(2, ufunc, dtypes_tup); + Py_DECREF(dtypes_tup); + if (exc_value == NULL){ + return -1; + } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + + return -1; +} + +static int +raise_casting_error( + PyObject *exc_type, + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + PyObject *exc_value; + PyObject *casting_value; + + casting_value = npy_casting_to_py_object(casting); + if (casting_value == NULL) { + return -1; + } + + exc_value = Py_BuildValue( + "ONOOi", + ufunc, + casting_value, + (PyObject *)from, + (PyObject *)to, + i + ); + if (exc_value == NULL){ + return -1; + } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + + return -1; +} + +/** Helper function to raise UFuncInputCastingError + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_input_casting_error( + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + static PyObject *exc_type = NULL; + npy_cache_import( + "numpy.core._exceptions", "_UFuncInputCastingError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + return raise_casting_error(exc_type, ufunc, casting, from, to, i); +} + + +/** Helper function to raise UFuncOutputCastingError + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_output_casting_error( + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + static PyObject *exc_type = NULL; + npy_cache_import( + "numpy.core._exceptions", "_UFuncOutputCastingError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + return raise_casting_error(exc_type, ufunc, casting, from, to, i); +} + /*UFUNC_API * @@ -79,45 +223,18 @@ PyUFunc_ValidateCasting(PyUFuncObject *ufunc, PyArray_Descr **dtypes) { int i, nin = ufunc->nin, nop = nin + ufunc->nout; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); for (i = 0; i < nop; ++i) { if (i < nin) { if (!PyArray_CanCastArrayTo(operands[i], dtypes[i], casting)) { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast ufunc %s " - "input from ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[i]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" with casting rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; + return raise_input_casting_error( + ufunc, casting, PyArray_DESCR(operands[i]), dtypes[i], i); } } else if (operands[i] != NULL) { if (!PyArray_CanCastTypeTo(dtypes[i], PyArray_DESCR(operands[i]), casting)) { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast ufunc %s " - "output from ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[i]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" with casting rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; + return raise_output_casting_error( + ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i); } } } @@ -1382,12 +1499,8 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc, { int nargs = ufunc->nargs; char *types; - const char *ufunc_name; - PyObject *errmsg; int i, j; - ufunc_name = ufunc_get_name_cstr(ufunc); - /* * If there are user-loops search them first. * TODO: There needs to be a loop selection acceleration structure, @@ -1422,19 +1535,7 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc, types += nargs; } - errmsg = PyUString_FromFormat("ufunc '%s' did not contain a loop " - "with signature matching types ", ufunc_name); - for (i = 0; i < nargs; ++i) { - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - if (i < nargs - 1) { - PyUString_ConcatAndDel(&errmsg, PyUString_FromString(" ")); - } - } - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - - return -1; + return raise_no_loop_found_error(ufunc, dtypes, nargs); } typedef struct { @@ -2251,7 +2352,7 @@ type_tuple_type_resolver(PyUFuncObject *self, /* If no function was found, throw an error */ PyErr_Format(PyExc_TypeError, - "No loop matching the specified signature and casting\n" + "No loop matching the specified signature and casting " "was found for ufunc %s", ufunc_name); return -1; diff --git a/numpy/core/tests/test_errstate.py b/numpy/core/tests/test_errstate.py index 670d485c1..0008c4cc8 100644 --- a/numpy/core/tests/test_errstate.py +++ b/numpy/core/tests/test_errstate.py @@ -39,3 +39,11 @@ class TestErrstate(object): with np.errstate(call=None): assert_(np.geterrcall() is None, 'call is not None') assert_(np.geterrcall() is olderrcall, 'call is not olderrcall') + + def test_errstate_decorator(self): + @np.errstate(all='ignore') + def foo(): + a = -np.arange(3) + a // 0 + + foo() diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 3dd45c5ce..241f8e48a 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -5920,7 +5920,7 @@ class TestMatmul(MatmulCommon): assert_array_equal(out, tgt, err_msg=msg) # test out with not allowed type cast (safe casting) - msg = "Cannot cast ufunc matmul output" + msg = "Cannot cast ufunc .* output" out = np.zeros((5, 2), dtype=np.int32) assert_raises_regex(TypeError, msg, self.matmul, a, b, out=out) @@ -7009,12 +7009,11 @@ class TestArrayAttributeDeletion(object): assert_raises(AttributeError, delattr, a, s) -def test_array_interface(): - # Test scalar coercion within the array interface +class TestArrayInterface(): class Foo(object): def __init__(self, value): self.value = value - self.iface = {'typestr': '=f8'} + self.iface = {'typestr': 'f8'} def __float__(self): return float(self.value) @@ -7023,22 +7022,39 @@ def test_array_interface(): def __array_interface__(self): return self.iface + f = Foo(0.5) - assert_equal(np.array(f), 0.5) - assert_equal(np.array([f]), [0.5]) - assert_equal(np.array([f, f]), [0.5, 0.5]) - assert_equal(np.array(f).dtype, np.dtype('=f8')) - # Test various shape definitions - f.iface['shape'] = () - assert_equal(np.array(f), 0.5) - f.iface['shape'] = None - assert_raises(TypeError, np.array, f) - f.iface['shape'] = (1, 1) - assert_equal(np.array(f), [[0.5]]) - f.iface['shape'] = (2,) - assert_raises(ValueError, np.array, f) - - # test scalar with no shape + + @pytest.mark.parametrize('val, iface, expected', [ + (f, {}, 0.5), + ([f], {}, [0.5]), + ([f, f], {}, [0.5, 0.5]), + (f, {'shape': ()}, 0.5), + (f, {'shape': None}, TypeError), + (f, {'shape': (1, 1)}, [[0.5]]), + (f, {'shape': (2,)}, ValueError), + (f, {'strides': ()}, 0.5), + (f, {'strides': (2,)}, ValueError), + (f, {'strides': 16}, TypeError), + ]) + def test_scalar_interface(self, val, iface, expected): + # Test scalar coercion within the array interface + self.f.iface = {'typestr': 'f8'} + self.f.iface.update(iface) + if HAS_REFCOUNT: + pre_cnt = sys.getrefcount(np.dtype('f8')) + if isinstance(expected, type): + assert_raises(expected, np.array, val) + else: + result = np.array(val) + assert_equal(np.array(val), expected) + assert result.dtype == 'f8' + del result + if HAS_REFCOUNT: + post_cnt = sys.getrefcount(np.dtype('f8')) + assert_equal(pre_cnt, post_cnt) + +def test_interface_no_shape(): class ArrayLike(object): array = np.array(1) __array_interface__ = array.__array_interface__ @@ -7713,6 +7729,8 @@ class TestWritebackIfCopy(object): # uses arr_insert np.place(a, a>2, [44, 55]) assert_equal(a, np.array([[0, 44], [1, 55], [2, 44]])) + # hit one of the failing paths + assert_raises(ValueError, np.place, a, a>20, []) def test_put_noncontiguous(self): a = np.arange(6).reshape(2,3).T # force non-c-contiguous diff --git a/numpy/distutils/fcompiler/pg.py b/numpy/distutils/fcompiler/pg.py index 99071800a..cdba0e39a 100644 --- a/numpy/distutils/fcompiler/pg.py +++ b/numpy/distutils/fcompiler/pg.py @@ -33,7 +33,7 @@ class PGroupFCompiler(FCompiler): 'compiler_f77': ["pgfortran"], 'compiler_fix': ["pgfortran", "-Mfixed"], 'compiler_f90': ["pgfortran"], - 'linker_so': ["pgfortran", "-shared", "-fpic"], + 'linker_so': ["pgfortran"], 'archiver': ["ar", "-cr"], 'ranlib': ["ranlib"] } @@ -56,6 +56,10 @@ class PGroupFCompiler(FCompiler): def get_flags_linker_so(self): return ["-dynamic", '-undefined', 'dynamic_lookup'] + else: + def get_flags_linker_so(self): + return ["-shared", '-fpic'] + def runtime_library_dir_option(self, dir): return '-R"%s"' % dir diff --git a/numpy/f2py/__init__.py b/numpy/f2py/__init__.py index 23a4b7c41..d146739bb 100644 --- a/numpy/f2py/__init__.py +++ b/numpy/f2py/__init__.py @@ -28,12 +28,16 @@ def compile(source, extension='.f' ): """ - Build extension module from processing source with f2py. + Build extension module from a Fortran 77 source string with f2py. Parameters ---------- - source : str + source : str or bytes Fortran source of module / subroutine to compile + + .. versionchanged:: 1.16.0 + Accept str as well as bytes + modulename : str, optional The name of the compiled python module extra_args : str or list, optional @@ -55,6 +59,16 @@ def compile(source, .. versionadded:: 1.11.0 + Returns + ------- + result : int + 0 on success + + Examples + -------- + .. include:: compile_session.dat + :literal: + """ import tempfile import shlex @@ -67,9 +81,11 @@ def compile(source, else: fname = source_fn + if not isinstance(source, str): + source = str(source, 'utf-8') try: with open(fname, 'w') as f: - f.write(str(source)) + f.write(source) args = ['-c', '-m', modulename, f.name] diff --git a/numpy/f2py/f2py2e.py b/numpy/f2py/f2py2e.py index 8750ed0b3..47223151f 100755 --- a/numpy/f2py/f2py2e.py +++ b/numpy/f2py/f2py2e.py @@ -396,8 +396,25 @@ def dict_append(d_out, d_in): def run_main(comline_list): - """Run f2py as if string.join(comline_list,' ') is used as a command line. - In case of using -h flag, return None. + """ + Equivalent to running:: + + f2py <args> + + where ``<args>=string.join(<list>,' ')``, but in Python. Unless + ``-h`` is used, this function returns a dictionary containing + information on generated modules and their dependencies on source + files. For example, the command ``f2py -m scalar scalar.f`` can be + executed from Python as follows + + You cannot build extension modules with this function, that is, + using ``-c`` is not allowed. Use ``compile`` command instead + + Examples + -------- + .. include:: run_main_session.dat + :literal: + """ crackfortran.reset_global_f2py_vars() f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__)) diff --git a/numpy/f2py/tests/test_compile_function.py b/numpy/f2py/tests/test_compile_function.py index 74e0804e2..36abf05f9 100644 --- a/numpy/f2py/tests/test_compile_function.py +++ b/numpy/f2py/tests/test_compile_function.py @@ -106,3 +106,20 @@ def test_f2py_init_compile_bad_cmd(): assert_equal(ret_val, 127) finally: sys.executable = temp + + +@pytest.mark.parametrize('fsource', + ['program test_f2py\nend program test_f2py', + b'program test_f2py\nend program test_f2py',]) +def test_compile_from_strings(tmpdir, fsource): + # Make sure we can compile str and bytes gh-12796 + cwd = os.getcwd() + try: + os.chdir(str(tmpdir)) + ret_val = numpy.f2py.compile( + fsource, + modulename='test_compile_from_strings', + extension='.f90') + assert_equal(ret_val, 0) + finally: + os.chdir(cwd) diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index b3bf1880b..77c851fcf 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -40,6 +40,33 @@ __all__ = [ ] +def _nan_mask(a, out=None): + """ + Parameters + ---------- + a : array-like + Input array with at least 1 dimension. + out : ndarray, optional + Alternate output array in which to place the result. The default + is ``None``; if provided, it must have the same shape as the + expected output and will prevent the allocation of a new array. + + Returns + ------- + y : bool ndarray or True + A bool array where ``np.nan`` positions are marked with ``False`` + and other positions are marked with ``True``. If the type of ``a`` + is such that it can't possibly contain ``np.nan``, returns ``True``. + """ + # we assume that a is an array for this private function + + if a.dtype.kind not in 'fc': + return True + + y = np.isnan(a, out=out) + y = np.invert(y, out=y) + return y + def _replace_nan(a, val): """ If `a` is of inexact type, make a copy of `a`, replace NaNs with diff --git a/numpy/lib/tests/test_nanfunctions.py b/numpy/lib/tests/test_nanfunctions.py index 504372faf..b7261c63f 100644 --- a/numpy/lib/tests/test_nanfunctions.py +++ b/numpy/lib/tests/test_nanfunctions.py @@ -1,8 +1,10 @@ from __future__ import division, absolute_import, print_function import warnings +import pytest import numpy as np +from numpy.lib.nanfunctions import _nan_mask from numpy.testing import ( assert_, assert_equal, assert_almost_equal, assert_no_warnings, assert_raises, assert_array_equal, suppress_warnings @@ -925,3 +927,29 @@ class TestNanFunctions_Quantile(object): p = p.tolist() np.nanquantile(np.arange(100.), p, interpolation="midpoint") assert_array_equal(p, p0) + +@pytest.mark.parametrize("arr, expected", [ + # array of floats with some nans + (np.array([np.nan, 5.0, np.nan, np.inf]), + np.array([False, True, False, True])), + # int64 array that can't possibly have nans + (np.array([1, 5, 7, 9], dtype=np.int64), + True), + # bool array that can't possibly have nans + (np.array([False, True, False, True]), + True), + # 2-D complex array with nans + (np.array([[np.nan, 5.0], + [np.nan, np.inf]], dtype=np.complex64), + np.array([[False, True], + [False, True]])), + ]) +def test__nan_mask(arr, expected): + for out in [None, np.empty(arr.shape, dtype=np.bool_)]: + actual = _nan_mask(arr, out=out) + assert_equal(actual, expected) + # the above won't distinguish between True proper + # and an array of True values; we want True proper + # for types that can't possibly contain NaN + if type(expected) is not np.ndarray: + assert actual is True |