gemini-code-assist[bot] commented on code in PR #663:
URL: https://github.com/apache/tvm-ffi/pull/663#discussion_r3574212180
##########
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:

In `_collect_init_property_funcs`, walking the MRO from child to parent
(most specific to least specific) means child properties are evaluated before
parent properties. If a child's `init_property` depends on a parent's
`init_property` (or parent fields), it will be evaluated with
uninitialized/default values. Walking the MRO in reverse (from root to child)
using a dictionary ensures parent properties are evaluated first, while still
allowing child definitions to correctly override parent definitions of the same
name.
```python
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 root toward the most specific class so that parent
properties
are evaluated before child properties (allowing dependent properties to
work).
Child definitions override parent definitions of the same name.
"""
funcs_dict: dict[str, Callable[..., Any]] = {}
for cls in reversed(type_cls.__mro__):
for name, func in cls.__dict__.get("__ffi_init_property_funcs__",
{}).items():
funcs_dict[name] = func
return list(funcs_dict.items())
```
##########
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:

In Python 3.14+ (PEP 749), `__annotations__` is a descriptor on the class
that lazily evaluates annotations via `__annotate__`. Assigning
`owner.__annotations__ = {}` replaces this descriptor with a plain dictionary,
which completely shadows and loses any other annotations declared on the class.
Since `__annotations__` is no longer inherited in Python 3.14+, we can safely
mutate the dictionary returned by `owner.__annotations__` directly without
affecting parent classes, preserving other annotations.
```suggestion
import sys
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 sys.version_info >= (3, 14):
owner.__annotations__[name] = ann
else:
if "__annotations__" not in owner.__dict__:
owner.__annotations__ = {}
owner.__annotations__[name] = ann
```
--
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]