Skip to content

Add minimal C-api implementation that builds with Pyo3#7562

Draft
bschoenmaeckers wants to merge 24 commits intoRustPython:mainfrom
bschoenmaeckers:c-api
Draft

Add minimal C-api implementation that builds with Pyo3#7562
bschoenmaeckers wants to merge 24 commits intoRustPython:mainfrom
bschoenmaeckers:c-api

Conversation

@bschoenmaeckers
Copy link
Copy Markdown

This is my shot at implementing a minimal Cpython compatible C-api. I've implemented the bare minimum to get the included Pyo3 example running where most of the api is stubbed. I'm not familiar with the rest of the RustPython code base so let me know what you think and where I did stupid things.

Please take extra care reviewing the pylifecycle.rs & pystate.rs files where I try to setup the RustPython interpreter.

xref #5604

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 5, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: b6b67fcb-38aa-448a-bce3-b40ce5bbf56a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@youknowone
Copy link
Copy Markdown
Member

Could you try with hpy if you dont mind? We are looking for sustainable way

@bschoenmaeckers
Copy link
Copy Markdown
Author

Could you try with hpy if you dont mind? We are looking for sustainable way

That would require significant changes in codebase that are using the CPython api spec. I would like to explore the possibility to use the ab3/abi3t api. Would you be willing to accept that?

Copy link
Copy Markdown
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way will not work.

Just in case, the code can be generated, but I hope we carefully review the decisions. unlikely-to-happen decisions must be reviewed and justified by document(comment) the decisions.
If you are not familiar enough to the project to decide good way, please start from a smaller and simpler issue.

We only need abi3t, because we are compatible to free-threading.

We have to minimize the surface of C API. if HPy helps it, we need HPy.

Ideally c api must be very thin wrappers to RustPython features. "code" must not be included in this crate.

use std::sync::LazyLock;

pub struct PyTypeObject {
ty: LazyLock<&'static Py<PyType>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause significant performance draw down.
PyType also already has flags inside.
Will you create all the new type wrapper for every static types? That will not work.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you create all the new type wrapper for every static types? That will not work.

I was just trying to get the static type objects working. These are static exported pointers so I came up with this. If you have a better idea to statically create pointers to the TypeZoo, please, I'm very open to suggestions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyType also already has flags inside.

I got rid of the flags field in PyTypeObject, and map the RustPythons PyTypeFlags to the format PyType_GetFlags expects. Note that this does currently not map all the available flags.

Copy link
Copy Markdown
Author

@bschoenmaeckers bschoenmaeckers Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static type pointers are now dynamically set at runtime. They are now basically a static pointer to a Py<PyType> instance. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good try 👍 That will be better.

use std::cell::RefCell;

thread_local! {
static VM: RefCell<Option<ThreadedVirtualMachine>> = const { RefCell::new(None) };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C API must be simple API set. It must not contains any state ideally. And never the VM.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VM_CURRENT static in vm/thread.rs is scoped. So I cannot set and unset it in PyGILState_Ensure/ PyGILState_Release. (These methods have nothing to do with the GIL in free-threaded world despite GIL in the name)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could use VM_STACK instead... 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Let's try to expose it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VM_STACK only stores pointers (NonNull<VirtualMachine>) to a vm, so I need some other place to store it. What do you recommend?

@bschoenmaeckers
Copy link
Copy Markdown
Author

Ideally c api must be very thin wrappers to RustPython features. "code" must not be included in this crate.

I wanted to create as little of modificaties to the existing code base without significant motivation. But I definitely agree with you.

let (response_tx, response_rx) = mpsc::channel();
tx.send(response_tx).expect("Failed to send VM request");
response_rx.recv().expect("Failed to receive VM response")
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also somewhat cursed. I want to be able to create ThreadedVirtualMachine scoped to the thread where PyGILState_Ensure is called. This may be from a different thread than where the main interpreter is running. So I need a way to share the Interpreter between different threads.

@youknowone
Copy link
Copy Markdown
Member

youknowone commented Apr 8, 2026

I wanted to create as little of modificaties to the existing code base without significant motivation. But I definitely agree with you.

That's actually a good view in general. In this case, C APIs must be jsut C API for real RustPython features.
If RustPython lacks features to support C API, we have to consider to add it inside the core crate. Not 100% sure. C API is broad and something must not be fit in this way. Let's investigate it if we can make it or not.

@bschoenmaeckers bschoenmaeckers force-pushed the c-api branch 3 times, most recently from 16f8074 to 850cd07 Compare April 8, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants