Skip to content

Exposing APIs from HPy extensions to be used by other HPy extensions #446

@steve-s

Description

@steve-s

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions