test_numpy_array.py 19.9 KB
Newer Older
1
2
import pytest

3
4
import env  # noqa: F401
from pybind11_tests import numpy_array as m
5

6
np = pytest.importorskip("numpy")
7
8


9
10
11
12
13
14
15
16
17
18
19
def test_dtypes():
    # See issue #1328.
    # - Platform-dependent sizes.
    for size_check in m.get_platform_dtype_size_checks():
        print(size_check)
        assert size_check.size_cpp == size_check.size_numpy, size_check
    # - Concrete sizes.
    for check in m.get_concrete_dtype_checks():
        print(check)
        assert check.numpy == check.pybind11, check
        if check.numpy.num != check.pybind11.num:
20
            print(
21
                f"NOTE: typenum mismatch for {check}: {check.numpy.num} != {check.pybind11.num}"
22
            )
23
24


25
@pytest.fixture(scope="function")
26
def arr():
27
    return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
28
29


30
def test_array_attributes():
31
    a = np.array(0, "f8")
32
33
34
    assert m.ndim(a) == 0
    assert all(m.shape(a) == [])
    assert all(m.strides(a) == [])
35
    with pytest.raises(IndexError) as excinfo:
36
        m.shape(a, 0)
37
    assert str(excinfo.value) == "invalid axis: 0 (ndim = 0)"
38
    with pytest.raises(IndexError) as excinfo:
39
        m.strides(a, 0)
40
    assert str(excinfo.value) == "invalid axis: 0 (ndim = 0)"
41
42
43
44
45
    assert m.writeable(a)
    assert m.size(a) == 1
    assert m.itemsize(a) == 8
    assert m.nbytes(a) == 8
    assert m.owndata(a)
46

47
    a = np.array([[1, 2, 3], [4, 5, 6]], "u2").view()
48
    a.flags.writeable = False
49
50
51
52
53
54
55
    assert m.ndim(a) == 2
    assert all(m.shape(a) == [2, 3])
    assert m.shape(a, 0) == 2
    assert m.shape(a, 1) == 3
    assert all(m.strides(a) == [6, 2])
    assert m.strides(a, 0) == 6
    assert m.strides(a, 1) == 2
56
    with pytest.raises(IndexError) as excinfo:
57
        m.shape(a, 2)
58
    assert str(excinfo.value) == "invalid axis: 2 (ndim = 2)"
59
    with pytest.raises(IndexError) as excinfo:
60
        m.strides(a, 2)
61
    assert str(excinfo.value) == "invalid axis: 2 (ndim = 2)"
62
63
64
65
66
    assert not m.writeable(a)
    assert m.size(a) == 6
    assert m.itemsize(a) == 2
    assert m.nbytes(a) == 12
    assert not m.owndata(a)
67
68


69
70
71
@pytest.mark.parametrize(
    "args, ret", [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)]
)
72
def test_index_offset(arr, args, ret):
73
74
75
76
    assert m.index_at(arr, *args) == ret
    assert m.index_at_t(arr, *args) == ret
    assert m.offset_at(arr, *args) == ret * arr.dtype.itemsize
    assert m.offset_at_t(arr, *args) == ret * arr.dtype.itemsize
77
78
79


def test_dim_check_fail(arr):
80
81
82
83
84
85
86
87
88
89
    for func in (
        m.index_at,
        m.index_at_t,
        m.offset_at,
        m.offset_at_t,
        m.data,
        m.data_t,
        m.mutate_data,
        m.mutate_data_t,
    ):
90
91
        with pytest.raises(IndexError) as excinfo:
            func(arr, 1, 2, 3)
92
        assert str(excinfo.value) == "too many indices for an array: 3 (ndim = 2)"
93
94


95
96
97
98
99
100
101
102
103
@pytest.mark.parametrize(
    "args, ret",
    [
        ([], [1, 2, 3, 4, 5, 6]),
        ([1], [4, 5, 6]),
        ([0, 1], [2, 3, 4, 5, 6]),
        ([1, 2], [6]),
    ],
)
104
def test_data(arr, args, ret):
105
    from sys import byteorder
106

107
    assert all(m.data_t(arr, *args) == ret)
108
109
    assert all(m.data(arr, *args)[(0 if byteorder == "little" else 1) :: 2] == ret)
    assert all(m.data(arr, *args)[(1 if byteorder == "little" else 0) :: 2] == 0)
110
111


112
@pytest.mark.parametrize("dim", [0, 1, 3])
113
def test_at_fail(arr, dim):
114
    for func in m.at_t, m.mutate_at_t:
115
116
        with pytest.raises(IndexError) as excinfo:
            func(arr, *([0] * dim))
117
        assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)"
118
119
120


def test_at(arr):
121
122
123
124
125
    assert m.at_t(arr, 0, 2) == 3
    assert m.at_t(arr, 1, 0) == 4

    assert all(m.mutate_at_t(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6])
    assert all(m.mutate_at_t(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6])
126
127


128
129
def test_mutate_readonly(arr):
    arr.flags.writeable = False
130
131
132
133
134
    for func, args in (
        (m.mutate_data, ()),
        (m.mutate_data_t, ()),
        (m.mutate_at_t, (0, 0)),
    ):
135
136
        with pytest.raises(ValueError) as excinfo:
            func(arr, *args)
137
        assert str(excinfo.value) == "array is not writeable"
138
139
140


def test_mutate_data(arr):
141
142
143
144
145
    assert all(m.mutate_data(arr).ravel() == [2, 4, 6, 8, 10, 12])
    assert all(m.mutate_data(arr).ravel() == [4, 8, 12, 16, 20, 24])
    assert all(m.mutate_data(arr, 1).ravel() == [4, 8, 12, 32, 40, 48])
    assert all(m.mutate_data(arr, 0, 1).ravel() == [4, 16, 24, 64, 80, 96])
    assert all(m.mutate_data(arr, 1, 2).ravel() == [4, 16, 24, 64, 80, 192])
146

147
148
149
150
151
    assert all(m.mutate_data_t(arr).ravel() == [5, 17, 25, 65, 81, 193])
    assert all(m.mutate_data_t(arr).ravel() == [6, 18, 26, 66, 82, 194])
    assert all(m.mutate_data_t(arr, 1).ravel() == [6, 18, 26, 67, 83, 195])
    assert all(m.mutate_data_t(arr, 0, 1).ravel() == [6, 19, 27, 68, 84, 196])
    assert all(m.mutate_data_t(arr, 1, 2).ravel() == [6, 19, 27, 68, 84, 197])
152
153
154


def test_bounds_check(arr):
155
156
157
158
159
160
161
162
163
164
    for func in (
        m.index_at,
        m.index_at_t,
        m.data,
        m.data_t,
        m.mutate_data,
        m.mutate_data_t,
        m.at_t,
        m.mutate_at_t,
    ):
165
        with pytest.raises(IndexError) as excinfo:
166
            func(arr, 2, 0)
167
        assert str(excinfo.value) == "index 2 is out of bounds for axis 0 with size 2"
168
        with pytest.raises(IndexError) as excinfo:
169
            func(arr, 0, 4)
170
        assert str(excinfo.value) == "index 4 is out of bounds for axis 1 with size 3"
171

172

173
def test_make_c_f_array():
174
175
176
177
    assert m.make_c_array().flags.c_contiguous
    assert not m.make_c_array().flags.f_contiguous
    assert m.make_f_array().flags.f_contiguous
    assert not m.make_f_array().flags.c_contiguous
178
179


180
181
182
def test_make_empty_shaped_array():
    m.make_empty_shaped_array()

183
184
185
186
187
    # empty shape means numpy scalar, PEP 3118
    assert m.scalar_int().ndim == 0
    assert m.scalar_int().shape == ()
    assert m.scalar_int() == 42

188

189
def test_wrap():
Jason Rhinelander's avatar
Jason Rhinelander committed
190
191
192
    def assert_references(a, b, base=None):
        if base is None:
            base = a
193
        assert a is not b
194
        assert a.__array_interface__["data"][0] == b.__array_interface__["data"][0]
195
196
197
198
199
200
        assert a.shape == b.shape
        assert a.strides == b.strides
        assert a.flags.c_contiguous == b.flags.c_contiguous
        assert a.flags.f_contiguous == b.flags.f_contiguous
        assert a.flags.writeable == b.flags.writeable
        assert a.flags.aligned == b.flags.aligned
201
202
        # 1.13 supported Python 3.6
        if tuple(int(x) for x in np.__version__.split(".")[:2]) >= (1, 14):
203
204
205
            assert a.flags.writebackifcopy == b.flags.writebackifcopy
        else:
            assert a.flags.updateifcopy == b.flags.updateifcopy
206
207
        assert np.all(a == b)
        assert not b.flags.owndata
Jason Rhinelander's avatar
Jason Rhinelander committed
208
        assert b.base is base
209
210
211
212
213
214
        if a.flags.writeable and a.ndim == 2:
            a[0, 0] = 1234
            assert b[0, 0] == 1234

    a1 = np.array([1, 2], dtype=np.int16)
    assert a1.flags.owndata and a1.base is None
215
    a2 = m.wrap(a1)
216
217
    assert_references(a1, a2)

218
    a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="F")
219
    assert a1.flags.owndata and a1.base is None
220
    a2 = m.wrap(a1)
221
222
    assert_references(a1, a2)

223
    a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="C")
224
    a1.flags.writeable = False
225
    a2 = m.wrap(a1)
226
227
228
    assert_references(a1, a2)

    a1 = np.random.random((4, 4, 4))
229
    a2 = m.wrap(a1)
230
231
    assert_references(a1, a2)

Jason Rhinelander's avatar
Jason Rhinelander committed
232
    a1t = a1.transpose()
233
    a2 = m.wrap(a1t)
Jason Rhinelander's avatar
Jason Rhinelander committed
234
    assert_references(a1t, a2, a1)
235

Jason Rhinelander's avatar
Jason Rhinelander committed
236
    a1d = a1.diagonal()
237
    a2 = m.wrap(a1d)
Jason Rhinelander's avatar
Jason Rhinelander committed
238
    assert_references(a1d, a2, a1)
239

240
    a1m = a1[::-1, ::-1, ::-1]
241
    a2 = m.wrap(a1m)
242
243
    assert_references(a1m, a2, a1)

244
245
246

def test_numpy_view(capture):
    with capture:
247
        ac = m.ArrayClass()
248
249
250
251
        ac_view_1 = ac.numpy_view()
        ac_view_2 = ac.numpy_view()
        assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32))
        del ac
Wenzel Jakob's avatar
Wenzel Jakob committed
252
        pytest.gc_collect()
253
254
255
    assert (
        capture
        == """
256
257
258
259
        ArrayClass()
        ArrayClass::numpy_view()
        ArrayClass::numpy_view()
    """
260
    )
261
262
263
264
265
266
267
    ac_view_1[0] = 4
    ac_view_1[1] = 3
    assert ac_view_2[0] == 4
    assert ac_view_2[1] == 3
    with capture:
        del ac_view_1
        del ac_view_2
Wenzel Jakob's avatar
Wenzel Jakob committed
268
269
        pytest.gc_collect()
        pytest.gc_collect()
270
271
272
    assert (
        capture
        == """
273
274
        ~ArrayClass()
    """
275
    )
276
277
278


def test_cast_numpy_int64_to_uint64():
279
280
    m.function_taking_uint64(123)
    m.function_taking_uint64(np.uint64(123))
281
282
283


def test_isinstance():
284
285
    assert m.isinstance_untyped(np.array([1, 2, 3]), "not an array")
    assert m.isinstance_typed(np.array([1.0, 2.0, 3.0]))
286
287
288


def test_constructors():
289
    defaults = m.default_constructors()
290
291
292
293
294
295
    for a in defaults.values():
        assert a.size == 0
    assert defaults["array"].dtype == np.array([]).dtype
    assert defaults["array_t<int32>"].dtype == np.int32
    assert defaults["array_t<double>"].dtype == np.float64

296
    results = m.converting_constructors([1, 2, 3])
297
298
299
300
301
    for a in results.values():
        np.testing.assert_array_equal(a, [1, 2, 3])
    assert results["array"].dtype == np.int_
    assert results["array_t<int32>"].dtype == np.int32
    assert results["array_t<double>"].dtype == np.float64
302
303


304
305
def test_overload_resolution(msg):
    # Exact overload matches:
306
307
308
309
310
311
312
    assert m.overloaded(np.array([1], dtype="float64")) == "double"
    assert m.overloaded(np.array([1], dtype="float32")) == "float"
    assert m.overloaded(np.array([1], dtype="ushort")) == "unsigned short"
    assert m.overloaded(np.array([1], dtype="intc")) == "int"
    assert m.overloaded(np.array([1], dtype="longlong")) == "long long"
    assert m.overloaded(np.array([1], dtype="complex")) == "double complex"
    assert m.overloaded(np.array([1], dtype="csingle")) == "float complex"
313
314

    # No exact match, should call first convertible version:
315
    assert m.overloaded(np.array([1], dtype="uint8")) == "double"
316

317
    with pytest.raises(TypeError) as excinfo:
318
        m.overloaded("not an array")
319
320
321
    assert (
        msg(excinfo.value)
        == """
322
        overloaded(): incompatible function arguments. The following argument types are supported:
323
324
325
326
327
328
329
            1. (arg0: numpy.ndarray[numpy.float64]) -> str
            2. (arg0: numpy.ndarray[numpy.float32]) -> str
            3. (arg0: numpy.ndarray[numpy.int32]) -> str
            4. (arg0: numpy.ndarray[numpy.uint16]) -> str
            5. (arg0: numpy.ndarray[numpy.int64]) -> str
            6. (arg0: numpy.ndarray[numpy.complex128]) -> str
            7. (arg0: numpy.ndarray[numpy.complex64]) -> str
330
331
332

        Invoked with: 'not an array'
    """
333
    )
334

335
336
337
338
339
    assert m.overloaded2(np.array([1], dtype="float64")) == "double"
    assert m.overloaded2(np.array([1], dtype="float32")) == "float"
    assert m.overloaded2(np.array([1], dtype="complex64")) == "float complex"
    assert m.overloaded2(np.array([1], dtype="complex128")) == "double complex"
    assert m.overloaded2(np.array([1], dtype="float32")) == "float"
340

341
342
    assert m.overloaded3(np.array([1], dtype="float64")) == "double"
    assert m.overloaded3(np.array([1], dtype="intc")) == "int"
343
344
    expected_exc = """
        overloaded3(): incompatible function arguments. The following argument types are supported:
345
346
            1. (arg0: numpy.ndarray[numpy.int32]) -> str
            2. (arg0: numpy.ndarray[numpy.float64]) -> str
347

348
        Invoked with: """
349
350

    with pytest.raises(TypeError) as excinfo:
351
352
        m.overloaded3(np.array([1], dtype="uintc"))
    assert msg(excinfo.value) == expected_exc + repr(np.array([1], dtype="uint32"))
353
    with pytest.raises(TypeError) as excinfo:
354
355
        m.overloaded3(np.array([1], dtype="float32"))
    assert msg(excinfo.value) == expected_exc + repr(np.array([1.0], dtype="float32"))
356
    with pytest.raises(TypeError) as excinfo:
357
358
        m.overloaded3(np.array([1], dtype="complex"))
    assert msg(excinfo.value) == expected_exc + repr(np.array([1.0 + 0.0j]))
359
360

    # Exact matches:
361
362
    assert m.overloaded4(np.array([1], dtype="double")) == "double"
    assert m.overloaded4(np.array([1], dtype="longlong")) == "long long"
363
364
365
    # Non-exact matches requiring conversion.  Since float to integer isn't a
    # save conversion, it should go to the double overload, but short can go to
    # either (and so should end up on the first-registered, the long long).
366
367
    assert m.overloaded4(np.array([1], dtype="float32")) == "double"
    assert m.overloaded4(np.array([1], dtype="short")) == "long long"
368

369
370
371
    assert m.overloaded5(np.array([1], dtype="double")) == "double"
    assert m.overloaded5(np.array([1], dtype="uintc")) == "unsigned int"
    assert m.overloaded5(np.array([1], dtype="float32")) == "unsigned int"
372
373


374
375
def test_greedy_string_overload():
    """Tests fix for #685 - ndarray shouldn't go to std::string overload"""
376

377
    assert m.issue685("abc") == "string"
378
    assert m.issue685(np.array([97, 98, 99], dtype="b")) == "array"
379
    assert m.issue685(123) == "other"
380
381


382
def test_array_unchecked_fixed_dims(msg):
383
    z1 = np.array([[1, 2], [3, 4]], dtype="float64")
384
    m.proxy_add2(z1, 10)
385
386
387
    assert np.all(z1 == [[11, 12], [13, 14]])

    with pytest.raises(ValueError) as excinfo:
388
389
390
391
        m.proxy_add2(np.array([1.0, 2, 3]), 5.0)
    assert (
        msg(excinfo.value) == "array has incorrect number of dimensions: 1; expected 2"
    )
392

393
    expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype="int")
394
    assert np.all(m.proxy_init3(3.0) == expect_c)
395
    expect_f = np.transpose(expect_c)
396
    assert np.all(m.proxy_init3F(3.0) == expect_f)
397

398
399
    assert m.proxy_squared_L2_norm(np.array(range(6))) == 55
    assert m.proxy_squared_L2_norm(np.array(range(6), dtype="float64")) == 55
400

401
402
    assert m.proxy_auxiliaries2(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32]
    assert m.proxy_auxiliaries2(z1) == m.array_auxiliaries2(z1)
403

404
405
406
    assert m.proxy_auxiliaries1_const_ref(z1[0, :])
    assert m.proxy_auxiliaries2_const_ref(z1)

407

Nick Cullen's avatar
Nick Cullen committed
408
def test_array_unchecked_dyn_dims():
409
    z1 = np.array([[1, 2], [3, 4]], dtype="float64")
410
    m.proxy_add2_dyn(z1, 10)
411
412
    assert np.all(z1 == [[11, 12], [13, 14]])

413
    expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype="int")
414
    assert np.all(m.proxy_init3_dyn(3.0) == expect_c)
415

416
417
    assert m.proxy_auxiliaries2_dyn(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32]
    assert m.proxy_auxiliaries2_dyn(z1) == m.array_auxiliaries2(z1)
418
419
420
421


def test_array_failure():
    with pytest.raises(ValueError) as excinfo:
422
        m.array_fail_test()
423
    assert str(excinfo.value) == "cannot create a pybind11::array from a nullptr"
424
425

    with pytest.raises(ValueError) as excinfo:
426
        m.array_t_fail_test()
427
    assert str(excinfo.value) == "cannot create a pybind11::array_t from a nullptr"
uentity's avatar
uentity committed
428

429
    with pytest.raises(ValueError) as excinfo:
430
        m.array_fail_test_negative_size()
431
    assert str(excinfo.value) == "negative dimensions are not allowed"
432

uentity's avatar
uentity committed
433

434
435
436
437
438
439
def test_initializer_list():
    assert m.array_initializer_list1().shape == (1,)
    assert m.array_initializer_list2().shape == (1, 2)
    assert m.array_initializer_list3().shape == (1, 2, 3)
    assert m.array_initializer_list4().shape == (1, 2, 3, 4)

uentity's avatar
uentity committed
440

Nick Cullen's avatar
Nick Cullen committed
441
def test_array_resize():
442
    a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype="float64")
443
    m.array_reshape2(a)
444
445
    assert a.size == 9
    assert np.all(a == [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
uentity's avatar
uentity committed
446
447

    # total size change should succced with refcheck off
448
    m.array_resize3(a, 4, False)
449
    assert a.size == 64
uentity's avatar
uentity committed
450
451
    # ... and fail with refcheck on
    try:
452
        m.array_resize3(a, 3, True)
uentity's avatar
uentity committed
453
    except ValueError as e:
454
        assert str(e).startswith("cannot resize an array")
uentity's avatar
uentity committed
455
456
457
    # transposed array doesn't own data
    b = a.transpose()
    try:
458
        m.array_resize3(b, 3, False)
uentity's avatar
uentity committed
459
    except ValueError as e:
460
        assert str(e).startswith("cannot resize this array: it does not own its data")
uentity's avatar
uentity committed
461
    # ... but reshape should be fine
462
    m.array_reshape2(b)
463
    assert b.shape == (8, 8)
uentity's avatar
uentity committed
464
465


466
@pytest.mark.xfail("env.PYPY")
Nick Cullen's avatar
Nick Cullen committed
467
def test_array_create_and_resize():
468
    a = m.create_and_resize(2)
469
470
    assert a.size == 4
    assert np.all(a == 42.0)
471
472


Nick Cullen's avatar
Nick Cullen committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
def test_array_view():
    a = np.ones(100 * 4).astype("uint8")
    a_float_view = m.array_view(a, "float32")
    assert a_float_view.shape == (100 * 1,)  # 1 / 4 bytes = 8 / 32

    a_int16_view = m.array_view(a, "int16")  # 1 / 2 bytes = 16 / 32
    assert a_int16_view.shape == (100 * 2,)


def test_array_view_invalid():
    a = np.ones(100 * 4).astype("uint8")
    with pytest.raises(TypeError):
        m.array_view(a, "deadly_dtype")


Nick Cullen's avatar
Nick Cullen committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
def test_reshape_initializer_list():
    a = np.arange(2 * 7 * 3) + 1
    x = m.reshape_initializer_list(a, 2, 7, 3)
    assert x.shape == (2, 7, 3)
    assert list(x[1][4]) == [34, 35, 36]
    with pytest.raises(ValueError) as excinfo:
        m.reshape_initializer_list(a, 1, 7, 3)
    assert str(excinfo.value) == "cannot reshape array of size 42 into shape (1,7,3)"


def test_reshape_tuple():
    a = np.arange(3 * 7 * 2) + 1
    x = m.reshape_tuple(a, (3, 7, 2))
    assert x.shape == (3, 7, 2)
    assert list(x[1][4]) == [23, 24]
    y = m.reshape_tuple(x, (x.size,))
    assert y.shape == (42,)
    with pytest.raises(ValueError) as excinfo:
        m.reshape_tuple(a, (3, 7, 1))
    assert str(excinfo.value) == "cannot reshape array of size 42 into shape (3,7,1)"
    with pytest.raises(ValueError) as excinfo:
        m.reshape_tuple(a, ())
    assert str(excinfo.value) == "cannot reshape array of size 42 into shape ()"


513
514
515
def test_index_using_ellipsis():
    a = m.index_using_ellipsis(np.zeros((5, 6, 7)))
    assert a.shape == (6,)
516
517


518
519
520
521
522
523
524
525
526
527
528
529
530
@pytest.mark.parametrize(
    "test_func",
    [
        m.test_fmt_desc_float,
        m.test_fmt_desc_double,
        m.test_fmt_desc_const_float,
        m.test_fmt_desc_const_double,
    ],
)
def test_format_descriptors_for_floating_point_types(test_func):
    assert "numpy.ndarray[numpy.float" in test_func.__doc__


531
@pytest.mark.parametrize("forcecast", [False, True])
532
@pytest.mark.parametrize("contiguity", [None, "C", "F"])
533
534
535
536
537
538
@pytest.mark.parametrize("noconvert", [False, True])
@pytest.mark.filterwarnings(
    "ignore:Casting complex values to real discards the imaginary part:numpy.ComplexWarning"
)
def test_argument_conversions(forcecast, contiguity, noconvert):
    function_name = "accept_double"
539
    if contiguity == "C":
540
        function_name += "_c_style"
541
    elif contiguity == "F":
542
543
544
545
546
547
548
        function_name += "_f_style"
    if forcecast:
        function_name += "_forcecast"
    if noconvert:
        function_name += "_noconvert"
    function = getattr(m, function_name)

549
550
    for dtype in [np.dtype("float32"), np.dtype("float64"), np.dtype("complex128")]:
        for order in ["C", "F"]:
551
552
553
554
555
            for shape in [(2, 2), (1, 3, 1, 1), (1, 1, 1), (0,)]:
                if not noconvert:
                    # If noconvert is not passed, only complex128 needs to be truncated and
                    # "cannot be safely obtained". So without `forcecast`, the argument shouldn't
                    # be accepted.
556
                    should_raise = dtype.name == "complex128" and not forcecast
557
558
559
560
561
                else:
                    # If noconvert is passed, only float64 and the matching order is accepted.
                    # If at most one dimension has a size greater than 1, the array is also
                    # trivially contiguous.
                    trivially_contiguous = sum(1 for d in shape if d > 1) <= 1
562
563
564
565
                    should_raise = dtype.name != "float64" or (
                        contiguity is not None
                        and contiguity != order
                        and not trivially_contiguous
566
567
568
569
570
571
                    )

                array = np.zeros(shape, dtype=dtype, order=order)
                if not should_raise:
                    function(array)
                else:
572
573
574
                    with pytest.raises(
                        TypeError, match="incompatible function arguments"
                    ):
575
576
577
                        function(array)


578
@pytest.mark.xfail("env.PYPY")
579
580
def test_dtype_refcount_leak():
    from sys import getrefcount
581

582
583
584
585
586
587
    dtype = np.dtype(np.float_)
    a = np.array([1], dtype=dtype)
    before = getrefcount(dtype)
    m.ndim(a)
    after = getrefcount(dtype)
    assert after == before
588
589
590
591
592
593


def test_round_trip_float():
    arr = np.zeros((), np.float64)
    arr[()] = 37.2
    assert m.round_trip_float(arr) == 37.2