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

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. 

6 

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. 

10 

11 

12 

13Let's start with the simplest: 

14 

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"``. 

20 

21 

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. 

25 

26>>> class Backend: pass 

27>>> be = Backend() 

28>>> be.__ua_domain__ = "ua_examples" 

29 

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. 

38 

39Without further ado, here's an example multimethod: 

40 

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... ) 

50 

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. 

57 

58>>> def __ua_function__(method, args, kwargs): 

59... return method.__name__, args, kwargs 

60>>> be.__ua_function__ = __ua_function__ 

61 

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. 

66 

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__ 

75 

76Now that we have defined the backend, the next thing to do is to call the multimethod. 

77 

78>>> with ua.set_backend(be): 

79... overridden_me(1, "2") 

80('override_me', (1, '2'), {}) 

81 

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. 

84 

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'), {}) 

90 

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. 

93 

94>>> del be.__ua_convert__ 

95>>> with ua.set_backend(be): 

96... overridden_me(1, "2") 

97('override_me', (1, '2'), {}) 

98 

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__``. 

102 

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: ... 

109 

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""" 

114 

115from ._backend import * 

116__version__ = '0.8.8.dev0+aa94c5a4.scipy'