From 645465c65cf33d6202aa43b71bef2d007f81ef9c Mon Sep 17 00:00:00 2001 From: sidhant007 Date: Wed, 24 Jun 2020 12:37:10 +0800 Subject: [PATCH 1/5] ENH: Add file like support to np.core.records.fromfile (#2504) --- numpy/compat/py3k.py | 5 ++++- numpy/compat/tests/test_compat.py | 18 +++++++++++++++++- numpy/core/records.py | 18 ++++++++---------- numpy/core/tests/test_records.py | 7 +++++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/numpy/compat/py3k.py b/numpy/compat/py3k.py index fd9f8bd4217e..0ea40107575f 100644 --- a/numpy/compat/py3k.py +++ b/numpy/compat/py3k.py @@ -10,7 +10,7 @@ compatibility with numpy while they transition their code to newer versions of Python. """ -__all__ = ['bytes', 'asbytes', 'isfileobj', 'getexception', 'strchar', +__all__ = ['bytes', 'asbytes', 'isfileobj', 'isfilelikeobj', 'getexception', 'strchar', 'unicode', 'asunicode', 'asbytes_nested', 'asunicode_nested', 'asstr', 'open_latin1', 'long', 'basestring', 'sixu', 'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path', @@ -53,6 +53,9 @@ def asstr(s): def isfileobj(f): return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)) +def isfilelikeobj(f): + return hasattr(f, 'readinto') + def open_latin1(filename, mode='r'): return open(filename, mode=mode, encoding='iso-8859-1') diff --git a/numpy/compat/tests/test_compat.py b/numpy/compat/tests/test_compat.py index 2b8acbaa0662..a6f7a8f49db0 100644 --- a/numpy/compat/tests/test_compat.py +++ b/numpy/compat/tests/test_compat.py @@ -1,9 +1,11 @@ from os.path import join -from numpy.compat import isfileobj +from numpy.compat import isfileobj, isfilelikeobj from numpy.testing import assert_ from numpy.testing import tempdir +import io +import gzip def test_isfileobj(): with tempdir(prefix="numpy_test_compat_") as folder: @@ -17,3 +19,17 @@ def test_isfileobj(): with open(filename, 'rb') as f: assert_(isfileobj(f)) + +def test_isfilelikeobj(): + with tempdir(prefix="numpy_test_compat_") as folder: + filename = join(folder, 'a.bin') + + with open(filename, 'wb') as f: + assert_(isfilelikeobj(f)) + + with gzip.open(filename, 'rb') as f: + assert_(isfilelikeobj(f)) + + assert_(isfilelikeobj(io.BytesIO())) + + assert_(not isfilelikeobj(io.StringIO())) diff --git a/numpy/core/records.py b/numpy/core/records.py index 0d3fd9118396..df91091b74ff 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -40,7 +40,7 @@ from . import numeric as sb from . import numerictypes as nt from numpy.compat import ( - isfileobj, os_fspath, contextlib_nullcontext + isfilelikeobj, os_fspath, contextlib_nullcontext ) from numpy.core.overrides import set_module from .arrayprint import get_printoptions @@ -847,12 +847,10 @@ def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None, return _array def get_remaining_size(fd): - try: - fn = fd.fileno() - except AttributeError: - return os.path.getsize(fd.name) - fd.tell() - st = os.fstat(fn) - size = st.st_size - fd.tell() + pos = fd.tell() + fd.seek(0, 2) + size = fd.tell() - pos + fd.seek(pos, 0) return size def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, @@ -911,7 +909,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, elif isinstance(shape, int): shape = (shape,) - if isfileobj(fd): + if isfilelikeobj(fd): # file already opened ctx = contextlib_nullcontext(fd) else: @@ -1036,7 +1034,7 @@ def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, array('def', dtype=' Date: Mon, 29 Jun 2020 10:24:07 +0800 Subject: [PATCH 2/5] Refactor: Remove isfilelikeobj function --- numpy/compat/py3k.py | 5 +---- numpy/compat/tests/test_compat.py | 18 +----------------- numpy/core/records.py | 17 +++++++++-------- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/numpy/compat/py3k.py b/numpy/compat/py3k.py index 0ea40107575f..fd9f8bd4217e 100644 --- a/numpy/compat/py3k.py +++ b/numpy/compat/py3k.py @@ -10,7 +10,7 @@ compatibility with numpy while they transition their code to newer versions of Python. """ -__all__ = ['bytes', 'asbytes', 'isfileobj', 'isfilelikeobj', 'getexception', 'strchar', +__all__ = ['bytes', 'asbytes', 'isfileobj', 'getexception', 'strchar', 'unicode', 'asunicode', 'asbytes_nested', 'asunicode_nested', 'asstr', 'open_latin1', 'long', 'basestring', 'sixu', 'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path', @@ -53,9 +53,6 @@ def asstr(s): def isfileobj(f): return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)) -def isfilelikeobj(f): - return hasattr(f, 'readinto') - def open_latin1(filename, mode='r'): return open(filename, mode=mode, encoding='iso-8859-1') diff --git a/numpy/compat/tests/test_compat.py b/numpy/compat/tests/test_compat.py index a6f7a8f49db0..2b8acbaa0662 100644 --- a/numpy/compat/tests/test_compat.py +++ b/numpy/compat/tests/test_compat.py @@ -1,11 +1,9 @@ from os.path import join -from numpy.compat import isfileobj, isfilelikeobj +from numpy.compat import isfileobj from numpy.testing import assert_ from numpy.testing import tempdir -import io -import gzip def test_isfileobj(): with tempdir(prefix="numpy_test_compat_") as folder: @@ -19,17 +17,3 @@ def test_isfileobj(): with open(filename, 'rb') as f: assert_(isfileobj(f)) - -def test_isfilelikeobj(): - with tempdir(prefix="numpy_test_compat_") as folder: - filename = join(folder, 'a.bin') - - with open(filename, 'wb') as f: - assert_(isfilelikeobj(f)) - - with gzip.open(filename, 'rb') as f: - assert_(isfilelikeobj(f)) - - assert_(isfilelikeobj(io.BytesIO())) - - assert_(not isfilelikeobj(io.StringIO())) diff --git a/numpy/core/records.py b/numpy/core/records.py index df91091b74ff..c512be8921a8 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -40,7 +40,7 @@ from . import numeric as sb from . import numerictypes as nt from numpy.compat import ( - isfilelikeobj, os_fspath, contextlib_nullcontext + os_fspath, contextlib_nullcontext ) from numpy.core.overrides import set_module from .arrayprint import get_printoptions @@ -848,10 +848,11 @@ def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None, def get_remaining_size(fd): pos = fd.tell() - fd.seek(0, 2) - size = fd.tell() - pos - fd.seek(pos, 0) - return size + try: + fd.seek(0, 2) + return fd.tell() - pos + finally: + fd.seek(pos, 0) def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, names=None, titles=None, aligned=False, byteorder=None): @@ -909,7 +910,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, elif isinstance(shape, int): shape = (shape,) - if isfilelikeobj(fd): + if hasattr(fd, 'readinto'): # file already opened ctx = contextlib_nullcontext(fd) else: @@ -1034,7 +1035,7 @@ def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, array('def', dtype=' Date: Fri, 3 Jul 2020 14:44:10 +0800 Subject: [PATCH 3/5] Add comment for better explanation --- numpy/core/records.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/numpy/core/records.py b/numpy/core/records.py index c512be8921a8..e95be0e3f9e0 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -911,6 +911,8 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, shape = (shape,) if hasattr(fd, 'readinto'): + # GH issue 2504. fd supports io.RawIOBase or io.BufferedIOBase interface. + # Example of fd: gzip, BytesIO, BufferedReader # file already opened ctx = contextlib_nullcontext(fd) else: From c2727d074c4503a9c77698c04ea9e1725a4e17a0 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 10 Aug 2020 10:42:42 +0300 Subject: [PATCH 4/5] DOC: add release note --- doc/release/upcoming_changes/16675.improvement.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 doc/release/upcoming_changes/16675.improvement.rst diff --git a/doc/release/upcoming_changes/16675.improvement.rst b/doc/release/upcoming_changes/16675.improvement.rst new file mode 100644 index 000000000000..310e44cfb57a --- /dev/null +++ b/doc/release/upcoming_changes/16675.improvement.rst @@ -0,0 +1,3 @@ +`numpy.core.records.fromfile` now supports file-like objects +------------------------------------------------------------ +`numpy.rec.fromfile` can now use file-like objects, for instance `BytesIO` From 58d3bc9cf8bec2cac213724652dc374ee2ea72b2 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Mon, 10 Aug 2020 12:00:39 -0700 Subject: [PATCH 5/5] DOC: intersphinx release note --- doc/release/upcoming_changes/16675.improvement.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/release/upcoming_changes/16675.improvement.rst b/doc/release/upcoming_changes/16675.improvement.rst index 310e44cfb57a..bc70d7e0f0b6 100644 --- a/doc/release/upcoming_changes/16675.improvement.rst +++ b/doc/release/upcoming_changes/16675.improvement.rst @@ -1,3 +1,4 @@ `numpy.core.records.fromfile` now supports file-like objects ------------------------------------------------------------ -`numpy.rec.fromfile` can now use file-like objects, for instance `BytesIO` +`numpy.rec.fromfile` can now use file-like objects, for instance +:py:class:`io.BytesIO`