Skip to content

Commit 492c543

Browse files
committed
docstrings and comments; unicode array.array disallowed
1 parent 5084c56 commit 492c543

File tree

2 files changed

+77
-25
lines changed

2 files changed

+77
-25
lines changed

numcodecs/compat.py

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,42 @@ def ensure_text(l, encoding='utf-8'):
3434

3535

3636
def ensure_ndarray(buf, dtype=None):
37-
"""TODO"""
37+
"""Convenience function to coerce `buf` to a numpy array, if it is not already a
38+
numpy array.
3839
39-
# make that we create an array from a memory buffer with no copy
40+
Parameters
41+
----------
42+
buf : array-like or bytes-like
43+
A numpy array or any object exporting a buffer interface.
44+
dtype : dtype, optional
45+
Request that the data be viewed as the given dtype.
4046
41-
if isinstance(buf, np.ndarray):
47+
Returns
48+
-------
49+
arr : ndarray
50+
A numpy array, sharing memory with `buf`.
51+
52+
Notes
53+
-----
54+
This function will not create a copy under any circumstances, it is guaranteed to
55+
return a view on memory exported by `buf`.
4256
57+
"""
58+
59+
if isinstance(buf, np.ndarray):
4360
# already a numpy array
4461
arr = buf
4562

63+
elif isinstance(buf, array.array) and buf.typecode == 'u':
64+
# guard condition, do not support array.array with unicode type, this is
65+
# problematic because numpy does not support it on all platforms
66+
raise TypeError('array.array with unicode type is not supported')
67+
4668
else:
4769

70+
# N.B., first take a memoryview to make sure that we subsequently create a
71+
# numpy array from a memory buffer with no copy
72+
4873
if PY2: # pragma: py3 no cover
4974
try:
5075
mem = memoryview(buf)
@@ -59,30 +84,45 @@ def ensure_ndarray(buf, dtype=None):
5984
arr = np.array(mem, copy=False)
6085

6186
if PY2 and isinstance(buf, array.array): # pragma: py3 no cover
62-
6387
# type information will not have been propagated via the old-style buffer
64-
# interface, so we have to manually hack it after the fact
65-
if buf.typecode == 'u':
66-
t = 'U1'
67-
else:
68-
t = buf.typecode
69-
arr = arr.view(t)
88+
# interface, so we have to manually hack it back in after the fact
89+
arr = arr.view(buf.typecode)
7090

7191
if dtype is not None:
72-
7392
# view as requested dtype
7493
arr = arr.view(dtype)
7594

7695
return arr
7796

7897

7998
def ensure_contiguous_ndarray(buf, dtype=None):
80-
"""TODO"""
99+
"""Convenience function to coerce `buf` to a numpy array, if it is not already a
100+
numpy array. Also ensures that the returned value exports fully contiguous memory,
101+
and supports the new-style buffer interface.
102+
103+
Parameters
104+
----------
105+
buf : array-like or bytes-like
106+
A numpy array or any object exporting a buffer interface.
107+
dtype : dtype, optional
108+
Request that the data be viewed as the given dtype.
109+
110+
Returns
111+
-------
112+
arr : ndarray
113+
A numpy array, sharing memory with `buf`.
114+
115+
Notes
116+
-----
117+
This function will not create a copy under any circumstances, it is guaranteed to
118+
return a view on memory exported by `buf`.
119+
120+
"""
81121

82122
# ensure input is a numpy array
83123
arr = ensure_ndarray(buf, dtype=dtype)
84124

85-
# check for datetime or timedelta ndarray, cannot take a memoryview of those
125+
# check for datetime or timedelta ndarray, the buffer interface doesn't support those
86126
if isinstance(buf, np.ndarray) and buf.dtype.kind in 'Mm':
87127
arr = arr.view(np.int64)
88128

@@ -93,7 +133,6 @@ def ensure_contiguous_ndarray(buf, dtype=None):
93133

94134
# check memory is contiguous, if so flatten
95135
if arr.flags.c_contiguous or arr.flags.f_contiguous:
96-
97136
# can flatten without copy
98137
arr = arr.reshape(-1, order='A')
99138

@@ -103,18 +142,18 @@ def ensure_contiguous_ndarray(buf, dtype=None):
103142
return arr
104143

105144

106-
def ensure_bytes(o):
107-
"""Obtain a bytes object from memory exposed by `o`."""
145+
def ensure_bytes(buf):
146+
"""Obtain a bytes object from memory exposed by `buf`."""
108147

109-
if not isinstance(o, binary_type):
148+
if not isinstance(buf, binary_type):
110149

111150
# go via numpy, for convenience
112-
a = ensure_contiguous_ndarray(o)
151+
arr = ensure_ndarray(buf)
113152

114153
# create bytes
115-
o = a.tobytes()
154+
buf = arr.tobytes(order='A')
116155

117-
return o
156+
return buf
118157

119158

120159
def ndarray_copy(src, dst):

numcodecs/tests/test_compat.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,22 @@ def test_ensure_contiguous_ndarray_shares_memory():
3333
('u', 4, array.array('I', b'qwertyuiqwertyui')),
3434
('f', 4, array.array('f', b'qwertyuiqwertyui')),
3535
('f', 8, array.array('d', b'qwertyuiqwertyui')),
36-
('U', 4, array.array('u', u'qwertyuiqwertyui')),
36+
('i', 1, array.array('b', b'qwertyuiqwertyui')),
37+
('u', 1, array.array('B', b'qwertyuiqwertyui')),
3738
('u', 1, mmap.mmap(-1, 10))
3839
]
39-
for typ, siz, buf in typed_bufs:
40+
if PY2: # pragma: py3 no cover
41+
typed_bufs.append(
42+
('S', 1, array.array('c', b'qwertyuiqwertyui')),
43+
)
44+
for expected_kind, expected_itemsize, buf in typed_bufs:
4045
a = ensure_contiguous_ndarray(buf)
4146
assert isinstance(a, np.ndarray)
42-
assert typ == a.dtype.kind
43-
assert siz == a.dtype.itemsize
47+
assert expected_kind == a.dtype.kind
48+
if isinstance(buf, array.array):
49+
assert buf.itemsize == a.dtype.itemsize
50+
else:
51+
assert expected_itemsize == a.dtype.itemsize
4452
if PY2: # pragma: py3 no cover
4553
assert np.shares_memory(a, np.getbuffer(buf))
4654
else: # pragma: py2 no cover
@@ -59,8 +67,13 @@ def test_ensure_contiguous_ndarray_invalid_inputs():
5967
with pytest.raises(ValueError):
6068
ensure_contiguous_ndarray(np.arange(100)[::2])
6169

70+
# unicode array.array not allowed
71+
a = array.array('u', u'qwertyuiqwertyui')
72+
with pytest.raises(TypeError):
73+
ensure_contiguous_ndarray(a)
6274

63-
def test_ensure_contiguous_ndarray_writable():
75+
76+
def test_ensure_contiguous_ndarray_writeable():
6477
# check that the writeability of the underlying buffer is preserved
6578
for writeable in [False, True]:
6679
a = np.arange(100)

0 commit comments

Comments
 (0)