Skip to content

Commit 1740dd3

Browse files
authored
Merge pull request #479 from Blosc/fix-view
Fix view
2 parents eec7a47 + c0889b8 commit 1740dd3

3 files changed

Lines changed: 33 additions & 9 deletions

File tree

src/blosc2/blosc2_ext.pyx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,9 +2356,10 @@ cdef class slice_flatter:
23562356
cdef class NDArray:
23572357
cdef b2nd_array_t* array
23582358

2359-
def __init__(self, array):
2359+
def __init__(self, array, base=None):
23602360
self._dtype = None
23612361
self.array = <b2nd_array_t *> PyCapsule_GetPointer(array, <char *> "b2nd_array_t*")
2362+
self.base = base # add reference to base if NDArray is a view
23622363

23632364
@property
23642365
def shape(self) -> tuple[int]:
@@ -2996,5 +2997,7 @@ def expand_dims(arr1: NDArray, axis_mask: list[bool], final_dims: int) -> blosc2
29962997
mask_[i] = axis_mask[i]
29972998
_check_rc(b2nd_expand_dims(arr1.array, &view, mask_, final_dims),"Error while expanding the arrays")
29982999

3000+
# create view with reference to arr1 to hold onto
3001+
new_base = arr1 if arr1.base is None else arr1.base
29993002
return blosc2.NDArray(_schunk=PyCapsule_New(view.sc, <char *> "blosc2_schunk*", NULL),
3000-
_array=PyCapsule_New(view, <char *> "b2nd_array_t*", NULL))
3003+
_array=PyCapsule_New(view, <char *> "b2nd_array_t*", NULL), _base=new_base)

src/blosc2/ndarray.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,8 @@ def __init__(self, **kwargs):
11731173
self._keep_last_read = False
11741174
# Where to store the last read data
11751175
self._last_read = {}
1176-
super().__init__(kwargs["_array"])
1176+
base = kwargs.pop("_base", None)
1177+
super().__init__(kwargs["_array"], base=base)
11771178
# Accessor to fields
11781179
self._fields = {}
11791180
if self.dtype.fields:

tests/ndarray/test_resize.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,36 @@ def test_resize(shape, new_shape, chunks, blocks, fill_value):
4242
)
4343
def test_expand_dims(shape, axis, chunks, blocks, fill_value):
4444
a = blosc2.full(shape, fill_value=fill_value, chunks=chunks, blocks=blocks)
45-
45+
npa = a[:]
4646
b = blosc2.expand_dims(a, axis=axis)
47-
npa = np.expand_dims(a[:], axis)
48-
assert npa.shape == b.shape
49-
np.testing.assert_array_equal(npa, b[:])
47+
npb = np.expand_dims(npa, axis)
48+
assert npb.shape == b.shape
49+
np.testing.assert_array_equal(npb, b[:])
5050

5151
# Repeated expansion
5252
axis = (axis,) if isinstance(axis, int) else axis
5353
axis = axis[0] if (len(axis) + b.ndim) > blosc2.MAX_DIM else axis
5454
b = blosc2.expand_dims(b, axis=axis)
55+
npb = np.expand_dims(npb, axis)
56+
assert npb.shape == b.shape
57+
np.testing.assert_array_equal(npb, b[:])
58+
59+
# Check that handling of views is correct
60+
a = blosc2.expand_dims(a, axis=axis) # could lose ref to original array and thus dealloc data
5561
npa = np.expand_dims(npa, axis)
56-
assert npa.shape == b.shape
57-
np.testing.assert_array_equal(npa, b[:])
62+
assert a[()].shape == npa[()].shape # getitem fails if deallocate has happened
63+
64+
# Now check that garbage collecting works and there will be no memory leaks for views
65+
import sys
66+
67+
arr = np.arange(4)
68+
bloscarr_ = blosc2.asarray(arr)
69+
assert sys.getrefcount(arr) == sys.getrefcount(bloscarr_) == 2
70+
71+
view = np.expand_dims(arr, 0)
72+
bloscview = blosc2.expand_dims(bloscarr_, 0)
73+
assert sys.getrefcount(arr) == sys.getrefcount(bloscarr_) == 3
74+
75+
del view
76+
del bloscview
77+
assert sys.getrefcount(arr) == sys.getrefcount(bloscarr_) == 2

0 commit comments

Comments
 (0)