blob: 24883c04c0c30f0c55319522abec88f367e35bee [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Any
import pytest
from tvm_ffi import get_global_func_metadata, register_global_func, remove_global_func
from tvm_ffi.core import TypeInfo, TypeSchema, _lookup_type_attr
from tvm_ffi.testing import _SchemaAllTypes
def _replace_container_types(ty: str) -> str:
return {
"Array": "Sequence",
"List": "MutableSequence",
"Map": "Mapping",
"Dict": "MutableMapping",
}.get(ty, ty)
@pytest.mark.parametrize(
"func_name,expected",
[
("testing.schema_id_int", "Callable[[int], int]"),
("testing.schema_id_float", "Callable[[float], float]"),
("testing.schema_id_bool", "Callable[[bool], bool]"),
("testing.schema_id_device", "Callable[[Device], Device]"),
("testing.schema_id_dtype", "Callable[[dtype], dtype]"),
("testing.schema_id_string", "Callable[[str], str]"),
("testing.schema_id_bytes", "Callable[[bytes], bytes]"),
("testing.schema_id_func", "Callable[[Callable[..., Any]], Callable[..., Any]]"),
(
"testing.schema_id_func_typed",
"Callable[[Callable[[int, float, Callable[..., Any]], None]], Callable[[int, float, Callable[..., Any]], None]]",
),
("testing.schema_id_any", "Callable[[Any], Any]"),
("testing.schema_id_object", "Callable[[Object], Object]"),
("testing.schema_id_dltensor", "Callable[[Tensor], Tensor]"),
("testing.schema_id_tensor", "Callable[[Tensor], Tensor]"),
("testing.schema_tensor_view_input", "Callable[[Tensor], None]"),
("testing.schema_id_opt_int", "Callable[[int | None], int | None]"),
("testing.schema_id_opt_str", "Callable[[str | None], str | None]"),
("testing.schema_id_opt_obj", "Callable[[Object | None], Object | None]"),
("testing.schema_id_arr_int", "Callable[[Array[int]], Array[int]]"),
("testing.schema_id_arr_str", "Callable[[Array[str]], Array[str]]"),
("testing.schema_id_arr_obj", "Callable[[Array[Object]], Array[Object]]"),
("testing.schema_id_arr", "Callable[[Array[Any]], Array[Any]]"),
("testing.schema_id_map_str_int", "Callable[[Map[str, int]], Map[str, int]]"),
("testing.schema_id_map_str_str", "Callable[[Map[str, str]], Map[str, str]]"),
("testing.schema_id_map_str_obj", "Callable[[Map[str, Object]], Map[str, Object]]"),
("testing.schema_id_map", "Callable[[Map[Any, Any]], Map[Any, Any]]"),
("testing.schema_id_dict_str_int", "Callable[[Dict[str, int]], Dict[str, int]]"),
("testing.schema_id_dict_str_str", "Callable[[Dict[str, str]], Dict[str, str]]"),
("testing.schema_id_variant_int_str", "Callable[[int | str], int | str]"),
("testing.schema_packed", "Callable[..., Any]"),
(
"testing.schema_arr_map_opt",
"Callable[[Array[int | None], Map[str, Array[int]], str | None], Map[str, Array[int]]]",
),
(
"testing.schema_variant_mix",
"Callable[[int | str | Array[int]], int | str | Array[int]]",
),
("testing.schema_no_args", "Callable[[], int]"),
("testing.schema_no_return", "Callable[[int], None]"),
("testing.schema_no_args_no_return", "Callable[[], None]"),
],
)
def test_schema_global_func(func_name: str, expected: str) -> None:
metadata: dict[str, Any] = get_global_func_metadata(func_name)
actual: TypeSchema = TypeSchema.from_json_str(metadata["type_schema"])
assert str(actual) == expected, f"{func_name}: {actual}"
assert actual.repr(_replace_container_types) == (
expected.replace("Array", "Sequence")
.replace("List", "MutableSequence")
.replace("Map", "Mapping")
.replace("Dict", "MutableMapping")
)
@pytest.mark.parametrize(
"field_name,expected",
[
("v_bool", "bool"),
("v_int", "int"),
("v_float", "float"),
("v_device", "Device"),
("v_dtype", "dtype"),
("v_string", "str"),
("v_bytes", "bytes"),
("v_opt_int", "int | None"),
("v_opt_str", "str | None"),
("v_arr_int", "Array[int]"),
("v_arr_str", "Array[str]"),
("v_map_str_int", "Map[str, int]"),
("v_map_str_arr_int", "Map[str, Array[int]]"),
("v_variant", "str | Array[int] | Map[str, int]"),
("v_opt_arr_variant", "Array[int | str] | None"),
],
)
def test_schema_field(field_name: str, expected: str) -> None:
type_info: TypeInfo = getattr(_SchemaAllTypes, "__tvm_ffi_type_info__")
for field in type_info.fields:
if field.name == field_name:
actual: TypeSchema = TypeSchema.from_json_str(field.metadata["type_schema"])
assert str(actual) == expected, f"{field_name}: {actual}"
assert actual.repr(_replace_container_types) == (
expected.replace("Array", "Sequence")
.replace("List", "MutableSequence")
.replace("Map", "Mapping")
.replace("Dict", "MutableMapping")
)
break
else:
raise ValueError(f"Field not found: {field_name}")
@pytest.mark.parametrize(
"method_name,expected",
[
("add_int", "Callable[[testing.SchemaAllTypes, int], int]"),
("append_int", "Callable[[testing.SchemaAllTypes, Array[int], int], Array[int]]"),
("maybe_concat", "Callable[[testing.SchemaAllTypes, str | None, str | None], str | None]"),
(
"merge_map",
"Callable[[testing.SchemaAllTypes, Map[str, Array[int]], Map[str, Array[int]]], Map[str, Array[int]]]",
),
("make_with", "Callable[[int, float, str], testing.SchemaAllTypes]"),
],
)
def test_schema_member_method(method_name: str, expected: str) -> None:
type_info: TypeInfo = getattr(_SchemaAllTypes, "__tvm_ffi_type_info__")
for method in type_info.methods:
if method.name == method_name:
actual: TypeSchema = TypeSchema.from_json_str(method.metadata["type_schema"])
assert str(actual) == expected, f"{method_name}: {actual}"
assert actual.repr(_replace_container_types) == (
expected.replace("Array", "Sequence")
.replace("List", "MutableSequence")
.replace("Map", "Mapping")
.replace("Dict", "MutableMapping")
)
break
else:
raise ValueError(f"Method not found: {method_name}")
def test_metadata_global_func() -> None:
metadata: dict[str, Any] = get_global_func_metadata("testing.schema_id_int")
assert len(metadata) == 4
assert "type_schema" in metadata
assert metadata["bool_attr"] is True
assert metadata["int_attr"] == 1
assert metadata["str_attr"] == "hello"
def test_metadata_empty_for_python_func() -> None:
@register_global_func("test.python_func_no_metadata")
def simple_func(x: int) -> int:
return x + 1
metadata = get_global_func_metadata("test.python_func_no_metadata")
assert metadata == {}
remove_global_func("test.python_func_no_metadata")
def test_metadata_field() -> None:
type_info: TypeInfo = getattr(_SchemaAllTypes, "__tvm_ffi_type_info__")
for field in type_info.fields:
if field.name == "v_bool":
assert len(field.metadata) == 4
assert "type_schema" in field.metadata
assert field.metadata["bool_attr"] is True
assert field.metadata["int_attr"] == 1
assert field.metadata["str_attr"] == "hello"
break
else:
raise ValueError("Field not found: v_bool")
def test_metadata_member_method() -> None:
type_info: TypeInfo = getattr(_SchemaAllTypes, "__tvm_ffi_type_info__")
for method in type_info.methods:
if method.name == "add_int":
assert len(method.metadata) == 4
assert "type_schema" in method.metadata
assert method.metadata["bool_attr"] is True
assert method.metadata["int_attr"] == 1
assert method.metadata["str_attr"] == "hello"
break
else:
raise ValueError("Method not found: add_int")
def test_mem_fn_as_global_func() -> None:
metadata: dict[str, Any] = get_global_func_metadata("testing.TestIntPairSum")
type_schema: TypeSchema = TypeSchema.from_json_str(metadata["type_schema"])
assert str(type_schema) == "Callable[[testing.TestIntPair], int]"
def test_class_metadata_none() -> None:
assert _lookup_type_attr(64, "__metadata__") is None