-
-
Notifications
You must be signed in to change notification settings - Fork 52
Description
The motivating example is the NumPy API that is exposed to other Python extensions such that they can work with arrays natively/directly without a round-trip through Python code/abstractions.
How the NumPy API works at the moment:
- NumPy provides a header file with a definition of a struct that holds pointers to some objects (e.g., array type), and some API functions, this is similar to
HPyContext
- NumPy exposes a
PyCapsule
with a pointer to this struct filled with pointers to the implementation - 3rd party extension includes the NumPy header file, fishes the
PyCapsule
from NumPy, gets the raw C pointer from it and uses it to call the NumPy API through the struct
The very same scheme can work with HPy, but has one drawback: the 3rd party extension gets some HPyContext
and passes it to NumPy, which means:
- NumPy must be built for ABI compatible HPyContext version (could be lower minor version, because those are binary compatible)
- Before this the Python VM could (for some optimization/implementation reason) send different
HPyContext
instance to different packages (it can store module state in it, for example). WithHPyContext
flowing from one extension to another, this is no longer possible. - In general it may be useful to be able to intercept and control the communication between extensions
Are those restrictions problematic enough to seek a better solution?
One possibility is to provide some way to "wrap" function pointers with a trampoline that can "transform" the HPyContext
to another if necessary. Example in code:
// NumPy:
HPy my_api_function(HPyContext *ctx, HPy h) { ... }
// ...
numpy_api_capsule->my_api_function_pointer = HPy_AsAPI(ctx, &my_api_function);
// 3rd party using the API to call the function:
numpy_api_capsule->my_api_function_pointer(my_hpy_context, my_handle);
// HPy universal implementation of the generated trampoline would be:
HPy_API_token numpy_token; // implementation specific:
// a pointer to anything the implementation needs, initialized in the HPy_AsAPI call
HPy my_api_function_trampoline(HPyContext *caller_ctx, HPy h) {
HPyContext *numpyCtx = _HPy_TransformContext(caller_ctx, numpy_token); // part of ABI, not API
my_api_function(numpyCtx, h);
}
Question is how to generate the trampoline. We can use macros for that, something like HPy_APIDef(...)
. As a bonus we could generate CPython API trampolines, so that the API can be usable from non-HPy packages (NumPy would have to expose another capsule with the CPython trampolines to be used by non-HPy packages).