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

nptyping._ndarray_meta   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 139
Duplicated Lines 86.33 %

Importance

Changes 0
Metric Value
wmc 24
eloc 89
dl 120
loc 139
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A _NDArray._sizes_and_type() 8 8 1
A _NDArray._only_type() 5 5 1
A _NDArrayMeta.dtype() 7 7 1
A _NDArray._after_subscription() 16 16 4
A _NDArrayMeta.__instancecheck__() 10 10 1
A _NDArrayMeta._is_type_eq() 4 4 2
A _NDArrayMeta._is_shape_eq() 14 14 5
A _NDArrayMeta.__eq__() 4 4 1
A _NDArrayMeta.__repr__() 7 7 3
A _NDArrayMeta.shape() 7 7 1
A _NDArray._only_size() 5 5 1
A _NDArray._only_sizes() 5 5 1
A _NDArray._size_and_type() 6 6 1
A _NDArrayMeta.__str__() 2 2 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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