Add support for custom call policies #767
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds a new function annotation,
nb::call_policy<Policy>()
, which causes static methods of the user-providedPolicy
to be invoked before and after the bound C++ function. Unlikecall_guard
, the call policy methods operate on the Python object level and have access to the function arguments (precall can even modify them) and return value. This allows various esoteric user customizations that might be desirable in certain circumstances, with a minimal impact on the footprint of nanobind itself.Motivating use case: a number of the types of interest in my application support a lightweight callback mechanism. The callback type we use is trivially copyable, so it can't manage ownership of a Python object directly. A custom call policy allows us to automatically take out a reference to the underlying Python callable when a new callback is subscribed, and drop the reference when it's unsubscribed. Allowing
precall()
to modify the argument list permits normalization (letting equal-but-distinct Python callables map to identical C++ callback objects, so that the C++ unsubscribe logic can find the callback that was originally subscribed).While working on this I noticed that a function annotated with
nb::keep_alive
will leak a reference to its return value if thekeep_alive
can't be set up. I worked around the analogous issue for call policies, but didn't change the implementation ofkeep_alive
in case this is a deliberate performance tradeoff.