1import sys
2import itertools
3
4import pytest
5import numpy as np
6from numpy.testing import assert_, assert_equal, assert_raises, IS_PYPY
7
8# This is the structure of the table used for plain objects:
9#
10# +-+-+-+
11# |x|y|z|
12# +-+-+-+
13
14# Structure of a plain array description:
15Pdescr = [
16    ('x', 'i4', (2,)),
17    ('y', 'f8', (2, 2)),
18    ('z', 'u1')]
19
20# A plain list of tuples with values for testing:
21PbufferT = [
22    # x     y                  z
23    ([3, 2], [[6., 4.], [6., 4.]], 8),
24    ([4, 3], [[7., 5.], [7., 5.]], 9),
25    ]
26
27
28# This is the structure of the table used for nested objects (DON'T PANIC!):
29#
30# +-+---------------------------------+-----+----------+-+-+
31# |x|Info                             |color|info      |y|z|
32# | +-----+--+----------------+----+--+     +----+-----+ | |
33# | |value|y2|Info2           |name|z2|     |Name|Value| | |
34# | |     |  +----+-----+--+--+    |  |     |    |     | | |
35# | |     |  |name|value|y3|z3|    |  |     |    |     | | |
36# +-+-----+--+----+-----+--+--+----+--+-----+----+-----+-+-+
37#
38
39# The corresponding nested array description:
40Ndescr = [
41    ('x', 'i4', (2,)),
42    ('Info', [
43        ('value', 'c16'),
44        ('y2', 'f8'),
45        ('Info2', [
46            ('name', 'S2'),
47            ('value', 'c16', (2,)),
48            ('y3', 'f8', (2,)),
49            ('z3', 'u4', (2,))]),
50        ('name', 'S2'),
51        ('z2', 'b1')]),
52    ('color', 'S2'),
53    ('info', [
54        ('Name', 'U8'),
55        ('Value', 'c16')]),
56    ('y', 'f8', (2, 2)),
57    ('z', 'u1')]
58
59NbufferT = [
60    # x     Info                                                color info        y                  z
61    #       value y2 Info2                            name z2         Name Value
62    #                name   value    y3       z3
63    ([3, 2], (6j, 6., (b'nn', [6j, 4j], [6., 4.], [1, 2]), b'NN', True), b'cc', (u'NN', 6j), [[6., 4.], [6., 4.]], 8),
64    ([4, 3], (7j, 7., (b'oo', [7j, 5j], [7., 5.], [2, 1]), b'OO', False), b'dd', (u'OO', 7j), [[7., 5.], [7., 5.]], 9),
65    ]
66
67
68byteorder = {'little':'<', 'big':'>'}[sys.byteorder]
69
70def normalize_descr(descr):
71    "Normalize a description adding the platform byteorder."
72
73    out = []
74    for item in descr:
75        dtype = item[1]
76        if isinstance(dtype, str):
77            if dtype[0] not in ['|', '<', '>']:
78                onebyte = dtype[1:] == "1"
79                if onebyte or dtype[0] in ['S', 'V', 'b']:
80                    dtype = "|" + dtype
81                else:
82                    dtype = byteorder + dtype
83            if len(item) > 2 and np.prod(item[2]) > 1:
84                nitem = (item[0], dtype, item[2])
85            else:
86                nitem = (item[0], dtype)
87            out.append(nitem)
88        elif isinstance(dtype, list):
89            l = normalize_descr(dtype)
90            out.append((item[0], l))
91        else:
92            raise ValueError("Expected a str or list and got %s" %
93                             (type(item)))
94    return out
95
96
97############################################################
98#    Creation tests
99############################################################
100
101class CreateZeros:
102    """Check the creation of heterogeneous arrays zero-valued"""
103
104    def test_zeros0D(self):
105        """Check creation of 0-dimensional objects"""
106        h = np.zeros((), dtype=self._descr)
107        assert_(normalize_descr(self._descr) == h.dtype.descr)
108        assert_(h.dtype.fields['x'][0].name[:4] == 'void')
109        assert_(h.dtype.fields['x'][0].char == 'V')
110        assert_(h.dtype.fields['x'][0].type == np.void)
111        # A small check that data is ok
112        assert_equal(h['z'], np.zeros((), dtype='u1'))
113
114    def test_zerosSD(self):
115        """Check creation of single-dimensional objects"""
116        h = np.zeros((2,), dtype=self._descr)
117        assert_(normalize_descr(self._descr) == h.dtype.descr)
118        assert_(h.dtype['y'].name[:4] == 'void')
119        assert_(h.dtype['y'].char == 'V')
120        assert_(h.dtype['y'].type == np.void)
121        # A small check that data is ok
122        assert_equal(h['z'], np.zeros((2,), dtype='u1'))
123
124    def test_zerosMD(self):
125        """Check creation of multi-dimensional objects"""
126        h = np.zeros((2, 3), dtype=self._descr)
127        assert_(normalize_descr(self._descr) == h.dtype.descr)
128        assert_(h.dtype['z'].name == 'uint8')
129        assert_(h.dtype['z'].char == 'B')
130        assert_(h.dtype['z'].type == np.uint8)
131        # A small check that data is ok
132        assert_equal(h['z'], np.zeros((2, 3), dtype='u1'))
133
134
135class TestCreateZerosPlain(CreateZeros):
136    """Check the creation of heterogeneous arrays zero-valued (plain)"""
137    _descr = Pdescr
138
139class TestCreateZerosNested(CreateZeros):
140    """Check the creation of heterogeneous arrays zero-valued (nested)"""
141    _descr = Ndescr
142
143
144class CreateValues:
145    """Check the creation of heterogeneous arrays with values"""
146
147    def test_tuple(self):
148        """Check creation from tuples"""
149        h = np.array(self._buffer, dtype=self._descr)
150        assert_(normalize_descr(self._descr) == h.dtype.descr)
151        if self.multiple_rows:
152            assert_(h.shape == (2,))
153        else:
154            assert_(h.shape == ())
155
156    def test_list_of_tuple(self):
157        """Check creation from list of tuples"""
158        h = np.array([self._buffer], dtype=self._descr)
159        assert_(normalize_descr(self._descr) == h.dtype.descr)
160        if self.multiple_rows:
161            assert_(h.shape == (1, 2))
162        else:
163            assert_(h.shape == (1,))
164
165    def test_list_of_list_of_tuple(self):
166        """Check creation from list of list of tuples"""
167        h = np.array([[self._buffer]], dtype=self._descr)
168        assert_(normalize_descr(self._descr) == h.dtype.descr)
169        if self.multiple_rows:
170            assert_(h.shape == (1, 1, 2))
171        else:
172            assert_(h.shape == (1, 1))
173
174
175class TestCreateValuesPlainSingle(CreateValues):
176    """Check the creation of heterogeneous arrays (plain, single row)"""
177    _descr = Pdescr
178    multiple_rows = 0
179    _buffer = PbufferT[0]
180
181class TestCreateValuesPlainMultiple(CreateValues):
182    """Check the creation of heterogeneous arrays (plain, multiple rows)"""
183    _descr = Pdescr
184    multiple_rows = 1
185    _buffer = PbufferT
186
187class TestCreateValuesNestedSingle(CreateValues):
188    """Check the creation of heterogeneous arrays (nested, single row)"""
189    _descr = Ndescr
190    multiple_rows = 0
191    _buffer = NbufferT[0]
192
193class TestCreateValuesNestedMultiple(CreateValues):
194    """Check the creation of heterogeneous arrays (nested, multiple rows)"""
195    _descr = Ndescr
196    multiple_rows = 1
197    _buffer = NbufferT
198
199
200############################################################
201#    Reading tests
202############################################################
203
204class ReadValuesPlain:
205    """Check the reading of values in heterogeneous arrays (plain)"""
206
207    def test_access_fields(self):
208        h = np.array(self._buffer, dtype=self._descr)
209        if not self.multiple_rows:
210            assert_(h.shape == ())
211            assert_equal(h['x'], np.array(self._buffer[0], dtype='i4'))
212            assert_equal(h['y'], np.array(self._buffer[1], dtype='f8'))
213            assert_equal(h['z'], np.array(self._buffer[2], dtype='u1'))
214        else:
215            assert_(len(h) == 2)
216            assert_equal(h['x'], np.array([self._buffer[0][0],
217                                             self._buffer[1][0]], dtype='i4'))
218            assert_equal(h['y'], np.array([self._buffer[0][1],
219                                             self._buffer[1][1]], dtype='f8'))
220            assert_equal(h['z'], np.array([self._buffer[0][2],
221                                             self._buffer[1][2]], dtype='u1'))
222
223
224class TestReadValuesPlainSingle(ReadValuesPlain):
225    """Check the creation of heterogeneous arrays (plain, single row)"""
226    _descr = Pdescr
227    multiple_rows = 0
228    _buffer = PbufferT[0]
229
230class TestReadValuesPlainMultiple(ReadValuesPlain):
231    """Check the values of heterogeneous arrays (plain, multiple rows)"""
232    _descr = Pdescr
233    multiple_rows = 1
234    _buffer = PbufferT
235
236class ReadValuesNested:
237    """Check the reading of values in heterogeneous arrays (nested)"""
238
239    def test_access_top_fields(self):
240        """Check reading the top fields of a nested array"""
241        h = np.array(self._buffer, dtype=self._descr)
242        if not self.multiple_rows:
243            assert_(h.shape == ())
244            assert_equal(h['x'], np.array(self._buffer[0], dtype='i4'))
245            assert_equal(h['y'], np.array(self._buffer[4], dtype='f8'))
246            assert_equal(h['z'], np.array(self._buffer[5], dtype='u1'))
247        else:
248            assert_(len(h) == 2)
249            assert_equal(h['x'], np.array([self._buffer[0][0],
250                                           self._buffer[1][0]], dtype='i4'))
251            assert_equal(h['y'], np.array([self._buffer[0][4],
252                                           self._buffer[1][4]], dtype='f8'))
253            assert_equal(h['z'], np.array([self._buffer[0][5],
254                                           self._buffer[1][5]], dtype='u1'))
255
256    def test_nested1_acessors(self):
257        """Check reading the nested fields of a nested array (1st level)"""
258        h = np.array(self._buffer, dtype=self._descr)
259        if not self.multiple_rows:
260            assert_equal(h['Info']['value'],
261                         np.array(self._buffer[1][0], dtype='c16'))
262            assert_equal(h['Info']['y2'],
263                         np.array(self._buffer[1][1], dtype='f8'))
264            assert_equal(h['info']['Name'],
265                         np.array(self._buffer[3][0], dtype='U2'))
266            assert_equal(h['info']['Value'],
267                         np.array(self._buffer[3][1], dtype='c16'))
268        else:
269            assert_equal(h['Info']['value'],
270                         np.array([self._buffer[0][1][0],
271                                self._buffer[1][1][0]],
272                                dtype='c16'))
273            assert_equal(h['Info']['y2'],
274                         np.array([self._buffer[0][1][1],
275                                self._buffer[1][1][1]],
276                                dtype='f8'))
277            assert_equal(h['info']['Name'],
278                         np.array([self._buffer[0][3][0],
279                                self._buffer[1][3][0]],
280                               dtype='U2'))
281            assert_equal(h['info']['Value'],
282                         np.array([self._buffer[0][3][1],
283                                self._buffer[1][3][1]],
284                               dtype='c16'))
285
286    def test_nested2_acessors(self):
287        """Check reading the nested fields of a nested array (2nd level)"""
288        h = np.array(self._buffer, dtype=self._descr)
289        if not self.multiple_rows:
290            assert_equal(h['Info']['Info2']['value'],
291                         np.array(self._buffer[1][2][1], dtype='c16'))
292            assert_equal(h['Info']['Info2']['z3'],
293                         np.array(self._buffer[1][2][3], dtype='u4'))
294        else:
295            assert_equal(h['Info']['Info2']['value'],
296                         np.array([self._buffer[0][1][2][1],
297                                self._buffer[1][1][2][1]],
298                               dtype='c16'))
299            assert_equal(h['Info']['Info2']['z3'],
300                         np.array([self._buffer[0][1][2][3],
301                                self._buffer[1][1][2][3]],
302                               dtype='u4'))
303
304    def test_nested1_descriptor(self):
305        """Check access nested descriptors of a nested array (1st level)"""
306        h = np.array(self._buffer, dtype=self._descr)
307        assert_(h.dtype['Info']['value'].name == 'complex128')
308        assert_(h.dtype['Info']['y2'].name == 'float64')
309        assert_(h.dtype['info']['Name'].name == 'str256')
310        assert_(h.dtype['info']['Value'].name == 'complex128')
311
312    def test_nested2_descriptor(self):
313        """Check access nested descriptors of a nested array (2nd level)"""
314        h = np.array(self._buffer, dtype=self._descr)
315        assert_(h.dtype['Info']['Info2']['value'].name == 'void256')
316        assert_(h.dtype['Info']['Info2']['z3'].name == 'void64')
317
318
319class TestReadValuesNestedSingle(ReadValuesNested):
320    """Check the values of heterogeneous arrays (nested, single row)"""
321    _descr = Ndescr
322    multiple_rows = False
323    _buffer = NbufferT[0]
324
325class TestReadValuesNestedMultiple(ReadValuesNested):
326    """Check the values of heterogeneous arrays (nested, multiple rows)"""
327    _descr = Ndescr
328    multiple_rows = True
329    _buffer = NbufferT
330
331class TestEmptyField:
332    def test_assign(self):
333        a = np.arange(10, dtype=np.float32)
334        a.dtype = [("int",   "<0i4"), ("float", "<2f4")]
335        assert_(a['int'].shape == (5, 0))
336        assert_(a['float'].shape == (5, 2))
337
338class TestCommonType:
339    def test_scalar_loses1(self):
340        res = np.find_common_type(['f4', 'f4', 'i2'], ['f8'])
341        assert_(res == 'f4')
342
343    def test_scalar_loses2(self):
344        res = np.find_common_type(['f4', 'f4'], ['i8'])
345        assert_(res == 'f4')
346
347    def test_scalar_wins(self):
348        res = np.find_common_type(['f4', 'f4', 'i2'], ['c8'])
349        assert_(res == 'c8')
350
351    def test_scalar_wins2(self):
352        res = np.find_common_type(['u4', 'i4', 'i4'], ['f4'])
353        assert_(res == 'f8')
354
355    def test_scalar_wins3(self):  # doesn't go up to 'f16' on purpose
356        res = np.find_common_type(['u8', 'i8', 'i8'], ['f8'])
357        assert_(res == 'f8')
358
359class TestMultipleFields:
360    def setup(self):
361        self.ary = np.array([(1, 2, 3, 4), (5, 6, 7, 8)], dtype='i4,f4,i2,c8')
362
363    def _bad_call(self):
364        return self.ary['f0', 'f1']
365
366    def test_no_tuple(self):
367        assert_raises(IndexError, self._bad_call)
368
369    def test_return(self):
370        res = self.ary[['f0', 'f2']].tolist()
371        assert_(res == [(1, 3), (5, 7)])
372
373
374class TestIsSubDType:
375    # scalar types can be promoted into dtypes
376    wrappers = [np.dtype, lambda x: x]
377
378    def test_both_abstract(self):
379        assert_(np.issubdtype(np.floating, np.inexact))
380        assert_(not np.issubdtype(np.inexact, np.floating))
381
382    def test_same(self):
383        for cls in (np.float32, np.int32):
384            for w1, w2 in itertools.product(self.wrappers, repeat=2):
385                assert_(np.issubdtype(w1(cls), w2(cls)))
386
387    def test_subclass(self):
388        # note we cannot promote floating to a dtype, as it would turn into a
389        # concrete type
390        for w in self.wrappers:
391            assert_(np.issubdtype(w(np.float32), np.floating))
392            assert_(np.issubdtype(w(np.float64), np.floating))
393
394    def test_subclass_backwards(self):
395        for w in self.wrappers:
396            assert_(not np.issubdtype(np.floating, w(np.float32)))
397            assert_(not np.issubdtype(np.floating, w(np.float64)))
398
399    def test_sibling_class(self):
400        for w1, w2 in itertools.product(self.wrappers, repeat=2):
401            assert_(not np.issubdtype(w1(np.float32), w2(np.float64)))
402            assert_(not np.issubdtype(w1(np.float64), w2(np.float32)))
403
404    def test_nondtype_nonscalartype(self):
405        # See gh-14619 and gh-9505 which introduced the deprecation to fix
406        # this. These tests are directly taken from gh-9505
407        assert not np.issubdtype(np.float32, 'float64')
408        assert not np.issubdtype(np.float32, 'f8')
409        assert not np.issubdtype(np.int32, str)
410        assert not np.issubdtype(np.int32, 'int64')
411        assert not np.issubdtype(np.str_, 'void')
412        # for the following the correct spellings are
413        # np.integer, np.floating, or np.complexfloating respectively:
414        assert not np.issubdtype(np.int8, int)  # np.int8 is never np.int_
415        assert not np.issubdtype(np.float32, float)
416        assert not np.issubdtype(np.complex64, complex)
417        assert not np.issubdtype(np.float32, "float")
418        assert not np.issubdtype(np.float64, "f")
419
420        # Test the same for the correct first datatype and abstract one
421        # in the case of int, float, complex:
422        assert np.issubdtype(np.float64, 'float64')
423        assert np.issubdtype(np.float64, 'f8')
424        assert np.issubdtype(np.str_, str)
425        assert np.issubdtype(np.int64, 'int64')
426        assert np.issubdtype(np.void, 'void')
427        assert np.issubdtype(np.int8, np.integer)
428        assert np.issubdtype(np.float32, np.floating)
429        assert np.issubdtype(np.complex64, np.complexfloating)
430        assert np.issubdtype(np.float64, "float")
431        assert np.issubdtype(np.float32, "f")
432
433
434class TestSctypeDict:
435    def test_longdouble(self):
436        assert_(np.sctypeDict['f8'] is not np.longdouble)
437        assert_(np.sctypeDict['c16'] is not np.clongdouble)
438
439
440class TestBitName:
441    def test_abstract(self):
442        assert_raises(ValueError, np.core.numerictypes.bitname, np.floating)
443
444
445class TestMaximumSctype:
446
447    # note that parametrizing with sctype['int'] and similar would skip types
448    # with the same size (gh-11923)
449
450    @pytest.mark.parametrize('t', [np.byte, np.short, np.intc, np.int_, np.longlong])
451    def test_int(self, t):
452        assert_equal(np.maximum_sctype(t), np.sctypes['int'][-1])
453
454    @pytest.mark.parametrize('t', [np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong])
455    def test_uint(self, t):
456        assert_equal(np.maximum_sctype(t), np.sctypes['uint'][-1])
457
458    @pytest.mark.parametrize('t', [np.half, np.single, np.double, np.longdouble])
459    def test_float(self, t):
460        assert_equal(np.maximum_sctype(t), np.sctypes['float'][-1])
461
462    @pytest.mark.parametrize('t', [np.csingle, np.cdouble, np.clongdouble])
463    def test_complex(self, t):
464        assert_equal(np.maximum_sctype(t), np.sctypes['complex'][-1])
465
466    @pytest.mark.parametrize('t', [np.bool_, np.object_, np.unicode_, np.bytes_, np.void])
467    def test_other(self, t):
468        assert_equal(np.maximum_sctype(t), t)
469
470
471class Test_sctype2char:
472    # This function is old enough that we're really just documenting the quirks
473    # at this point.
474
475    def test_scalar_type(self):
476        assert_equal(np.sctype2char(np.double), 'd')
477        assert_equal(np.sctype2char(np.int_), 'l')
478        assert_equal(np.sctype2char(np.unicode_), 'U')
479        assert_equal(np.sctype2char(np.bytes_), 'S')
480
481    def test_other_type(self):
482        assert_equal(np.sctype2char(float), 'd')
483        assert_equal(np.sctype2char(list), 'O')
484        assert_equal(np.sctype2char(np.ndarray), 'O')
485
486    def test_third_party_scalar_type(self):
487        from numpy.core._rational_tests import rational
488        assert_raises(KeyError, np.sctype2char, rational)
489        assert_raises(KeyError, np.sctype2char, rational(1))
490
491    def test_array_instance(self):
492        assert_equal(np.sctype2char(np.array([1.0, 2.0])), 'd')
493
494    def test_abstract_type(self):
495        assert_raises(KeyError, np.sctype2char, np.floating)
496
497    def test_non_type(self):
498        assert_raises(ValueError, np.sctype2char, 1)
499
500@pytest.mark.parametrize("rep, expected", [
501    (np.int32, True),
502    (list, False),
503    (1.1, False),
504    (str, True),
505    (np.dtype(np.float64), True),
506    (np.dtype((np.int16, (3, 4))), True),
507    (np.dtype([('a', np.int8)]), True),
508    ])
509def test_issctype(rep, expected):
510    # ensure proper identification of scalar
511    # data-types by issctype()
512    actual = np.issctype(rep)
513    assert_equal(actual, expected)
514
515
516@pytest.mark.skipif(sys.flags.optimize > 1,
517                    reason="no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1")
518@pytest.mark.xfail(IS_PYPY,
519                   reason="PyPy cannot modify tp_doc after PyType_Ready")
520class TestDocStrings:
521    def test_platform_dependent_aliases(self):
522        if np.int64 is np.int_:
523            assert_('int64' in np.int_.__doc__)
524        elif np.int64 is np.longlong:
525            assert_('int64' in np.longlong.__doc__)
526
527
528class TestScalarTypeNames:
529    # gh-9799
530
531    numeric_types = [
532        np.byte, np.short, np.intc, np.int_, np.longlong,
533        np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong,
534        np.half, np.single, np.double, np.longdouble,
535        np.csingle, np.cdouble, np.clongdouble,
536    ]
537
538    def test_names_are_unique(self):
539        # none of the above may be aliases for each other
540        assert len(set(self.numeric_types)) == len(self.numeric_types)
541
542        # names must be unique
543        names = [t.__name__ for t in self.numeric_types]
544        assert len(set(names)) == len(names)
545
546    @pytest.mark.parametrize('t', numeric_types)
547    def test_names_reflect_attributes(self, t):
548        """ Test that names correspond to where the type is under ``np.`` """
549        assert getattr(np, t.__name__) is t
550
551    @pytest.mark.parametrize('t', numeric_types)
552    def test_names_are_undersood_by_dtype(self, t):
553        """ Test the dtype constructor maps names back to the type """
554        assert np.dtype(t.__name__).type is t
555