Coverage for src/hdmf/query.py: 69%
120 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-07-21 22:12 +0000
« prev ^ index » next coverage.py v7.2.5, created at 2023-07-21 22:12 +0000
1from abc import ABCMeta, abstractmethod
3import numpy as np
5from .array import Array
6from .utils import ExtenderMeta, docval_macro, docval, getargs
9class Query(metaclass=ExtenderMeta):
10 __operations__ = (
11 '__lt__',
12 '__gt__',
13 '__le__',
14 '__ge__',
15 '__eq__',
16 '__ne__',
17 )
19 @classmethod
20 def __build_operation(cls, op):
21 def __func(self, arg):
22 return cls(self, op, arg)
24 @ExtenderMeta.pre_init
25 def __make_operators(cls, name, bases, classdict):
26 if not isinstance(cls.__operations__, tuple): 26 ↛ 27line 26 didn't jump to line 27, because the condition on line 26 was never true
27 raise TypeError("'__operations__' must be of type tuple")
28 # add any new operations
29 if len(bases) and 'Query' in globals() and issubclass(bases[-1], Query) \ 29 ↛ 31line 29 didn't jump to line 31, because the condition on line 29 was never true
30 and bases[-1].__operations__ is not cls.__operations__:
31 new_operations = list(cls.__operations__)
32 new_operations[0:0] = bases[-1].__operations__
33 cls.__operations__ = tuple(new_operations)
34 for op in cls.__operations__:
35 if not hasattr(cls, op): 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true
36 setattr(cls, op, cls.__build_operation(op))
38 def __init__(self, obj, op, arg):
39 self.obj = obj
40 self.op = op
41 self.arg = arg
42 self.collapsed = None
43 self.expanded = None
45 @docval({'name': 'expand', 'type': bool, 'help': 'whether or not to expand result', 'default': True})
46 def evaluate(self, **kwargs):
47 expand = getargs('expand', kwargs)
48 if expand: 48 ↛ 53line 48 didn't jump to line 53, because the condition on line 48 was never false
49 if self.expanded is None: 49 ↛ 51line 49 didn't jump to line 51, because the condition on line 49 was never false
50 self.expanded = self.__evalhelper()
51 return self.expanded
52 else:
53 if self.collapsed is None:
54 self.collapsed = self.__collapse(self.__evalhelper())
55 return self.collapsed
57 def __evalhelper(self):
58 obj = self.obj
59 arg = self.arg
60 if isinstance(obj, Query): 60 ↛ 61line 60 didn't jump to line 61, because the condition on line 60 was never true
61 obj = obj.evaluate()
62 elif isinstance(obj, HDMFDataset): 62 ↛ 64line 62 didn't jump to line 64, because the condition on line 62 was never false
63 obj = obj.dataset
64 if isinstance(arg, Query): 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true
65 arg = self.arg.evaluate()
66 return getattr(obj, self.op)(self.arg)
68 def __collapse(self, result):
69 if isinstance(result, slice):
70 return (result.start, result.stop)
71 elif isinstance(result, list):
72 ret = list()
73 for idx in result:
74 if isinstance(idx, slice) and (idx.step is None or idx.step == 1):
75 ret.append((idx.start, idx.stop))
76 else:
77 ret.append(idx)
78 return ret
79 else:
80 return result
82 def __and__(self, other):
83 return NotImplemented
85 def __or__(self, other):
86 return NotImplemented
88 def __xor__(self, other):
89 return NotImplemented
91 def __contains__(self, other):
92 return NotImplemented
95@docval_macro('array_data')
96class HDMFDataset(metaclass=ExtenderMeta):
97 __operations__ = (
98 '__lt__',
99 '__gt__',
100 '__le__',
101 '__ge__',
102 '__eq__',
103 '__ne__',
104 )
106 @classmethod
107 def __build_operation(cls, op):
108 def __func(self, arg):
109 return Query(self, op, arg)
111 setattr(__func, '__name__', op)
112 return __func
114 @ExtenderMeta.pre_init
115 def __make_operators(cls, name, bases, classdict):
116 if not isinstance(cls.__operations__, tuple): 116 ↛ 117line 116 didn't jump to line 117, because the condition on line 116 was never true
117 raise TypeError("'__operations__' must be of type tuple")
118 # add any new operations
119 if len(bases) and 'Query' in globals() and issubclass(bases[-1], Query) \ 119 ↛ 121line 119 didn't jump to line 121, because the condition on line 119 was never true
120 and bases[-1].__operations__ is not cls.__operations__:
121 new_operations = list(cls.__operations__)
122 new_operations[0:0] = bases[-1].__operations__
123 cls.__operations__ = tuple(new_operations)
124 for op in cls.__operations__:
125 setattr(cls, op, cls.__build_operation(op))
127 def __evaluate_key(self, key):
128 if isinstance(key, tuple) and len(key) == 0: 128 ↛ 129line 128 didn't jump to line 129, because the condition on line 128 was never true
129 return key
130 if isinstance(key, (tuple, list, np.ndarray)):
131 return list(map(self.__evaluate_key, key))
132 else:
133 if isinstance(key, Query):
134 return key.evaluate()
135 return key
137 def __getitem__(self, key):
138 idx = self.__evaluate_key(key)
139 return self.dataset[idx]
141 @docval({'name': 'dataset', 'type': ('array_data', Array), 'doc': 'the HDF5 file lazily evaluate'})
142 def __init__(self, **kwargs):
143 super().__init__()
144 self.__dataset = getargs('dataset', kwargs)
146 @property
147 def dataset(self):
148 return self.__dataset
150 @property
151 def dtype(self):
152 return self.__dataset.dtype
154 def __len__(self):
155 return len(self.__dataset)
157 def __iter__(self):
158 return iter(self.dataset)
160 def __next__(self):
161 return next(self.dataset)
163 def next(self):
164 return self.dataset.next()
167class ReferenceResolver(metaclass=ABCMeta):
168 """
169 A base class for classes that resolve references
170 """
172 @classmethod
173 @abstractmethod
174 def get_inverse_class(cls):
175 """
176 Return the class the represents the ReferenceResolver
177 that resolves references to the opposite type.
179 BuilderResolver.get_inverse_class should return a class
180 that subclasses ContainerResolver.
182 ContainerResolver.get_inverse_class should return a class
183 that subclasses BuilderResolver.
184 """
185 pass
187 @abstractmethod
188 def invert(self):
189 """
190 Return an object that defers reference resolution
191 but in the opposite direction.
192 """
193 pass
196class BuilderResolver(ReferenceResolver):
197 """
198 A reference resolver that resolves references to Builders
200 Subclasses should implement the invert method and the get_inverse_class
201 classmethod
203 BuilderResolver.get_inverse_class should return a class that subclasses
204 ContainerResolver.
205 """
207 pass
210class ContainerResolver(ReferenceResolver):
211 """
212 A reference resolver that resolves references to Containers
214 Subclasses should implement the invert method and the get_inverse_class
215 classmethod
217 ContainerResolver.get_inverse_class should return a class that subclasses
218 BuilderResolver.
219 """
221 pass