gemini-code-assist[bot] commented on PR #36:
URL: https://github.com/apache/tvm-ffi/pull/36#issuecomment-3367433723
This PR introduces a comprehensive system for generating and attaching
JSON-based type schemas to FFI-exposed entities in TVM, including global
functions, object fields, and object methods. This enhancement provides richer,
machine-readable type information, which can be invaluable for tooling, static
analysis, and dynamic dispatch mechanisms built on top of the TVM FFI.
**Key Changes:**
1. **JSON-based Type Schema Generation (C++):**
* Introduced `tvm::ffi::details::TypeSchema<T>` template to generate a
JSON string representing the type `T`. This is implemented for various
fundamental types (e.g., `int`, `float`, `bool`, `String`, `Bytes`,
`DLDataType`, `DLDevice`) and complex container types (e.g., `Array<T>`,
`Map<K, V>`, `Optional<T>`, `Tuple<Args...>`, `Variant<Args...>`).
* For functions, `tvm::ffi::TypedFunction<R(Args...)>::TypeSchema()`
and `tvm::ffi::details::FuncFunctorImpl` now provide JSON schemas that include
return type and argument types.
* A new `tvm::ffi::EscapeString` utility is added for proper JSON
string escaping.
2. **Enhanced Reflection Metadata (C++):**
* The `tvm::ffi::reflection::Metadata` class is introduced, allowing
arbitrary key-value pairs to be attached to fields and methods during
registration.
* `FieldInfoBuilder` and `MethodInfoBuilder` are added to temporarily
hold this metadata before it's serialized into the `TVMFFIFieldInfo` and
`TVMFFIMethodInfo` C structs.
* The `type_schema` generated by `TypeSchema<T>` is automatically
included as a metadata entry for all registered fields, methods, and global
functions.
3. **Python API for Schema Access:**
* A new `tvm_ffi.core.TypeSchema` Python class is added, capable of
parsing the JSON schema strings into a structured Python object, providing a
more convenient way to inspect type information.
* `tvm_ffi.core.TypeField` and `tvm_ffi.core.TypeMethod` now include a
`metadata` dictionary, making the attached JSON metadata accessible from Python.
* A new Python function `tvm_ffi.get_global_func_metadata(name: str)`
is exposed to retrieve the full metadata (including `type_schema`) for any
registered global function.
* Registered Python classes now have a `__tvm_ffi_type_info__`
attribute, providing access to their fields and methods, each with their
associated metadata.
**Concrete Examples:**
**C++ Examples:**
```cpp
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/map.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ffi/string.h>
#include <tvm/ffi/type_traits.h>
namespace tvm {
namespace ffi {
namespace reflection {
// Example 1: Generating a type schema for a simple type
std::string int_schema = details::TypeSchema<int>::v();
// int_schema will be: "{\"type\":\"int\"}"
// Example 2: Generating a type schema for a complex type
std::string array_int_schema = details::TypeSchema<Array<int64_t>>::v();
// array_int_schema will be:
"{\"type\":\"ffi.Array\",\"args\":[{\"type\":\"int\"}]}"
// Example 3: Registering a global function with custom metadata and type
schema
int64_t my_add_func(int64_t a, int64_t b) { return a + b; }
TVM_FFI_STATIC_INIT_BLOCK() {
GlobalDef()
.def("my_add", my_add_func,
Metadata{{"description", "Adds two integers"}, {"version", 1}});
}
// The 'type_schema' for "my_add" will be automatically generated and
included in its metadata.
// You can retrieve it using the ffi.GetGlobalFuncMetadata C++ API (exposed
to Python).
// Example 4: Registering a field with custom metadata and type schema
class MyObjectObj : public Object {
public:
int my_field;
TVM_FFI_DECLARE_OBJECT_INFO("my.MyObject", MyObjectObj, Object);
};
class MyObject : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(MyObject, ObjectRef,
MyObjectObj);
};
TVM_FFI_STATIC_INIT_BLOCK() {
ObjectDef<MyObjectObj>()
.def_rw("my_field", &MyObjectObj::my_field, "A simple integer field",
Metadata{{"unit", "count"}});
}
// The 'type_schema' for "my_field" will be automatically generated and
included in its metadata.
// You can retrieve it using GetFieldInfo (as shown in
tests/cpp/test_metadata.cc).
} // namespace reflection
} // namespace ffi
} // namespace tvm
```
**Python Examples:**
```python
import json
from typing import Any, Callable
from tvm_ffi import get_global_func_metadata, register_object
from tvm_ffi.core import TypeInfo, TypeSchema
# Example 1: Accessing global function metadata and type schema
# (Assuming 'testing.schema_id_int' is registered in C++ as in
src/ffi/extra/testing.cc)
metadata: dict[str, Any] = get_global_func_metadata("testing.schema_id_int")
# metadata will contain (example values):
# {
# "type_schema":
"{\"type\":\"ffi.Function\",\"args\":[{\"type\":\"int\"},{\"type\":\"int\"}]}",
# "bool_attr": True,
# "int_attr": 1,
# "str_attr": "hello"
# }
schema_str: str = metadata["type_schema"]
type_schema_obj: TypeSchema = TypeSchema.from_json_str(schema_str)
print(f"Schema for testing.schema_id_int: {type_schema_obj}")
# Expected output: Schema for testing.schema_id_int: Callable[[int], int]
# Example 2: Accessing object field metadata and type schema
@register_object("testing.MyPythonObject")
class MyPythonObject:
my_int_field: int
my_str_field: str
# After registration, type_info is available
type_info: TypeInfo = getattr(MyPythonObject, "__tvm_ffi_type_info__")
# Find the field "my_int_field"
int_field_schema_str = ""
for field in type_info.fields:
if field.name == "my_int_field":
int_field_schema_str = field.metadata["type_schema"]
break
int_field_type_schema = TypeSchema.from_json_str(int_field_schema_str)
print(f"Schema for MyPythonObject.my_int_field: {int_field_type_schema}")
# Expected output: Schema for MyPythonObject.my_int_field: int
# Example 3: Accessing object method metadata and type schema
# (Assuming 'add_int' is a method registered on 'testing.SchemaAllTypes' in
C++ as in src/ffi/extra/testing.cc)
from tvm_ffi.testing import _SchemaAllTypes
schema_all_types_info: TypeInfo = getattr(_SchemaAllTypes,
"__tvm_ffi_type_info__")
add_int_method_schema_str = ""
for method in schema_all_types_info.methods:
if method.name == "add_int":
add_int_method_schema_str = method.metadata["type_schema"]
break
add_int_method_type_schema =
TypeSchema.from_json_str(add_int_method_schema_str)
print(f"Schema for _SchemaAllTypes.add_int: {add_int_method_type_schema}")
# Expected output: Schema for _SchemaAllTypes.add_int: Callable[[int], int]
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]