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

nptyping._array_meta.ArrayMeta.__getitem__()   D

Complexity

Conditions 12

Size

Total Lines 41
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 33
nop 2
dl 0
loc 41
rs 4.8
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like nptyping._array_meta.ArrayMeta.__getitem__() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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