Skip to content

feat(python): add Function.__init__ constructor for direct callable wrapping#492

Draft
junrushao wants to merge 1 commit intoapache:mainfrom
junrushao:2026-03-01/func-init
Draft

feat(python): add Function.__init__ constructor for direct callable wrapping#492
junrushao wants to merge 1 commit intoapache:mainfrom
junrushao:2026-03-01/func-init

Conversation

@junrushao
Copy link
Member

@junrushao junrushao commented Mar 1, 2026

Summary

  • Add Function.__init__ constructor to the Cython Function class, enabling direct construction via tvm_ffi.Function(callable) instead of requiring tvm_ffi.convert(callable)
  • Support three input modes: wrap a plain Python callable, copy-construct from an existing Function (with ref-count increment), or construct with no arguments for backward compatibility (null handle)
  • Reject non-callable inputs with TypeError and moved-from Function objects with ValueError

Architecture

  • Adds __init__ to the Cython Function extension type in function.pxi. For plain callables, delegates to the existing _convert_to_ffi_func_handle helper. For Function-to-Function copies, increments the C-level ref count via TVMFFIObjectIncRef and shares the handle.
  • func parameter defaults to None for backward compatibility: when omitted the object remains in its default null-handle state (matching the prior behavior where __cinit__ left chandle = NULL).
  • No changes to the C ABI, C++ layer, or global registry.

Public Interfaces

  • New: tvm_ffi.Function(func: Callable[..., Any] | None = None) -> Function constructor.
  • Type stub updated in core.pyi.

Behavioral Changes

  • Previously, tvm_ffi.Function could only be obtained via tvm_ffi.convert() or the global function registry. Now it can be constructed directly.
  • Copy-constructing from a Function produces a second ref to the same underlying packed function (verified via same_as).
  • Calling Function() with no arguments preserves backward compatibility by returning a null-handle instance identical to the prior default construction.

Tests

  • test_pyfunc_init covers: wrapping a Python callable, copy-constructing from Function, ValueError on moved-from source, no-arg null-handle backward compat, TypeError on non-callable input.
  • Moved-from test uses __new__ to avoid leaking a handle that would be immediately overwritten by __move_handle_from__.

Untested Edge Cases

  • Thread-safety of concurrent __init__ calls (covered by GIL in CPython).
  • Interaction with release_gil property on the newly constructed function (inherits default from __cinit__).

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the tvm_ffi.Function class by providing a direct constructor, simplifying the process of wrapping Python callables or duplicating existing Function objects. This change improves usability by offering a more intuitive initialization path, moving beyond reliance on tvm_ffi.convert() or the global function registry, while maintaining robust error handling for invalid inputs.

Highlights

  • Direct Function Initialization: Introduced a Function.__init__ constructor in the Cython Function class, allowing direct creation from Python callables or existing Function instances.
  • Input Handling: Implemented robust input validation, raising TypeError for non-callable inputs and ValueError for attempts to initialize from moved-from Function objects.
  • Reference Counting: Ensured proper reference counting when copy-constructing from an existing Function by incrementing the C-level reference count.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • python/tvm_ffi/core.pyi
    • Added the __init__ method signature to the Function class.
  • python/tvm_ffi/cython/function.pxi
    • Implemented the __init__ method for the Function Cython class.
    • Added logic to handle initialization from Python callables or existing Function instances.
    • Included error handling for non-callable inputs and moved-from Function objects.
    • Managed C-level reference counting for Function copies.
  • tests/python/test_function.py
    • Added test_pyfunc_init to verify the new Function.__init__ constructor's behavior.
    • Included tests for wrapping callables, copy-construction, and error cases.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a convenient __init__ constructor for tvm_ffi.Function, allowing direct instantiation from callables. The implementation correctly handles both new callables and copy-construction from existing Function objects, including checks for moved-from instances. The accompanying tests are thorough and cover the main use cases and error conditions. I've found a minor memory leak in one of the new tests and suggested a fix. Overall, this is a great addition that improves the usability of the FFI.

@junrushao junrushao force-pushed the 2026-03-01/func-init branch from 749bb17 to 1ff8b85 Compare March 1, 2026 23:40
…rapping

Architecture:
- Adds `__init__` to the Cython `Function` extension type in `function.pxi`.
  For plain callables, delegates to the existing `_convert_to_ffi_func_handle`
  helper. For `Function`-to-`Function` copies, increments the C-level ref
  count via `TVMFFIObjectIncRef` and shares the handle.
- `func` parameter defaults to `None` for backward compatibility: when omitted
  the object remains in its default null-handle state (matching the prior
  behavior where `__cinit__` left `chandle = NULL`).
- No changes to the C ABI, C++ layer, or global registry.

Public Interfaces:
- New: `tvm_ffi.Function(func=None)` constructor accepting any Python callable,
  an existing `Function`, or `None` (default, null handle).
- Type stub updated in `core.pyi` with `Callable[..., Any] | None = None`.

UI/UX:
- none

Behavioral Changes:
- Previously `tvm_ffi.Function` could only be obtained via `tvm_ffi.convert()`
  or the global function registry. Now it can be constructed directly.
- Copy-constructing from a `Function` produces a second ref to the same
  underlying packed function (verified via `same_as`).
- Calling `Function()` with no arguments preserves backward compatibility by
  returning a null-handle instance identical to the prior default construction.

Docs:
- Cython docstring and `.pyi` stub updated. No standalone doc page exists for
  `Function.__init__`; the stub is the primary surface.

Tests:
- Executed: `uv run pytest -vvs tests/python/test_function.py::test_pyfunc_init`
- Result: PASSED (covers callable wrapping, copy-construct, moved-from
  ValueError, no-arg null-handle backward compat, non-callable TypeError)
- Fixed test handle leak: moved-from test now uses `__new__` to avoid
  allocating a handle that gets immediately overwritten by `__move_handle_from__`.

Untested Edge Cases:
- Thread-safety of concurrent `__init__` calls (covered by GIL in CPython).
- Interaction with `release_gil` property on newly constructed function
  (inherits default from `__cinit__`).
@junrushao junrushao force-pushed the 2026-03-01/func-init branch from 1ff8b85 to ec4ab4c Compare March 1, 2026 23:42
@junrushao junrushao mentioned this pull request Mar 1, 2026
10 tasks
@junrushao junrushao requested a review from tqchen March 1, 2026 23:44
def __cinit__(self) -> None:
self.c_release_gil = _RELEASE_GIL_BY_DEFAULT

def __init__(self, func: Optional[Callable[..., Any]] = None) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

let us wait until cutedsl release cycles fixes the compact issue, the land this one

@junrushao junrushao marked this pull request as draft March 2, 2026 22:24
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