Passed
Pull Request — master (#15)
by Ramon
03:38
created

nptyping._ndarray_meta._NDArrayMeta.shape()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 3
dl 7
loc 7
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
from collections import OrderedDict
2
from typing import Any, Tuple, Union
3
4
import numpy as np
5
from typish import SubscriptableType, Literal, ClsFunction, EllipsisType
6
7
_Size = Union[int, Literal[Any]]  # TODO add type vars as well
8
_Type = Union[type, Literal[Any], np.dtype]
9
_NSizes = Tuple[_Size, EllipsisType]
10
_SizeAndType = Tuple[_Size, _Type]
11
_Sizes = Tuple[_Size, ...]
12
_SizesAndType = Tuple[Tuple[_Size, ...], _Type]
13
_NSizesAndType = Tuple[_NSizes, _Type]
14
_Default = Tuple[Tuple[Literal[Any], EllipsisType], Literal[Any]]
15
16
17 View Code Duplication
class _NDArrayMeta(SubscriptableType):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
18
    _shape = tuple()  # Overridden by _NDArray._shape.
19
    _type = ...  # Overridden by _NDArray._type.
20
21
    @property
22
    def dtype(cls) -> np.dtype:
23
        """
24
        Return the numpy dtype.
25
        :return: the numpy dtype.
26
        """
27
        return np.dtype(cls._type)  # TODO if type is Any, this wont work
28
29
    @property
30
    def shape(cls) -> Tuple[int, int]:
31
        """
32
        Return the shape as a tuple of ints.
33
        :return: the shape as a tuple of ints.
34
        """
35
        return cls._shape
36
37
    def __repr__(cls):
38
        shape_ = cls._shape
39
        if len(cls._shape) == 2 and cls._shape[1] is ...:
40
            shape_ = (cls._shape[0], '...')
41
42
        type_ = getattr(cls._type, '__name__', cls._type)
43
        return 'NDArray[{}, {}]'.format(shape_, type_).replace('\'', '')
44
45
    def __str__(cls):
46
        return repr(cls)
47
48
    def __eq__(cls, other) -> bool:
49
        return (isinstance(other, _NDArrayMeta)
50
                and cls._shape == other._shape
51
                and cls._type == other._type)
52
53
    def __instancecheck__(cls, instance: np.ndarray) -> bool:
54
        """
55
        Checks whether the given instance conforms the current NDArray type by
56
        checking the shape and the dtype.
57
        :param instance: a numpy.ndarray.
58
        :return: True if instance is an instance of cls.
59
        """
60
        return (isinstance(instance, np.ndarray)
61
                and _NDArrayMeta._is_shape_eq(cls, instance)
62
                and _NDArrayMeta._is_type_eq(cls, instance))
63
64
    def _is_shape_eq(cls, instance: np.ndarray) -> bool:
65
66
        def _is_eq_to(this: Any, that: Any) -> bool:
67
            return that is Any or this == that
68
69
        if cls._shape == (Any, ...):
70
            return True
71
        if len(cls._shape) == 2 and cls._shape[1] is ...:
72
            size = cls._shape[0]
73
            return all([s == size for s in instance.shape])
74
        if len(instance.shape) != len(cls._shape):
75
            return False
76
        zipped = zip(instance.shape, cls._shape)
77
        return all([_is_eq_to(a, b) for a, b in zipped])
78
79
    def _is_type_eq(cls, instance: np.ndarray) -> bool:
80
        if cls._type is Any:
81
            return True
82
        return cls.dtype == instance.dtype
83
84
85 View Code Duplication
class _NDArray(metaclass=_NDArrayMeta):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
86
    _shape = (Any, ...)
87
    _type = Any
88
89
    @classmethod
90
    def _after_subscription(cls, item: Any) -> None:
91
        method = ClsFunction(OrderedDict([
92
            (_Size, cls._only_size),
93
            (_Type, cls._only_type),
94
            (_NSizes, lambda _: ...),
95
            (_SizeAndType, cls._size_and_type),
96
            (_Sizes, cls._only_sizes),
97
            (_SizesAndType, cls._sizes_and_type),
98
            (_NSizesAndType, cls._sizes_and_type),
99
            (_Default, lambda _: ...),
100
        ]))
101
102
        if not method.understands(item):
103
            raise TypeError('Invalid parameter for NDArray: "{}"'.format(item))
104
        return method(item)
105
106
    @classmethod
107
    def _only_size(cls, item: int):
108
        # E.g. NDArray[3]
109
        # The given item is the size of the single dimension.
110
        cls._shape = (item,)
111
112
    @classmethod
113
    def _only_type(cls, item: type):
114
        # E.g. NDArray[int]
115
        # The given item is the type of the single dimension.
116
        cls._type = item
117
118
    @classmethod
119
    def _size_and_type(cls, item: Tuple[_Size, _Type]):
120
        # E.g. NDArray[3, int]
121
        # The given item is the size of the single dimension and its type.
122
        cls._shape = (item[0],)
123
        cls._type = item[1]
124
125
    @classmethod
126
    def _only_sizes(cls, item: Tuple[_Size, ...]):
127
        # E.g. NDArray[(2, Any, 2)]
128
        # The given item is a tuple with just sizes of the dimensions.
129
        cls._shape = item
130
131
    @classmethod
132
    def _sizes_and_type(cls, item: Tuple[Tuple[_Size, ...], _Type]):
133
        # E.g. NDArray[(2, Any, 2), int]
134
        # The given item is a tuple with sizes of the dimensions and the type.
135
        # Or e.g. NDArray[(3, ...), int]
136
        # The given item is a tuple with sizes of n dimensions and the type.
137
        cls._only_sizes(item[0])
138
        cls._only_type(item[1])
139