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 |
|
|
|
|
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
|
|
|
|