Skip to content

Commit 31b6153

Browse files
committed
Deprecate stateful functions and __getitem__ and extend __call__
1 parent 3f0a19c commit 31b6153

File tree

1 file changed

+94
-23
lines changed

1 file changed

+94
-23
lines changed

datajoint/fetch.py

Lines changed: 94 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .blob import unpack
77
from . import DataJointError
88
from . import key as PRIMARY_KEY
9+
import warnings
910

1011

1112
def update_dict(d1, d2):
@@ -37,9 +38,15 @@ def _initialize_behavior(self):
3738
@property
3839
def squeeze(self):
3940
"""
41+
DEPRECATED
42+
4043
Changes the state of the fetch object to squeeze the returned values as much as possible.
4144
:return: a copy of the fetch object
4245
"""
46+
47+
warnings.warn('Use of `squeeze` on `fetch` object is deprecated. Please use `squeeze=True` keyword arguments '
48+
'in the call to `fetch`/`keys` instead', stacklevel=2)
49+
4350
ret = self.copy()
4451
ret.ext_behavior['squeeze'] = True
4552
return ret
@@ -60,6 +67,9 @@ def _prepare_attributes(item):
6067
raise DataJointError("Index must be a sequence or a string.")
6168
return item, attributes
6269

70+
def __len__(self):
71+
return len(self._relation)
72+
6373

6474

6575

@@ -76,6 +86,8 @@ def _initialize_behavior(self):
7686

7787
def order_by(self, *args):
7888
"""
89+
DEPRECATED
90+
7991
Changes the state of the fetch object to order the results by a particular attribute.
8092
The commands are handed down to mysql.
8193
:param args: the attributes to sort by. If DESC is passed after the name, then the order is descending.
@@ -85,6 +97,8 @@ def order_by(self, *args):
8597
>>> my_relation.fetch.order_by('language', 'name DESC')
8698
8799
"""
100+
warnings.warn('Use of `order_by` on `fetch` object is deprecated. Please use `order_by` keyword arguments in '
101+
'the call to `fetch`/`keys` instead', stacklevel=2)
88102
self = Fetch(self)
89103
if len(args) > 0:
90104
self.sql_behavior['order_by'] = args
@@ -93,71 +107,104 @@ def order_by(self, *args):
93107
@property
94108
def as_dict(self):
95109
"""
110+
DEPRECATED
111+
96112
Changes the state of the fetch object to return dictionaries.
97113
:return: a copy of the fetch object
98114
Example:
99115
100116
>>> my_relation.fetch.as_dict()
101117
102118
"""
119+
warnings.warn('Use of `as_dict` on `fetch` object is deprecated. Please use `as_dict` keyword arguments in the '
120+
'call to `fetch`/`keys` instead', stacklevel=2)
103121
ret = Fetch(self)
104122
ret.sql_behavior['as_dict'] = True
105123
return ret
106124

107125
def limit(self, limit):
108126
"""
127+
DEPRECATED
128+
109129
Limits the number of items fetched.
110130
111131
:param limit: limit on the number of items
112132
:return: a copy of the fetch object
113133
"""
134+
warnings.warn('Use of `limit` on `fetch` object is deprecated. Please use `limit` keyword arguments in '
135+
'the call to `fetch`/`keys` instead', stacklevel=2)
114136
ret = Fetch(self)
115137
ret.sql_behavior['limit'] = limit
116138
return ret
117139

118140
def offset(self, offset):
119141
"""
142+
DEPRECATED
143+
120144
Offsets the number of itms fetched. Needs to be applied with limit.
121145
122146
:param offset: offset
123147
:return: a copy of the fetch object
124148
"""
149+
150+
warnings.warn('Use of `offset` on `fetch` object is deprecated. Please use `offset` keyword arguments in '
151+
'the call to `fetch`/`keys` instead', stacklevel=2)
125152
ret = Fetch(self)
126153
if ret.sql_behavior['limit'] is None:
127154
warnings.warn('You should supply a limit together with an offset,')
128155
ret.sql_behavior['offset'] = offset
129156
return ret
130157

131-
def __call__(self, **kwargs):
158+
def __call__(self, *attrs, **kwargs):
132159
"""
133160
Fetches the relation from the database table into an np.array and unpacks blob attributes.
134161
162+
:param attrs: OPTIONAL. one or more attributes to fetch. If not provided, the call will return
163+
all attributes of this relation. If provided, returns tuples with an entry for each attribute.
135164
:param offset: the number of tuples to skip in the returned result
136165
:param limit: the maximum number of tuples to return
137166
:param order_by: the list of attributes to order the results. No ordering should be assumed if order_by=None.
138167
:param as_dict: returns a list of dictionaries instead of a record array
139168
:return: the contents of the relation in the form of a structured numpy.array
140169
"""
170+
# if 'order_by' passed in a string, make into list
171+
if isinstance(kwargs.get('order_by'), str):
172+
kwargs['order_by'] = [kwargs['order_by']]
173+
141174
sql_behavior = update_dict(self.sql_behavior, kwargs)
142175
ext_behavior = update_dict(self.ext_behavior, kwargs)
176+
total_behavior = dict(sql_behavior)
177+
total_behavior.update(ext_behavior)
143178

144179
unpack_ = partial(unpack, squeeze=ext_behavior['squeeze'])
145180

146181
if sql_behavior['limit'] is None and sql_behavior['offset'] is not None:
147182
warnings.warn('Offset set, but no limit. Setting limit to a large number. '
148183
'Consider setting a limit explicitly.')
149184
sql_behavior['limit'] = 2 * len(self._relation)
150-
cur = self._relation.cursor(**sql_behavior)
151-
heading = self._relation.heading
152-
if sql_behavior['as_dict']:
153-
ret = [OrderedDict((name, unpack_(d[name]) if heading[name].is_blob else d[name])
154-
for name in heading.names)
155-
for d in cur.fetchall()]
156-
else:
157-
ret = list(cur.fetchall())
158-
ret = np.array(ret, dtype=heading.as_dtype)
159-
for blob_name in heading.blobs:
160-
ret[blob_name] = list(map(unpack_, ret[blob_name]))
185+
186+
if len(attrs) == 0: # fetch all attributes
187+
cur = self._relation.cursor(**sql_behavior)
188+
heading = self._relation.heading
189+
if sql_behavior['as_dict']:
190+
ret = [OrderedDict((name, unpack_(d[name]) if heading[name].is_blob else d[name])
191+
for name in heading.names)
192+
for d in cur.fetchall()]
193+
else:
194+
ret = list(cur.fetchall())
195+
ret = np.array(ret, dtype=heading.as_dtype)
196+
for blob_name in heading.blobs:
197+
ret[blob_name] = list(map(unpack_, ret[blob_name]))
198+
199+
else: # if list of attributes provided
200+
single_output = len(attrs) == 1
201+
attributes = [a for a in attrs if a is not PRIMARY_KEY]
202+
result = self._relation.proj(*attributes).fetch(**total_behavior)
203+
return_values = [
204+
list(to_dicts(result[self._relation.primary_key]))
205+
if attribute is PRIMARY_KEY else result[attribute]
206+
for attribute in attrs]
207+
ret = return_values[0] if single_output else return_values
161208

162209
return ret
163210

@@ -193,6 +240,8 @@ def keys(self, **kwargs):
193240

194241
def __getitem__(self, item):
195242
"""
243+
DEPRECATED
244+
196245
Fetch attributes as separate outputs.
197246
datajoint.key is a special value that requests the entire primary key
198247
:return: tuple with an entry for each element of item
@@ -201,6 +250,10 @@ def __getitem__(self, item):
201250
>>> a, b = relation['a', 'b']
202251
>>> a, b, key = relation['a', 'b', datajoint.key]
203252
"""
253+
254+
warnings.warn('Use of `rel.fetch[a, b]` notation is deprecated. Please use `rel.fetch(a, b) for equivalent '
255+
'result', stacklevel=2)
256+
204257
behavior = dict(self.sql_behavior)
205258
behavior.update(self.ext_behavior)
206259

@@ -222,8 +275,7 @@ def __repr__(self):
222275
["\t{key}:\t{value}".format(key=k, value=str(v)) for k, v in behavior.items() if v is not None])
223276
return repr_str
224277

225-
def __len__(self):
226-
return len(self._relation)
278+
227279

228280

229281
class Fetch1(FetchBase, Callable):
@@ -233,28 +285,44 @@ class Fetch1(FetchBase, Callable):
233285
:param relation: relation the fetch object fetches data from
234286
"""
235287

236-
def __call__(self, **kwargs):
288+
def __call__(self, *attrs, **kwargs):
237289
"""
238290
This version of fetch is called when self is expected to contain exactly one tuple.
239291
:return: the one tuple in the relation in the form of a dict
240292
"""
241293
heading = self._relation.heading
242294

243-
#sql_behavior = update_dict(self.sql_behavior, kwargs)
295+
# sql_behavior = update_dict(self.sql_behavior, kwargs)
244296
ext_behavior = update_dict(self.ext_behavior, kwargs)
245297

246298
unpack_ = partial(unpack, squeeze=ext_behavior['squeeze'])
247299

300+
if len(attrs) == 0: # fetch all attributes
301+
cur = self._relation.cursor(as_dict=True)
302+
ret = cur.fetchone()
303+
if not ret or cur.fetchone():
304+
raise DataJointError('fetch1 should only be used for relations with exactly one tuple')
305+
ret = OrderedDict((name, unpack_(ret[name]) if heading[name].is_blob else ret[name])
306+
for name in heading.names)
307+
else:
308+
single_output = len(attrs) == 1
309+
attributes = [a for a in attrs if a is not PRIMARY_KEY]
310+
result = self._relation.proj(*attributes).fetch(**ext_behavior)
311+
if len(result) != 1:
312+
raise DataJointError('fetch1 should only return one tuple. %d tuples were found' % len(result))
313+
return_values = tuple(
314+
next(to_dicts(result[self._relation.primary_key]))
315+
if attribute is PRIMARY_KEY else result[attribute][0]
316+
for attribute in attrs)
317+
ret = return_values[0] if single_output else return_values
318+
248319

249-
cur = self._relation.cursor(as_dict=True)
250-
ret = cur.fetchone()
251-
if not ret or cur.fetchone():
252-
raise DataJointError('fetch1 should only be used for relations with exactly one tuple')
253-
return OrderedDict((name, unpack_(ret[name]) if heading[name].is_blob else ret[name])
254-
for name in heading.names)
320+
return ret
255321

256322
def __getitem__(self, item):
257323
"""
324+
DEPRECATED
325+
258326
Fetch attributes as separate outputs.
259327
datajoint.key is a special value that requests the entire primary key
260328
:return: tuple with an entry for each element of item
@@ -265,10 +333,13 @@ def __getitem__(self, item):
265333
>>> a, b, key = relation['a', 'b', datajoint.key]
266334
267335
"""
336+
warnings.warn('Use of `rel.fetch[a, b]` notation is deprecated. Please use `rel.fetch(a, b) for equivalent '
337+
'result', stacklevel=2)
338+
268339
behavior = dict(self.sql_behavior)
269340
behavior.update(self.ext_behavior)
270341

271-
single_output = isinstance(item, str) or item is PRIMARY_KEY or isinstance(item, int)
342+
single_output = isinstance(item, str) or item is PRIMARY_KEY
272343
item, attributes = self._prepare_attributes(item)
273344
result = self._relation.proj(*attributes).fetch(**behavior)
274345
if len(result) != 1:

0 commit comments

Comments
 (0)