uarray 0.9.2__cp313-cp313-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uarray/.coveragerc +9 -0
- uarray/__init__.py +118 -0
- uarray/_backend.py +800 -0
- uarray/_typing.pyi +65 -0
- uarray/_uarray.cp313-win32.pyd +0 -0
- uarray/_uarray.pyi +131 -0
- uarray/_version.py +16 -0
- uarray/conftest.py +11 -0
- uarray/py.typed +0 -0
- uarray/pytest.ini +3 -0
- uarray/tests/__init__.py +0 -0
- uarray/tests/example_helpers.py +46 -0
- uarray/tests/test_uarray.py +580 -0
- uarray-0.9.2.dist-info/LICENSE +29 -0
- uarray-0.9.2.dist-info/METADATA +88 -0
- uarray-0.9.2.dist-info/RECORD +18 -0
- uarray-0.9.2.dist-info/WHEEL +5 -0
- uarray-0.9.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import uarray as ua
|
|
2
|
+
import pickle
|
|
3
|
+
|
|
4
|
+
import pytest # type: ignore
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture(scope="function", autouse=True)
|
|
8
|
+
def cleanup_backends():
|
|
9
|
+
with ua.reset_state():
|
|
10
|
+
yield
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Backend:
|
|
14
|
+
__ua_domain__ = "ua_tests"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DisableBackend:
|
|
18
|
+
def __init__(self, domain="ua_tests"):
|
|
19
|
+
self.__ua_domain__ = domain
|
|
20
|
+
self.active = True
|
|
21
|
+
self.ret = object()
|
|
22
|
+
|
|
23
|
+
def __ua_function__(self, f, a, kw):
|
|
24
|
+
if self.active:
|
|
25
|
+
return self.ret
|
|
26
|
+
|
|
27
|
+
raise ua.BackendNotImplementedError(self.__ua_domain__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture()
|
|
31
|
+
def nullary_mm():
|
|
32
|
+
return ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_nestedbackend(nullary_mm):
|
|
36
|
+
obj = object()
|
|
37
|
+
be_outer = Backend()
|
|
38
|
+
be_outer.__ua_function__ = lambda f, a, kw: obj
|
|
39
|
+
|
|
40
|
+
def default(*a, **kw):
|
|
41
|
+
return nullary_mm(*a, **kw)
|
|
42
|
+
|
|
43
|
+
mm2 = ua.generate_multimethod(
|
|
44
|
+
lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default
|
|
45
|
+
)
|
|
46
|
+
be_inner = Backend()
|
|
47
|
+
|
|
48
|
+
def be2_ua_func(f, a, kw):
|
|
49
|
+
with ua.skip_backend(be_inner):
|
|
50
|
+
return f(*a, **kw)
|
|
51
|
+
|
|
52
|
+
be_inner.__ua_function__ = be2_ua_func
|
|
53
|
+
with ua.set_backend(be_outer), ua.set_backend(be_inner):
|
|
54
|
+
assert mm2() is obj
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _replacer(args, kwargs, dispatchables):
|
|
58
|
+
return (args, kwargs)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@ua.create_multimethod(_replacer, "ua_tests")
|
|
62
|
+
def pickle_mm():
|
|
63
|
+
return ()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_pickle_support():
|
|
67
|
+
unpickle_mm = pickle.loads(pickle.dumps(pickle_mm))
|
|
68
|
+
|
|
69
|
+
assert unpickle_mm is pickle_mm
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_registration(nullary_mm):
|
|
73
|
+
obj = object()
|
|
74
|
+
be = Backend()
|
|
75
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
76
|
+
|
|
77
|
+
ua.register_backend(be)
|
|
78
|
+
assert nullary_mm() is obj
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_global(nullary_mm):
|
|
82
|
+
obj = object()
|
|
83
|
+
be = Backend()
|
|
84
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
85
|
+
|
|
86
|
+
ua.set_global_backend(be)
|
|
87
|
+
assert nullary_mm() is obj
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ctx_before_global(nullary_mm):
|
|
91
|
+
obj = object()
|
|
92
|
+
obj2 = object()
|
|
93
|
+
be = Backend()
|
|
94
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
95
|
+
|
|
96
|
+
be2 = Backend()
|
|
97
|
+
be2.__ua_function__ = lambda f, a, kw: obj2
|
|
98
|
+
|
|
99
|
+
ua.set_global_backend(be)
|
|
100
|
+
|
|
101
|
+
with ua.set_backend(be2):
|
|
102
|
+
assert nullary_mm() is obj2
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_global_before_registered(nullary_mm):
|
|
106
|
+
obj = object()
|
|
107
|
+
obj2 = object()
|
|
108
|
+
be = Backend()
|
|
109
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
110
|
+
|
|
111
|
+
be2 = Backend()
|
|
112
|
+
be2.__ua_function__ = lambda f, a, kw: obj2
|
|
113
|
+
|
|
114
|
+
ua.set_global_backend(be)
|
|
115
|
+
ua.register_backend(be2)
|
|
116
|
+
assert nullary_mm() is obj
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_global_try_last(nullary_mm):
|
|
120
|
+
obj = object()
|
|
121
|
+
obj2 = object()
|
|
122
|
+
be = Backend()
|
|
123
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
124
|
+
|
|
125
|
+
be2 = Backend()
|
|
126
|
+
be2.__ua_function__ = lambda f, a, kw: obj2
|
|
127
|
+
|
|
128
|
+
ua.set_global_backend(be, try_last=True)
|
|
129
|
+
ua.register_backend(be2)
|
|
130
|
+
assert nullary_mm() is obj2
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_global_only(nullary_mm):
|
|
134
|
+
obj = object()
|
|
135
|
+
be = Backend()
|
|
136
|
+
be.__ua_function__ = lambda f, a, kw: NotImplemented
|
|
137
|
+
|
|
138
|
+
be2 = Backend()
|
|
139
|
+
be2.__ua_function__ = lambda f, a, kw: obj
|
|
140
|
+
|
|
141
|
+
ua.set_global_backend(be, only=True)
|
|
142
|
+
ua.register_backend(be2)
|
|
143
|
+
|
|
144
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
145
|
+
nullary_mm()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_clear_backends(nullary_mm):
|
|
149
|
+
obj = object()
|
|
150
|
+
obj2 = object()
|
|
151
|
+
be = Backend()
|
|
152
|
+
be.__ua_function__ = lambda f, a, kw: obj
|
|
153
|
+
|
|
154
|
+
be2 = Backend()
|
|
155
|
+
be2.__ua_function__ = lambda f, a, kw: obj2
|
|
156
|
+
|
|
157
|
+
ua.set_global_backend(be)
|
|
158
|
+
ua.register_backend(be2)
|
|
159
|
+
|
|
160
|
+
ua.clear_backends(Backend.__ua_domain__, registered=True, globals=True)
|
|
161
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
162
|
+
nullary_mm()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_function_attrs():
|
|
166
|
+
def extractor():
|
|
167
|
+
return ()
|
|
168
|
+
|
|
169
|
+
def replacer(a, kw, d):
|
|
170
|
+
return a, kw
|
|
171
|
+
|
|
172
|
+
def default():
|
|
173
|
+
return NotImplemented
|
|
174
|
+
|
|
175
|
+
mm = ua.generate_multimethod(extractor, replacer, "ua_tests", default=default)
|
|
176
|
+
|
|
177
|
+
assert mm.arg_extractor is extractor
|
|
178
|
+
assert mm.arg_replacer is replacer
|
|
179
|
+
assert mm.default is default
|
|
180
|
+
assert mm.domain == "ua_tests"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_raising_from_backend(nullary_mm):
|
|
184
|
+
def raise_(foo):
|
|
185
|
+
raise foo
|
|
186
|
+
|
|
187
|
+
Foo = ua.BackendNotImplementedError("Foo")
|
|
188
|
+
be = Backend()
|
|
189
|
+
be.__ua_function__ = lambda f, a, kw: raise_(Foo)
|
|
190
|
+
|
|
191
|
+
# BackendNotImplementedErrors are nested
|
|
192
|
+
with ua.set_backend(be):
|
|
193
|
+
with pytest.raises(ua.BackendNotImplementedError) as e:
|
|
194
|
+
nullary_mm()
|
|
195
|
+
|
|
196
|
+
assert (
|
|
197
|
+
e.value.args[0]
|
|
198
|
+
== "No selected backends had an implementation for this function."
|
|
199
|
+
)
|
|
200
|
+
assert type(e.value.args[1]) == tuple
|
|
201
|
+
assert e.value.args[1] == (be, Foo)
|
|
202
|
+
|
|
203
|
+
Bar = ua.BackendNotImplementedError("Bar")
|
|
204
|
+
be2 = Backend()
|
|
205
|
+
be2.__ua_function__ = lambda f, a, kw: raise_(Bar)
|
|
206
|
+
# Errors are in the order the backends were tried
|
|
207
|
+
with ua.set_backend(be), ua.set_backend(be2):
|
|
208
|
+
with pytest.raises(ua.BackendNotImplementedError) as e:
|
|
209
|
+
nullary_mm()
|
|
210
|
+
|
|
211
|
+
assert e.value.args[1] == (be2, Bar)
|
|
212
|
+
assert e.value.args[2] == (be, Foo)
|
|
213
|
+
|
|
214
|
+
be3 = Backend()
|
|
215
|
+
be3.__ua_function__ = lambda f, a, kw: "Success"
|
|
216
|
+
# Can succeed after a backend has raised BackendNotImplementedError
|
|
217
|
+
with ua.set_backend(be3), ua.set_backend(be):
|
|
218
|
+
assert nullary_mm() == "Success"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_nested():
|
|
222
|
+
be = Backend()
|
|
223
|
+
be.__ua_function__ = lambda f, a, kw: None
|
|
224
|
+
|
|
225
|
+
ctx = ua.set_backend(be)
|
|
226
|
+
|
|
227
|
+
with ctx, ctx:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_invalid():
|
|
232
|
+
be1 = Backend()
|
|
233
|
+
be1.__ua_function__ = lambda f, a, kw: None
|
|
234
|
+
|
|
235
|
+
be2 = Backend()
|
|
236
|
+
be2.__ua_function__ = lambda f, a, kw: None
|
|
237
|
+
|
|
238
|
+
ctx1 = ua.set_backend(be1)
|
|
239
|
+
ctx2 = ua.set_backend(be2)
|
|
240
|
+
|
|
241
|
+
with pytest.raises(RuntimeError):
|
|
242
|
+
try:
|
|
243
|
+
ctx1.__enter__()
|
|
244
|
+
try:
|
|
245
|
+
ctx2.__enter__()
|
|
246
|
+
finally:
|
|
247
|
+
ctx1.__exit__(None, None, None)
|
|
248
|
+
finally:
|
|
249
|
+
ctx2.__exit__(None, None, None)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def test_skip_comparison(nullary_mm):
|
|
253
|
+
be1 = Backend()
|
|
254
|
+
be1.__ua_function__ = lambda f, a, kw: None
|
|
255
|
+
|
|
256
|
+
class Backend2(Backend):
|
|
257
|
+
@staticmethod
|
|
258
|
+
def __ua_function__(f, a, kw):
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
def __eq__(self, other):
|
|
262
|
+
return other is self or other is be1
|
|
263
|
+
|
|
264
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
265
|
+
with ua.set_backend(be1), ua.skip_backend(Backend2()):
|
|
266
|
+
nullary_mm()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def test_skip_raises(nullary_mm):
|
|
270
|
+
be1 = Backend()
|
|
271
|
+
be1.__ua_function__ = lambda f, a, kw: None
|
|
272
|
+
|
|
273
|
+
foo = Exception("Foo")
|
|
274
|
+
|
|
275
|
+
class Backend2(Backend):
|
|
276
|
+
@staticmethod
|
|
277
|
+
def __ua_function__(f, a, kw):
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
def __eq__(self, other):
|
|
281
|
+
raise foo
|
|
282
|
+
|
|
283
|
+
with pytest.raises(Exception) as e:
|
|
284
|
+
with ua.set_backend(be1), ua.skip_backend(Backend2()):
|
|
285
|
+
nullary_mm()
|
|
286
|
+
|
|
287
|
+
assert e.value is foo
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_getset_state(cleanup_backends):
|
|
291
|
+
ua.set_global_backend(Backend())
|
|
292
|
+
ua.register_backend(Backend())
|
|
293
|
+
|
|
294
|
+
with ua.set_backend(Backend()), ua.skip_backend(Backend()):
|
|
295
|
+
state = ua.get_state()
|
|
296
|
+
|
|
297
|
+
pstate = state._pickle()
|
|
298
|
+
|
|
299
|
+
assert pstate != ua.get_state()._pickle()
|
|
300
|
+
|
|
301
|
+
with ua.set_state(state):
|
|
302
|
+
assert pstate[:2] == ua.get_state()._pickle()[:2]
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class ComparableBackend(Backend):
|
|
306
|
+
def __init__(self, obj):
|
|
307
|
+
super().__init__()
|
|
308
|
+
self.obj = obj
|
|
309
|
+
|
|
310
|
+
def __eq__(self, other):
|
|
311
|
+
return isinstance(other, ComparableBackend) and self.obj == other.obj
|
|
312
|
+
|
|
313
|
+
def __ne__(self, other):
|
|
314
|
+
return not (self == other)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_pickle_state():
|
|
318
|
+
ua.set_global_backend(ComparableBackend("a"))
|
|
319
|
+
ua.register_backend(ComparableBackend("b"))
|
|
320
|
+
|
|
321
|
+
with ua.set_backend(ComparableBackend("c")), ua.skip_backend(
|
|
322
|
+
ComparableBackend("d")
|
|
323
|
+
):
|
|
324
|
+
state = ua.get_state()
|
|
325
|
+
|
|
326
|
+
state_loaded = pickle.loads(pickle.dumps(state))
|
|
327
|
+
|
|
328
|
+
assert state._pickle() == state_loaded._pickle()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_hierarchical_backends():
|
|
332
|
+
mm = ua.generate_multimethod(
|
|
333
|
+
lambda: (), lambda a, kw, d: (a, kw), "ua_tests.foo.bar"
|
|
334
|
+
)
|
|
335
|
+
subdomains = "ua_tests.foo.bar".split(".")
|
|
336
|
+
depth = len(subdomains)
|
|
337
|
+
|
|
338
|
+
mms = [
|
|
339
|
+
ua.generate_multimethod(
|
|
340
|
+
lambda: (), lambda a, kw, d: (a, kw), ".".join(subdomains[: i + 1])
|
|
341
|
+
)
|
|
342
|
+
for i in range(depth)
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
be = [DisableBackend(".".join(subdomains[: i + 1])) for i in range(depth)]
|
|
346
|
+
|
|
347
|
+
ua.set_global_backend(be[1])
|
|
348
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
349
|
+
mms[0]()
|
|
350
|
+
|
|
351
|
+
for i in range(1, depth):
|
|
352
|
+
assert mms[i]() is be[1].ret
|
|
353
|
+
|
|
354
|
+
ua.set_global_backend(be[0])
|
|
355
|
+
|
|
356
|
+
for i in range(depth):
|
|
357
|
+
assert mms[i]() is be[min(i, 1)].ret
|
|
358
|
+
|
|
359
|
+
ua.set_global_backend(be[2])
|
|
360
|
+
|
|
361
|
+
for i in range(depth):
|
|
362
|
+
assert mms[i]() is be[i].ret
|
|
363
|
+
|
|
364
|
+
be[2].active = False
|
|
365
|
+
for i in range(depth):
|
|
366
|
+
print(i)
|
|
367
|
+
assert mms[i]() is be[min(i, 1)].ret
|
|
368
|
+
|
|
369
|
+
be[1].active = False
|
|
370
|
+
for i in range(depth):
|
|
371
|
+
assert mms[i]() is be[0].ret
|
|
372
|
+
|
|
373
|
+
be[0].active = False
|
|
374
|
+
for i in range(depth):
|
|
375
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
376
|
+
mms[i]()
|
|
377
|
+
|
|
378
|
+
# only=True prevents all further domain checking
|
|
379
|
+
be[0].active = True
|
|
380
|
+
be[1].active = True
|
|
381
|
+
with ua.set_backend(be[2], only=True), pytest.raises(ua.BackendNotImplementedError):
|
|
382
|
+
mms[2]()
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def test_multidomain_backends():
|
|
386
|
+
n_domains = 2
|
|
387
|
+
be = DisableBackend(domain=["ua_tests" + str(i) for i in range(n_domains)])
|
|
388
|
+
|
|
389
|
+
mms = [
|
|
390
|
+
ua.generate_multimethod(
|
|
391
|
+
lambda: (), lambda a, kw, d: (a, kw), "ua_tests" + str(i)
|
|
392
|
+
)
|
|
393
|
+
for i in range(n_domains)
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
def assert_no_backends():
|
|
397
|
+
for i in range(len(mms)):
|
|
398
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
399
|
+
mms[i]()
|
|
400
|
+
|
|
401
|
+
def assert_backend_active(backend):
|
|
402
|
+
assert all(mms[i]() is backend.ret for i in range(len(mms)))
|
|
403
|
+
|
|
404
|
+
assert_no_backends()
|
|
405
|
+
|
|
406
|
+
with ua.set_backend(be):
|
|
407
|
+
assert_backend_active(be)
|
|
408
|
+
|
|
409
|
+
ua.set_global_backend(be)
|
|
410
|
+
assert_backend_active(be)
|
|
411
|
+
|
|
412
|
+
with ua.skip_backend(be):
|
|
413
|
+
assert_no_backends()
|
|
414
|
+
|
|
415
|
+
assert_backend_active(be)
|
|
416
|
+
|
|
417
|
+
for i in range(len(mms)):
|
|
418
|
+
ua.clear_backends(mms[i].domain, globals=True)
|
|
419
|
+
|
|
420
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
421
|
+
mms[i]()
|
|
422
|
+
|
|
423
|
+
for j in range(i + 1, len(mms)):
|
|
424
|
+
assert mms[j]() is be.ret
|
|
425
|
+
|
|
426
|
+
assert_no_backends()
|
|
427
|
+
|
|
428
|
+
ua.register_backend(be)
|
|
429
|
+
assert_backend_active(be)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def test_determine_backend(nullary_mm):
|
|
433
|
+
class TypeA:
|
|
434
|
+
pass
|
|
435
|
+
|
|
436
|
+
class TypeB:
|
|
437
|
+
pass
|
|
438
|
+
|
|
439
|
+
mark = "determine_backend_test"
|
|
440
|
+
|
|
441
|
+
class TypeBackend:
|
|
442
|
+
__ua_domain__ = "ua_tests"
|
|
443
|
+
|
|
444
|
+
def __init__(self, my_type):
|
|
445
|
+
self.my_type = my_type
|
|
446
|
+
|
|
447
|
+
def __ua_convert__(self, dispatchables, coerce):
|
|
448
|
+
if not all(
|
|
449
|
+
type(d.value) is self.my_type and d.type is mark for d in dispatchables
|
|
450
|
+
):
|
|
451
|
+
return NotImplemented
|
|
452
|
+
return tuple(d.value for d in dispatchables)
|
|
453
|
+
|
|
454
|
+
def __ua_function__(self, func, args, kwargs):
|
|
455
|
+
return self.my_type
|
|
456
|
+
|
|
457
|
+
BackendA = TypeBackend(TypeA)
|
|
458
|
+
BackendB = TypeBackend(TypeB)
|
|
459
|
+
|
|
460
|
+
with ua.set_backend(BackendA), pytest.raises(ua.BackendNotImplementedError):
|
|
461
|
+
with ua.determine_backend(TypeB(), mark, domain="ua_tests"):
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
with ua.set_backend(BackendA), ua.set_backend(BackendB):
|
|
465
|
+
with ua.determine_backend(TypeA(), mark, domain="ua_tests"):
|
|
466
|
+
assert nullary_mm() is TypeA
|
|
467
|
+
|
|
468
|
+
with ua.determine_backend(TypeB(), mark, domain="ua_tests"):
|
|
469
|
+
assert nullary_mm() is TypeB
|
|
470
|
+
|
|
471
|
+
# Has no __ua_convert__, so assumed to not accept the type
|
|
472
|
+
with ua.set_backend(DisableBackend()), pytest.raises(ua.BackendNotImplementedError):
|
|
473
|
+
with ua.determine_backend(TypeB(), mark, domain="ua_tests"):
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
with ua.set_backend(BackendA), ua.set_backend(BackendB):
|
|
477
|
+
with pytest.raises(ua.BackendNotImplementedError):
|
|
478
|
+
with ua.determine_backend_multi(
|
|
479
|
+
[ua.Dispatchable(TypeA(), mark), ua.Dispatchable(TypeB(), mark)],
|
|
480
|
+
domain="ua_tests",
|
|
481
|
+
):
|
|
482
|
+
pass
|
|
483
|
+
|
|
484
|
+
with ua.determine_backend_multi(
|
|
485
|
+
[ua.Dispatchable(TypeA(), mark), ua.Dispatchable(TypeA(), mark)],
|
|
486
|
+
domain="ua_tests",
|
|
487
|
+
):
|
|
488
|
+
assert nullary_mm() is TypeA
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def test_determine_backend_coerce(nullary_mm):
|
|
492
|
+
class TypeA:
|
|
493
|
+
pass
|
|
494
|
+
|
|
495
|
+
class TypeB:
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
mark = "determine_backend_test"
|
|
499
|
+
|
|
500
|
+
class TypeBackend:
|
|
501
|
+
__ua_domain__ = "ua_tests"
|
|
502
|
+
|
|
503
|
+
def __init__(self, my_type):
|
|
504
|
+
self.my_type = my_type
|
|
505
|
+
|
|
506
|
+
def __ua_convert__(self, dispatchables, coerce):
|
|
507
|
+
if len(dispatchables) > 0:
|
|
508
|
+
print(dispatchables[0], coerce)
|
|
509
|
+
if coerce and all(d.coercible for d in dispatchables):
|
|
510
|
+
return tuple(self.my_type() for _ in dispatchables)
|
|
511
|
+
|
|
512
|
+
if not all(
|
|
513
|
+
type(d.value) is self.my_type and d.type is mark for d in dispatchables
|
|
514
|
+
):
|
|
515
|
+
return NotImplemented
|
|
516
|
+
return tuple(d.value for d in dispatchables)
|
|
517
|
+
|
|
518
|
+
def __ua_function__(self, func, args, kwargs):
|
|
519
|
+
return self.my_type
|
|
520
|
+
|
|
521
|
+
BackendA = TypeBackend(TypeA)
|
|
522
|
+
BackendB = TypeBackend(TypeB)
|
|
523
|
+
unary_mm = ua.generate_multimethod(
|
|
524
|
+
lambda a: (ua.Dispatchable(a, mark),), lambda a, kw, d: (d, kw), "ua_tests"
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# coercion is not forced on the existing set backend
|
|
528
|
+
with ua.set_backend(BackendA), ua.set_backend(BackendB):
|
|
529
|
+
with ua.determine_backend(TypeA(), mark, domain="ua_tests", coerce=True):
|
|
530
|
+
assert nullary_mm() is TypeA
|
|
531
|
+
assert unary_mm(TypeB()) is TypeA
|
|
532
|
+
|
|
533
|
+
# But is allowed if the backend was set with coerce in the first place
|
|
534
|
+
with ua.set_backend(BackendA), ua.set_backend(BackendB, coerce=True):
|
|
535
|
+
with ua.determine_backend(TypeA(), mark, domain="ua_tests", coerce=True):
|
|
536
|
+
assert nullary_mm() is TypeB
|
|
537
|
+
assert unary_mm(TypeA()) is TypeB
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def test_default(nullary_mm):
|
|
541
|
+
obj = object()
|
|
542
|
+
be = Backend()
|
|
543
|
+
be.__ua_function__ = lambda f, a, kw: NotImplemented
|
|
544
|
+
|
|
545
|
+
# If a backend returns NotImplemented, the default is called
|
|
546
|
+
def default1(*a, **kw):
|
|
547
|
+
return obj
|
|
548
|
+
|
|
549
|
+
mm1 = ua.generate_multimethod(
|
|
550
|
+
lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default1
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
with ua.set_backend(be):
|
|
554
|
+
assert mm1() is obj
|
|
555
|
+
|
|
556
|
+
# If all backends fail, the default is called again without a specific backend
|
|
557
|
+
num_calls = [0]
|
|
558
|
+
|
|
559
|
+
def default2(*a, **kw):
|
|
560
|
+
num_calls[0] = num_calls[0] + 1
|
|
561
|
+
raise ua.BackendNotImplementedError()
|
|
562
|
+
|
|
563
|
+
mm2 = ua.generate_multimethod(
|
|
564
|
+
lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default2
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
with ua.set_backend(be), pytest.raises(ua.BackendNotImplementedError):
|
|
568
|
+
mm2()
|
|
569
|
+
|
|
570
|
+
assert num_calls[0] == 2
|
|
571
|
+
|
|
572
|
+
# If the last backend is set as only or coerce, the last default call is skipped
|
|
573
|
+
num_calls[0] = 0
|
|
574
|
+
with ua.set_backend(be, only=True), pytest.raises(ua.BackendNotImplementedError):
|
|
575
|
+
mm2()
|
|
576
|
+
assert num_calls[0] == 1
|
|
577
|
+
num_calls[0] = 0
|
|
578
|
+
with ua.set_backend(be, coerce=True), pytest.raises(ua.BackendNotImplementedError):
|
|
579
|
+
mm2()
|
|
580
|
+
assert num_calls[0] == 1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018, Quansight-Labs
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: uarray
|
|
3
|
+
Version: 0.9.2
|
|
4
|
+
Summary: Array interface object for Python with pluggable backends and a multiple-dispatch mechanism for defining down-stream functions
|
|
5
|
+
Maintainer-email: Hameer Abbasi <habbasi@quansight.com>
|
|
6
|
+
License: BSD 3-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2018, Quansight-Labs
|
|
9
|
+
All rights reserved.
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer.
|
|
16
|
+
|
|
17
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
and/or other materials provided with the distribution.
|
|
20
|
+
|
|
21
|
+
* Neither the name of the copyright holder nor the names of its
|
|
22
|
+
contributors may be used to endorse or promote products derived from
|
|
23
|
+
this software without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
Project-URL: Documentation, https://uarray.org/
|
|
37
|
+
Project-URL: Source, https://github.com/Quansight-Labs/uarray/
|
|
38
|
+
Project-URL: Repository, https://github.com/Quansight-Labs/uarray.git
|
|
39
|
+
Project-URL: Issue Tracker, https://github.com/Quansight-Labs/uarray/issues
|
|
40
|
+
Keywords: uarray,scipy,multiple-dispatch
|
|
41
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
42
|
+
Classifier: Operating System :: OS Independent
|
|
43
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
44
|
+
Classifier: Programming Language :: Python
|
|
45
|
+
Classifier: Programming Language :: Python :: 3
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
47
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
49
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
50
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
51
|
+
Classifier: Intended Audience :: Developers
|
|
52
|
+
Classifier: Intended Audience :: Science/Research
|
|
53
|
+
Classifier: Typing :: Typed
|
|
54
|
+
Requires-Python: >=3.10
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
License-File: LICENSE
|
|
57
|
+
Provides-Extra: docs
|
|
58
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
59
|
+
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
60
|
+
Provides-Extra: tests
|
|
61
|
+
Requires-Dist: pytest>=3.5; extra == "tests"
|
|
62
|
+
Requires-Dist: pytest-flake8; extra == "tests"
|
|
63
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
64
|
+
Requires-Dist: mypy>=0.930; extra == "tests"
|
|
65
|
+
Provides-Extra: optional
|
|
66
|
+
Provides-Extra: all
|
|
67
|
+
Requires-Dist: uarray[docs]; extra == "all"
|
|
68
|
+
Requires-Dist: uarray[tests]; extra == "all"
|
|
69
|
+
Requires-Dist: uarray[optional]; extra == "all"
|
|
70
|
+
|
|
71
|
+
# `uarray` - A back-end mechanism geared towards array computing
|
|
72
|
+
|
|
73
|
+
[](https://gitter.im/Plures/uarray?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
74
|
+
[](https://github.com/Quansight-Labs/uarray/actions)
|
|
75
|
+
[](https://pypi.org/project/uarray/)
|
|
76
|
+
|
|
77
|
+
<img src="docs/logo.png" style="width: 20em; text-align: center;" alt="uarray logo">
|
|
78
|
+
|
|
79
|
+
- [Documentation](https://uarray.org)
|
|
80
|
+
- [Road Map](https://github.com/orgs/Quansight-Labs/projects/1)
|
|
81
|
+
- [Future Meetings](https://calendar.google.com/calendar/embed?src=quansight.com_cg7sf4usbcn18gdhdb3l2c6v1g%40group.calendar.google.com&ctz=America%2FNew_York)
|
|
82
|
+
- [Meeting Notes](https://github.com/Quansight-Labs/uarray/wiki/Meeting-Notes)
|
|
83
|
+
- [References](https://github.com/Quansight-Labs/uarray/wiki/References)
|
|
84
|
+
- [Papers](https://paperpile.com/shared/fHftX5)
|
|
85
|
+
|
|
86
|
+
## Contributing
|
|
87
|
+
|
|
88
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for more information on how to contribute to `uarray`.
|