diff --git a/doc/release/1.17.0-notes.rst b/doc/release/1.17.0-notes.rst index 4ec086e77bfc..1fa6fb9d6a8b 100644 --- a/doc/release/1.17.0-notes.rst +++ b/doc/release/1.17.0-notes.rst @@ -328,6 +328,11 @@ concatenation. In some cases where ``np.interp`` would previously return ``np.nan``, it now returns an appropriate infinity. +Pathlib support for ``np.fromfile``, ``ndarray.tofile`` and ``ndarray.dump`` +---------------------------------------------------------------------------- +``np.fromfile``, ``np.ndarray.tofile`` and ``np.ndarray.dump`` now support +the `pathlib.Path` type for the ``file``/``fid`` parameter. + Specialized ``np.isnan``, ``np.isinf``, and ``np.isfinite`` ufuncs for bool and int types ----------------------------------------------------------------------------------------- The boolean and integer types are incapable of storing ``np.nan`` and diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py index bdff64b20ab5..78bd882f15ab 100644 --- a/numpy/core/_add_newdocs.py +++ b/numpy/core/_add_newdocs.py @@ -1152,8 +1152,12 @@ Parameters ---------- - file : file or str + file : file or str or Path Open file object or filename. + + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + dtype : data-type Data type of the returned array. For binary files, it is used to determine the size and byte-order @@ -2962,9 +2966,12 @@ Parameters ---------- - file : str + file : str or Path A string naming the dump file. + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + """)) @@ -4009,8 +4016,12 @@ Parameters ---------- - fid : file or str + fid : file or str or Path An open file object, or a string containing a filename. + + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + sep : str Separator between array items for text output. If "" (empty), a binary file is written, equivalent to diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c index 3d7e035ac00e..97eb06769b26 100644 --- a/numpy/core/src/multiarray/methods.c +++ b/numpy/core/src/multiarray/methods.c @@ -577,6 +577,10 @@ array_tofile(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } + file = NpyPath_PathlikeToFspath(file); + if (file == NULL) { + return NULL; + } if (PyBytes_Check(file) || PyUnicode_Check(file)) { file = npy_PyFile_OpenFile(file, "wb"); if (file == NULL) { diff --git a/numpy/core/src/multiarray/methods.h b/numpy/core/src/multiarray/methods.h index 7bf87f42d53f..b96a3c8a8fa4 100644 --- a/numpy/core/src/multiarray/methods.h +++ b/numpy/core/src/multiarray/methods.h @@ -1,9 +1,32 @@ #ifndef _NPY_ARRAY_METHODS_H_ #define _NPY_ARRAY_METHODS_H_ +#include "npy_import.h" + extern NPY_NO_EXPORT PyMethodDef array_methods[]; NPY_NO_EXPORT const char * npy_casting_to_string(NPY_CASTING casting); +/* Pathlib support */ +static inline PyObject * +NpyPath_PathlikeToFspath(PyObject *file) +{ + static PyObject *os_PathLike = NULL; + static PyObject *os_fspath = NULL; + npy_cache_import("numpy.compat", "os_PathLike", &os_PathLike); + if (os_PathLike == NULL) { + return NULL; + } + npy_cache_import("numpy.compat", "os_fspath", &os_fspath); + if (os_fspath == NULL) { + return NULL; + } + + if (!PyObject_IsInstance(file, os_PathLike)) { + return file; + } + return PyObject_CallFunctionObjArgs(os_fspath, file, NULL); +} + #endif diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 23195cead800..915c9fcd9794 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -2078,6 +2078,12 @@ array_fromfile(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds) Py_XDECREF(type); return NULL; } + + file = NpyPath_PathlikeToFspath(file); + if (file == NULL) { + return NULL; + } + if (offset != 0 && strcmp(sep, "") != 0) { PyErr_SetString(PyExc_TypeError, "'offset' argument only permitted for binary files"); return NULL; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index b9eca45a36c0..86af9853d28f 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -23,6 +23,14 @@ from numpy.compat import pickle +try: + import pathlib +except ImportError: + try: + import pathlib2 as pathlib + except ImportError: + pathlib = None + if sys.version_info[0] >= 3: import builtins else: @@ -4639,6 +4647,20 @@ def test_roundtrip_filename(self): y = np.fromfile(self.filename, dtype=self.dtype) assert_array_equal(y, self.x.flat) + @pytest.mark.skipif(pathlib is None, reason="pathlib not found") + def test_roundtrip_pathlib(self): + p = pathlib.Path(self.filename) + self.x.tofile(p) + y = np.fromfile(p, dtype=self.dtype) + assert_array_equal(y, self.x.flat) + + @pytest.mark.skipif(pathlib is None, reason="pathlib not found") + def test_roundtrip_dump_pathlib(self): + p = pathlib.Path(self.filename) + self.x.dump(p) + y = np.load(p, allow_pickle=True) + assert_array_equal(y, self.x) + def test_roundtrip_binary_str(self): s = self.x.tobytes() y = np.frombuffer(s, dtype=self.dtype)