Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to include bare ffi methods in a #[pyclass] #3949

Open
ariebovenberg opened this issue Mar 10, 2024 · 1 comment
Open

Ability to include bare ffi methods in a #[pyclass] #3949

ariebovenberg opened this issue Mar 10, 2024 · 1 comment

Comments

@ariebovenberg
Copy link

As described in issues #3827 and #3843 there is some (unavoidable) overhead in calling PyO3 wrapped functions. An idea: the ability to fall back on lower level pyo3_ffi code for certain methods where you'd like to avoid this overhead.

Why is this important? Often, classes have small methods that are called a lot (e.g. magic methods). In my case, I'm porting Python classes with many such short methods.

related: this thread on discord

davidhewitt: Perhaps worth opening as a PyO3 issue? I could at least spitball some ideas on syntax and implementation and then maybe someone is interested in giving it a crack

@ariebovenberg ariebovenberg changed the title Ability to include bare' ffi methods in a #[pyclass] Ability to include bare ffi methods in a #[pyclass] Mar 10, 2024
@davidhewitt
Copy link
Member

Sorry for being a bit slow to reply here. Overall, my take is that I can see why users would want this, and I'm open to supporting it if there's a good design and sufficient motivation. Though I think personally my priority would be to understand what the framework overheads are which affect users and work to eliminate those, so that reaching into unsafe Rust doesn't seems necessary.

So I guess that this could look something like this:

use pyo3::ffi;

#[pyclass]
struct Foo;

#[pymethods]
impl Foo {
    #[pyo3(raw_method = METH_VARARGS | METH_KWARGS)]  // specify the calling convention
    unsafe fn raw_method(args: *mut ffi::PyObject, kwargs: *mut ffi::PyObject) -> *mut ffi::PyObject {
        // insert code here
    }

    #[pyo3(raw_slot_method = tp_len)] // or specify a protocol slot
    unsafe fn tp_len(slf: *mut ffi::PyObject) -> ffi::Py_ssize_t {
        // insert code here
    }
}

There's a few things to consider here:

  • We might want to make PyO3's argument extraction code public API too, as it's pretty useful. Though maybe a good reason to skip the framework would be to use bespoke extraction when you don't care about things like fancy error handling.
  • While GIL refs are still around, setting up the GILPool is a necessary soundness mechanism, so skipping the framework requires that you'd need to type out this boilerplate.
  • The other main PyO3 framework step is to update our "ReferencePool" which is used for PyObject drop/clone without the GIL held. There are perhaps ways that we can make this cheaper (e.g. maybe this problem goes away when Python 3.13 freethreaded Python lands)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants