Passed
Pull Request — master (#4)
by Ramon
03:24
created

nptyping._array_meta.meta()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 3
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
"""
2
PRIVATE MODULE: do not import (from) it directly.
3
This module contains meta functionality for the ``Array`` type.
4
"""
5
from functools import lru_cache
6
from typing import Type
7
import numpy as np
8
9
10
class ArrayMeta(type):
11
    _generic_type = None
12
    _rows = None
13
    _cols = None
14
15
    @lru_cache(maxsize=32)
16
    def __getitem__(cls, item: object) -> Type['Array']:
17
        generic_type = item
18
        rows = ...
19
        cols = ...
20
        if isinstance(item, tuple):
21
            if not len(item):
22
                raise TypeError('Parameter Array[...] cannot be empty')
23
24
            generic_type = tuple()
25
            for index, value in enumerate(item):
26
                if isinstance(value, type):
27
                    generic_type += (value,)
28
                else:
29
                    break
30
            else:
31
                index += 1
0 ignored issues
show
introduced by
The variable index does not seem to be defined in case the for loop on line 25 is not entered. Are you sure this can never be the case?
Loading history...
32
33
            if len(generic_type) == 1:
34
                generic_type = generic_type[0]
35
36
            rowcol_types = [int, type(...), type(None)]
37
            if len(item) > index:
38
                if type(item[index]) not in rowcol_types:
39
                    raise TypeError('Unexpected type %s, expecting int or ... or None' % item[index])
40
                rows = item[index] or ...
41
            index += 1
42
            if len(item) > index:
43
                if isinstance(generic_type, tuple):
44
                    raise TypeError('You are not allowed to specify a column count, combined with multiple column '
45
                                    'types.')
46
                if type(item[index]) not in rowcol_types:
47
                    raise TypeError('Unexpected type %s, expecting int or ... or None' % item[index])
48
                cols = item[index] or ...
49
50
        class _Array(metaclass=meta(generic_type, rows, cols)):
51
            def func(self): pass
52
            pass
53
54
        result = type('Array', (_Array,), {})
55
        return result
56
57
    @classmethod
58
    def __instancecheck__(mcs, inst):
59
        result = False
60
        if isinstance(inst, np.ndarray):
61
            result = True  # In case of an empty array or no _generic_type.
62
            rows = 0
63
            cols = 0
64
            if len(inst.shape) > 0:
65
                rows = inst.shape[0]
66
            if len(inst.shape) > 1:
67
                cols = inst.shape[1]
68
69
            if inst.size > 0 and mcs._generic_type:
70
                if isinstance(mcs._generic_type, tuple):
71
                    inst_dtypes = [inst.dtype[name] for name in inst.dtype.names]
72
                    cls_dtypes = [np.dtype(typ) for typ in mcs._generic_type]
73
                    result = inst_dtypes == cls_dtypes
74
                else:
75
                    result = isinstance(inst[0], mcs._generic_type)
76
                    result |= inst.dtype == np.dtype(mcs._generic_type)
77
                result &= mcs._rows is ... or mcs._rows == rows
78
                result &= mcs._cols is ... or mcs._cols == cols
79
        return result
80
81
82
def meta(generic_type: type = None, rows: int = ..., cols: int = ...) -> ArrayMeta:
83
    # Create a meta class with the given arguments.
84
    _metaclass = type('_ArrayMeta', (ArrayMeta,), {})
85
    _metaclass._generic_type = generic_type
86
    _metaclass._rows = rows
87
    _metaclass._cols = cols
88
    return _metaclass
89