User API#
Users of libraries that integrate spatch should usually refer to the library documentation.
In general, the main interaction with spatch will be to modify dispatching behavior. We expect further inspection and modification API to be added in the future.
Modifying and tracing dispatching#
Libraries will re-expose all of this functionality under their own API/names.
There are currently three global environment variables to modify dispatching behavior at startup time:
<SPECIFIC_NAME>_PRIORITIZE
: Comma seperated list of backends. This is the same asBackendOpts
prioritize=
option.<SPECIFIC_NAME>_BLOCK
: Comma seperated list of backends. This prevents loading the backends as if they were not installed. No backend code will be executed (its entry-point will not be loaded).<SPECIFIC_NAME>_SET_ORDER
: Comma seperated list of backend orders. seperated by>
. I.e.name1>name2,name3>name2
means thatname1
andname3
are ordered beforename2
. This is more fine-grained than the above two and the above two take precedence. Useful to fix relative order of backends without affecting the priority of backends not listed (including when backends have issues and loading fails).
Note that unknown backend names are ignored in these variables, so check these carefully.
The main interaction of users should however be via the backend options system:
- class spatch.backend_system.BackendOpts(*, prioritize=(), disable=(), type=None, trace=False)#
Customize or query the backend dispatching behavior.
Context manager to allow customizing the dispatching behavior. Instantiating the context manager fetches the current dispatching state, modifies it as requested, and then stores the state. You can use this context multiple times (but not nested).
enable_globally()
can be used for convenience but should only be used from the main program.Initializing
BackendOpts
without arguments can be used to query the current state.See
__call__()
for information about use as a function decorator.Warning
When modifying dispatching behavior you must be aware that this may have side effects on your program. See details in notes.
- Parameters:
prioritize (str or list of str) – The backends to prioritize, this may also enable a backend that would otherwise never be chosen. Prioritization nests, outer prioritization remain active.
disable (str or list of str) – Specific backends to disable. This nests, outer disabled are still disabled (unless prioritized).
type (type) –
A type to dispatch for. Functions will behave as if this type was used when calling (additionally to types passed). This is a way to enforce use of this type (and thus backends using it). But if used for a larger chunk of code it can clearly break type assumptions easily. (The type argument of a previous call is replaced.)
Note
If no version of a function exists that supports this type, then dispatching will currently fail. It may try without the type in the future to allow a graceful fallback.
trace (bool) –
If
True
entering returns a list and this list will contain information for each call to a dispatchable function. (When nesting, an outer existing tracing is currently paused.)Note
Tracing is considered for debugging/human readers and does not guarantee a stable API for the
trace
result.
- backends#
Tuple of active backend names in order of priority. If type is set, not all will be applicable and type specialized backends have typically a lower priority since they will be chosen based on input types.
- type#
The type to dispatch for within this context.
- trace#
The trace object (currenly a list as described in the examples). If used, the trace is also returned when entering the context.
Notes
Both
prioritize
andtype
can modify behavior of the contained block in significant ways.For
prioritize=
this depends on the backend. A backend may for example result in lower precision results. Assuming no bugs, a backend should return roughly equivalent results.For
type=
code behavior will change to work as if you were using this type. This will definitely change behavior. I.e. many functions may return the given type. Sometimes this may be useful to modify behavior of code wholesale.Especially if you call a third party library, either of these changes may break assumptions in their code and while a third party may ensure the correct type for them locally it is not a bug for them not to do so.
Examples
This example is based on a hypothetical
cucim
backend forskimage
:>>> with skimage.backend_opts(prioritize="cucim"): ... ...
Which might use cucim also for NumPy inputs (but return NumPy arrays then). (I.e.
cucim
would use the fact that it is prioritized here to decide it is OK to convert NumPy arrays – it could still defer for speed reasons.)On the other hand:
>>> with skimage.backend_opts(type=cupy.ndarray): ... ...
Would guarantee that we work with CuPy arrays that the a returned array is a CuPy array. Together with
prioritize="cucim"
it ensure the cucim version is used (otherwise another backend may be preferred if it also supports CuPy arrays) or cucim may choose to require prioritization to accept NumPy arrays.Backends should simply document their behavior with
backend_opts
and which usage pattern they see for their users.Tracing calls can be done using, where
trace
is a list of informations for each call. This contains a tuple of the function identifier and a list of backends called (typically exactly one, but it will also note if a backend deferred viashould_run
).>>> with skimage.backend_opts(trace=True) as trace: ... ...
- __call__(func)#
Decorate a function to freeze its dispatching state.
BackendOpts
can be used as a decorator, it means freezing the state early (user context around call is ignored). In other words, the following two patterns are very different, because for the decorator,backend_opts
is called outside of the function:@backend_opts(...) def func(): # code def func(): with backend_opts(...): # code
Note
An option here is to add
isolated=False
to allow mutating the context at call-time/time of entering (isolated=False
would do nothing in a simple context manager use-case).- Parameters:
func (callable) – The function to decorate.
- Returns:
func – The decorated function.
- Return type:
callable
- enable_globally()#
Apply these backend options globally.
Setting this state globally should only be done by the end user and never by a library. Global change of behavior may modify unexpected parts of the code (e.g. in third party code) so that it is safer to use the contextmanager
with
statement instead.This method will issue a warning if the dispatching state has been previously modified programatically.