uarray 0.9.2__cp313-cp313-win_amd64.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.
@@ -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
+ [![Join the chat at https://gitter.im/Plures/uarray](https://badges.gitter.im/Plures/uarray.svg)](https://gitter.im/Plures/uarray?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
74
+ [![Build Status](https://github.com/Quansight-Labs/uarray/actions/workflows/build.yaml/badge.svg)](https://github.com/Quansight-Labs/uarray/actions)
75
+ [![PyPI](https://img.shields.io/pypi/v/uarray.svg?style=flat-square)](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`.