junrushao commented on code in PR #663:
URL: https://github.com/apache/tvm-ffi/pull/663#discussion_r3574337253
##########
python/tvm_ffi/_dunder.py:
##########
@@ -104,6 +114,22 @@ def __init__(self: Any, *args: Any, **kwargs: Any) -> None:
return __init__
+def _collect_init_property_funcs(type_cls: type) -> list[tuple[str,
Callable[..., Any]]]:
+ """Collect ``(name, func)`` pairs for all ``init_property`` fields in MRO
order.
+
+ Walks from the most specific class toward the root. Child definitions take
+ precedence over parent definitions of the same name (first-seen wins).
+ """
+ funcs: list[tuple[str, Callable[..., Any]]] = []
+ seen: set[str] = set()
+ for cls in type_cls.__mro__:
+ for name, func in cls.__dict__.get("__ffi_init_property_funcs__",
{}).items():
+ if name not in seen:
+ funcs.append((name, func))
+ seen.add(name)
+ return funcs
Review Comment:
Fixed in 36ee686. _collect_init_property_funcs now walks the reversed MRO
into an insertion-ordered dict, so parent properties are initialized before
child properties while child definitions still replace inherited ones. I added
regressions for a child property depending on a parent property and for child
override selection. The complete test_dataclass_py_class.py file passes (386
passed, 1 version-gated skip on Python 3.13), and the Python 3.14
TestInitProperty group passes 9/9. Thanks!
##########
python/tvm_ffi/utils/descriptors.py:
##########
@@ -0,0 +1,61 @@
+# 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.
+"""Descriptor utilities for the tvm_ffi Python package."""
+
+from __future__ import annotations
+
+from typing import Any, Callable, Generic, TypeVar, overload
+
+_T = TypeVar("_T")
+
+
+class init_property(Generic[_T]):
+ """Auto-registered C++ field with eager computation at ``__init__`` time.
+
+ ``@py_class`` detects ``init_property`` descriptors and registers each one
+ as ``field(init=False, structural_eq="ignore")``, so the computed value
+ lives in C++ object storage and is accessible cross-language. The value
+ is computed once — immediately after ``__ffi_init__`` — and stored via the
+ field's C++ slot. Subsequent reads go directly to C++ memory.
+
+ The return annotation of the decorated function is injected into the class
+ ``__annotations__`` during class body execution so the field resolution
+ machinery picks it up automatically. If no return annotation is present,
+ ``typing.Any`` is used.
+ """
+
+ def __init__(self, func: Callable[[Any], _T]) -> None:
+ self.func = func
+ self.name: str | None = None
+ self._return_annotation: Any = func.__annotations__.get("return")
+
+ @overload
+ def __get__(self, obj: None, objtype: type) -> init_property[_T]: ...
+ @overload
+ def __get__(self, obj: object, objtype: type) -> _T: ...
+ def __get__(self, obj: object | None, objtype: type) -> _T |
init_property[_T]:
+ return self
+
+ def __set_name__(self, owner: type, name: str) -> None:
+ self.name = name
+
+ ann = self._return_annotation if self._return_annotation is not None
else Any
+ # Inject into the owner's own __annotations__ so on_fields_resolved
+ # processes this name as a typed field.
+ if "__annotations__" not in owner.__dict__:
+ owner.__annotations__ = {}
+ owner.__annotations__[name] = ann
Review Comment:
Fixed in 36ee686. On Python 3.14+, both the decorated function return
annotation and the owner annotations now use annotationlib.get_annotations with
Format.FORWARDREF, then the computed field is merged into the complete mapping.
I used this instead of directly mutating owner.__annotations__ because direct
access can eagerly evaluate unresolved self or forward references during
__set_name__. I added a no-future PEP 749 regression preserving a normal field,
an unresolved self-referential field, and the init_property return annotation.
The Python 3.14 TestInitProperty group passes 9/9. Thanks!
--
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]