PyO3 / PyO3/pyo3

RFC: `py_call!` macro

Open
#4,414 5 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
16.2k
Forks
1k
Avg merge
2d 6h
Merged PRs (30d)
66

Description

At the moment, to call a Python function we have .call(args, kwargs), .call1(args) and .call0() methods.

I have two main concerns with these:

  • I think to a user the names are not immediately intuitive. That said, I can't suggest a combination I love much better.
  • These calls all use the tuple-and-dict calling convention. We now have the "vectorcall" calling convention which supports a list of arguments as a C array, (and optionally keyword argument names as a tuple), which is meaningfully more efficient.

I have been wondering for a while if we should have a py_call! macro which mirrors Python syntax and does its best to be efficient.

In all of these examples below, I assume obj to be Bound<'_, T>:

py_call!{ obj() }   // call obj without arguments, equivalent to current `obj.call0()`
py_call!{ obj(a, b) }  // call obj with positional arguments a and b. Use "vectorcall" convention for efficiency
py_call!{ obj(a, b, *args) } // call obj with positional arguments a, b, and the unpacked `*args` iterable.

py_call!{ obj(a, b, *args, x=1, y=2) } // same as above with keyword arguments x and y
py_call!{ obj(a, b, *args, x=1, y=2, **kwargs) } // and even with `kwargs` iterable

Some special cases might still use the tuple-and-dict convention:

py_call!{ obj(*args) }  // call obj with args
py_call!{ obj(**kwargs) }  // call obj with kwargs
py_call!{ obj(*args, **kwargs) }  // call obj with args and kwargs

I wonder if we would extend this to method calls:

py_call!{ obj.method(*args) }  // call obj.method with args
// etc

I suspect that this macro would prefer to be a proc-macro for better error messages etc. The implementation... would probably be a jumble of interesting traits.

I honestly have no idea how easy / hard the implementation would be, but I'm excited that this might create a nice user experience and also be a win for performance!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing .call(args, kwargs), .call1(args), and .call0() APIs and the issue's vectorcall examples. Compare the proposed syntax and method-call extension with the current calling conventions and assess the proc-macro and trait design. Done would require an agreed scope and an implemented, efficient macro API with coverage for the listed call forms.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
api, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.