Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/_lib/_uarray/__init__.py: 100%
2 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
1"""
2.. note:
3 If you are looking for overrides for NumPy-specific methods, see the
4 documentation for :obj:`unumpy`. This page explains how to write
5 back-ends and multimethods.
7``uarray`` is built around a back-end protocol, and overridable multimethods.
8It is necessary to define multimethods for back-ends to be able to override them.
9See the documentation of :obj:`generate_multimethod` on how to write multimethods.
13Let's start with the simplest:
15``__ua_domain__`` defines the back-end *domain*. The domain consists of period-
16separated string consisting of the modules you extend plus the submodule. For
17example, if a submodule ``module2.submodule`` extends ``module1``
18(i.e., it exposes dispatchables marked as types available in ``module1``),
19then the domain string should be ``"module1.module2.submodule"``.
22For the purpose of this demonstration, we'll be creating an object and setting
23its attributes directly. However, note that you can use a module or your own type
24as a backend as well.
26>>> class Backend: pass
27>>> be = Backend()
28>>> be.__ua_domain__ = "ua_examples"
30It might be useful at this point to sidetrack to the documentation of
31:obj:`generate_multimethod` to find out how to generate a multimethod
32overridable by :obj:`uarray`. Needless to say, writing a backend and
33creating multimethods are mostly orthogonal activities, and knowing
34one doesn't necessarily require knowledge of the other, although it
35is certainly helpful. We expect core API designers/specifiers to write the
36multimethods, and implementors to override them. But, as is often the case,
37similar people write both.
39Without further ado, here's an example multimethod:
41>>> import uarray as ua
42>>> from uarray import Dispatchable
43>>> def override_me(a, b):
44... return Dispatchable(a, int),
45>>> def override_replacer(args, kwargs, dispatchables):
46... return (dispatchables[0], args[1]), {}
47>>> overridden_me = ua.generate_multimethod(
48... override_me, override_replacer, "ua_examples"
49... )
51Next comes the part about overriding the multimethod. This requires
52the ``__ua_function__`` protocol, and the ``__ua_convert__``
53protocol. The ``__ua_function__`` protocol has the signature
54``(method, args, kwargs)`` where ``method`` is the passed
55multimethod, ``args``/``kwargs`` specify the arguments and ``dispatchables``
56is the list of converted dispatchables passed in.
58>>> def __ua_function__(method, args, kwargs):
59... return method.__name__, args, kwargs
60>>> be.__ua_function__ = __ua_function__
62The other protocol of interest is the ``__ua_convert__`` protocol. It has the
63signature ``(dispatchables, coerce)``. When ``coerce`` is ``False``, conversion
64between the formats should ideally be an ``O(1)`` operation, but it means that
65no memory copying should be involved, only views of the existing data.
67>>> def __ua_convert__(dispatchables, coerce):
68... for d in dispatchables:
69... if d.type is int:
70... if coerce and d.coercible:
71... yield str(d.value)
72... else:
73... yield d.value
74>>> be.__ua_convert__ = __ua_convert__
76Now that we have defined the backend, the next thing to do is to call the multimethod.
78>>> with ua.set_backend(be):
79... overridden_me(1, "2")
80('override_me', (1, '2'), {})
82Note that the marked type has no effect on the actual type of the passed object.
83We can also coerce the type of the input.
85>>> with ua.set_backend(be, coerce=True):
86... overridden_me(1, "2")
87... overridden_me(1.0, "2")
88('override_me', ('1', '2'), {})
89('override_me', ('1.0', '2'), {})
91Another feature is that if you remove ``__ua_convert__``, the arguments are not
92converted at all and it's up to the backend to handle that.
94>>> del be.__ua_convert__
95>>> with ua.set_backend(be):
96... overridden_me(1, "2")
97('override_me', (1, '2'), {})
99You also have the option to return ``NotImplemented``, in which case processing moves on
100to the next back-end, which in this case, doesn't exist. The same applies to
101``__ua_convert__``.
103>>> be.__ua_function__ = lambda *a, **kw: NotImplemented
104>>> with ua.set_backend(be):
105... overridden_me(1, "2")
106Traceback (most recent call last):
107 ...
108uarray.BackendNotImplementedError: ...
110The last possibility is if we don't have ``__ua_convert__``, in which case the job is left
111up to ``__ua_function__``, but putting things back into arrays after conversion will not be
112possible.
113"""
115from ._backend import *
116__version__ = '0.8.8.dev0+aa94c5a4.scipy'