Skip to content

Commit d031cac

Browse files
authored
Merge pull request numpy#27335 from stefanv/savez-allow-pickle
ENH: Add ``allow_pickle`` flag to ``savez``
2 parents 47b5710 + 8a4ef4b commit d031cac

File tree

3 files changed

+53
-17
lines changed

3 files changed

+53
-17
lines changed

numpy/lib/_npyio_impl.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -588,13 +588,13 @@ def save(file, arr, allow_pickle=True, fix_imports=np._NoValue):
588588
pickle_kwargs=dict(fix_imports=fix_imports))
589589

590590

591-
def _savez_dispatcher(file, *args, **kwds):
591+
def _savez_dispatcher(file, *args, allow_pickle=True, **kwds):
592592
yield from args
593593
yield from kwds.values()
594594

595595

596596
@array_function_dispatch(_savez_dispatcher)
597-
def savez(file, *args, **kwds):
597+
def savez(file, *args, allow_pickle=True, **kwds):
598598
"""Save several arrays into a single file in uncompressed ``.npz`` format.
599599
600600
Provide arrays as keyword arguments to store them under the
@@ -614,6 +614,14 @@ def savez(file, *args, **kwds):
614614
Arrays to save to the file. Please use keyword arguments (see
615615
`kwds` below) to assign names to arrays. Arrays specified as
616616
args will be named "arr_0", "arr_1", and so on.
617+
allow_pickle : bool, optional
618+
Allow saving object arrays using Python pickles. Reasons for
619+
disallowing pickles include security (loading pickled data can execute
620+
arbitrary code) and portability (pickled objects may not be loadable
621+
on different Python installations, for example if the stored objects
622+
require libraries that are not available, and not all pickled data is
623+
compatible between different versions of Python).
624+
Default: True
617625
kwds : Keyword arguments, optional
618626
Arrays to save to the file. Each array will be saved to the
619627
output file with its corresponding keyword name.
@@ -678,16 +686,16 @@ def savez(file, *args, **kwds):
678686
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
679687
680688
"""
681-
_savez(file, args, kwds, False)
689+
_savez(file, args, kwds, False, allow_pickle=allow_pickle)
682690

683691

684-
def _savez_compressed_dispatcher(file, *args, **kwds):
692+
def _savez_compressed_dispatcher(file, *args, allow_pickle=True, **kwds):
685693
yield from args
686694
yield from kwds.values()
687695

688696

689697
@array_function_dispatch(_savez_compressed_dispatcher)
690-
def savez_compressed(file, *args, **kwds):
698+
def savez_compressed(file, *args, allow_pickle=True, **kwds):
691699
"""
692700
Save several arrays into a single file in compressed ``.npz`` format.
693701
@@ -708,6 +716,14 @@ def savez_compressed(file, *args, **kwds):
708716
Arrays to save to the file. Please use keyword arguments (see
709717
`kwds` below) to assign names to arrays. Arrays specified as
710718
args will be named "arr_0", "arr_1", and so on.
719+
allow_pickle : bool, optional
720+
Allow saving object arrays using Python pickles. Reasons for
721+
disallowing pickles include security (loading pickled data can execute
722+
arbitrary code) and portability (pickled objects may not be loadable
723+
on different Python installations, for example if the stored objects
724+
require libraries that are not available, and not all pickled data is
725+
compatible between different versions of Python).
726+
Default: True
711727
kwds : Keyword arguments, optional
712728
Arrays to save to the file. Each array will be saved to the
713729
output file with its corresponding keyword name.
@@ -750,7 +766,7 @@ def savez_compressed(file, *args, **kwds):
750766
True
751767
752768
"""
753-
_savez(file, args, kwds, True)
769+
_savez(file, args, kwds, True, allow_pickle=allow_pickle)
754770

755771

756772
def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None):
@@ -777,17 +793,17 @@ def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None):
777793
compression = zipfile.ZIP_STORED
778794

779795
zipf = zipfile_factory(file, mode="w", compression=compression)
780-
781-
for key, val in namedict.items():
782-
fname = key + '.npy'
783-
val = np.asanyarray(val)
784-
# always force zip64, gh-10776
785-
with zipf.open(fname, 'w', force_zip64=True) as fid:
786-
format.write_array(fid, val,
787-
allow_pickle=allow_pickle,
788-
pickle_kwargs=pickle_kwargs)
789-
790-
zipf.close()
796+
try:
797+
for key, val in namedict.items():
798+
fname = key + '.npy'
799+
val = np.asanyarray(val)
800+
# always force zip64, gh-10776
801+
with zipf.open(fname, 'w', force_zip64=True) as fid:
802+
format.write_array(fid, val,
803+
allow_pickle=allow_pickle,
804+
pickle_kwargs=pickle_kwargs)
805+
finally:
806+
zipf.close()
791807

792808

793809
def _ensure_ndmin_ndarray_check_param(ndmin):

numpy/lib/_npyio_impl.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,14 @@ def save(
158158
def savez(
159159
file: str | os.PathLike[str] | _SupportsWrite[bytes],
160160
*args: ArrayLike,
161+
allow_pickle: bool = ...,
161162
**kwds: ArrayLike,
162163
) -> None: ...
163164

164165
def savez_compressed(
165166
file: str | os.PathLike[str] | _SupportsWrite[bytes],
166167
*args: ArrayLike,
168+
allow_pickle: bool = ...,
167169
**kwds: ArrayLike,
168170
) -> None: ...
169171

numpy/lib/tests/test_io.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,7 @@ def test_load_refcount():
27902790
x = np.loadtxt(TextIO("0 1 2 3"), dtype=dt)
27912791
assert_equal(x, np.array([((0, 1), (2, 3))], dtype=dt))
27922792

2793+
27932794
def test_load_multiple_arrays_until_eof():
27942795
f = BytesIO()
27952796
np.save(f, 1)
@@ -2799,3 +2800,20 @@ def test_load_multiple_arrays_until_eof():
27992800
assert np.load(f) == 2
28002801
with pytest.raises(EOFError):
28012802
np.load(f)
2803+
2804+
2805+
def test_savez_nopickle():
2806+
obj_array = np.array([1, 'hello'], dtype=object)
2807+
with temppath(suffix='.npz') as tmp:
2808+
np.savez(tmp, obj_array)
2809+
2810+
with temppath(suffix='.npz') as tmp:
2811+
with pytest.raises(ValueError, match="Object arrays cannot be saved when.*"):
2812+
np.savez(tmp, obj_array, allow_pickle=False)
2813+
2814+
with temppath(suffix='.npz') as tmp:
2815+
np.savez_compressed(tmp, obj_array)
2816+
2817+
with temppath(suffix='.npz') as tmp:
2818+
with pytest.raises(ValueError, match="Object arrays cannot be saved when.*"):
2819+
np.savez_compressed(tmp, obj_array, allow_pickle=False)

0 commit comments

Comments
 (0)