Skip to content

py_loader: implement metacall_await in the python port - #955

Open
bhuvan-somisetty wants to merge 3 commits into
metacall:developfrom
bhuvan-somisetty:feat/py-port-await
Open

bhuvan-somisetty wants to merge 3 commits into
metacall:developfrom
bhuvan-somisetty:feat/py-port-await

Conversation

@bhuvan-somisetty

@bhuvan-somisetty bhuvan-somisetty commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Implements metacall_await in the python port, the stub that was under #if 0 in py_loader_port.c. Scope agreed with viferga on Discord (2026-11-11).

Approach

metacall_await(name, *args) returns a concurrent.futures.Future. The caller can block on f.result() while the loader runs the async function and settles the future from its own thread, through the resolve and reject callbacks of metacall_await_s.

  • concurrent.futures.Future is thread safe, so the callbacks take the GIL and call set_result / set_exception directly, no loop helpers needed
  • a rejected value that is not an exception is wrapped into a RuntimeError
  • the callbacks own one reference to the future and release it after settling
  • if metacall_await_s returns NULL, a RuntimeError is raised; the returned value is destroyed right away
  • metacall_await is exported from api.py and __init__.py

Commits

  1. tests: enable metacall_python_await_test
  2. tests: use the sync form in metacall_python_await_test: f = metacall_await('yeet', 1234); f.result(timeout=10)
  3. py_loader: implement metacall_await in the python port

Tests

Local Windows MSVC Debug build (Python 3.10): metacall-python-await-test and metacall-python-async-test pass, no build warnings. clang-format 21 check passes.

Open points

  • only the sync form for now. await metacall_await(...) inside a coroutine needs asyncio.wrap_future, planned as a follow up
  • not run under valgrind/sanitizers locally, relying on CI for that

AI-Assisted
Claude Code

Enable the test for metacall_await in the Python port and fix two bugs
in its script: threading was not imported, and result was assigned as a
local inside test() instead of the global. It fails for now because the
Python port does not implement metacall_await yet.
metacall_await from the python port will return a concurrent future,
so the caller blocks on result() while the background loop runs the
coroutine. Drop asyncio.run, the lock and the global, and add a
timeout so the test can not hang if the future never resolves.
metacall_await returns a concurrent.futures.Future. The caller can block
on result() while the loader runs the async function and settles the
future from its own thread, through the resolve and reject callbacks of
metacall_await_s. concurrent.futures.Future is thread safe, so the
callbacks call set_result and set_exception directly. A rejected value
that is not an exception is wrapped into a RuntimeError.

This branch has not been deployed

No deployments
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.

1 participant