This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 1007269 [FEAT] Streamline AccessPath/AccessStep print format (#598)
1007269 is described below
commit 1007269cd9e243fc9a20a2026a79503ab30e2b58
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu May 28 13:33:31 2026 -0400
[FEAT] Streamline AccessPath/AccessStep print format (#598)
## Summary
Register a custom `__ffi_repr__` for `ffi::reflection::AccessPath` and
`AccessStep` so their formatted output is a concise, human-readable path
expression:
```
<root>.config.layers[0][<missing:"weights">]
```
Per-step rendering:
- root: `<root>`
- attr: `.name`
- array item: `[index]`
- map item: `[<repr(key)>]` (string keys quoted)
- missing attr: `[<missing:"name">]`
- missing array: `[<missing:index>]`
- missing map: `[<missing:<repr(key)>>]`
The formatter delegates key quoting to the existing `EscapedStringPy`
path via `fn_repr(step->key)`, so non-string map keys are also handled
robustly.
---
src/ffi/extra/reflection_extra.cc | 50 ++++++++++++++++++++++++++++++++
tests/cpp/extra/test_access_path_repr.cc | 41 ++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/src/ffi/extra/reflection_extra.cc
b/src/ffi/extra/reflection_extra.cc
index 08b6511..f3930de 100644
--- a/src/ffi/extra/reflection_extra.cc
+++ b/src/ffi/extra/reflection_extra.cc
@@ -26,6 +26,8 @@
#include <tvm/ffi/reflection/accessor.h>
#include <tvm/ffi/reflection/registry.h>
+#include <sstream>
+
namespace tvm {
namespace ffi {
namespace reflection {
@@ -104,6 +106,41 @@ inline void AccessStepRegisterReflection() {
refl::ObjectDef<AccessStepObj>(refl::init(false))
.def_ro("kind", &AccessStepObj::kind)
.def_ro("key", &AccessStepObj::key);
+ // Register __ffi_repr__ for AccessStep: format one step fragment.
+ // kAttr -> ".name"
+ // kArrayItem -> "[index]"
+ // kMapItem -> "[<repr(key)>]" (string keys are quoted via
fn_repr)
+ // kAttrMissing -> "[<missing:"name">]"
+ // kArrayItemMissing -> "[<missing:index>]"
+ // kMapItemMissing -> "[<missing:<repr(key)>>]"
+ refl::TypeAttrDef<AccessStepObj>().def(
+ refl::type_attr::kRepr,
+ [](const AccessStep& step, const ffi::Function& fn_repr) -> ffi::String {
+ std::ostringstream os;
+ switch (step->kind) {
+ case AccessKind::kAttr:
+ os << "." << step->key.cast<ffi::String>();
+ break;
+ case AccessKind::kArrayItem:
+ os << "[" << step->key.cast<int64_t>() << "]";
+ break;
+ case AccessKind::kMapItem:
+ os << "[" << fn_repr(step->key).cast<ffi::String>() << "]";
+ break;
+ case AccessKind::kAttrMissing:
+ os << "[<missing:" << fn_repr(step->key).cast<ffi::String>() <<
">]";
+ break;
+ case AccessKind::kArrayItemMissing:
+ os << "[<missing:" << step->key.cast<int64_t>() << ">]";
+ break;
+ case AccessKind::kMapItemMissing:
+ os << "[<missing:" << fn_repr(step->key).cast<ffi::String>() <<
">]";
+ break;
+ default:
+ TVM_FFI_UNREACHABLE();
+ }
+ return os.str();
+ });
}
inline void AccessPathRegisterReflection() {
@@ -125,6 +162,19 @@ inline void AccessPathRegisterReflection() {
.def("_to_steps", &AccessPathObj::ToSteps)
.def("_path_equal",
[](const AccessPath& self, const AccessPath& other) { return
self->PathEqual(other); });
+ // Register __ffi_repr__ for AccessPath: flatten via ToSteps() and walk the
resulting vector.
+ // Root (depth == 0) emits "<root>"; non-root nodes concatenate each step's
fragment.
+ refl::TypeAttrDef<AccessPathObj>().def(
+ refl::type_attr::kRepr,
+ [](const AccessPath& path, const ffi::Function& fn_repr) -> ffi::String {
+ Array<AccessStep> steps = path->ToSteps();
+ std::ostringstream os;
+ os << "<root>";
+ for (const AccessStep& step : steps) {
+ os << fn_repr(step).cast<ffi::String>();
+ }
+ return os.str();
+ });
}
int64_t StructuralKeyHash(const Any& key) {
diff --git a/tests/cpp/extra/test_access_path_repr.cc
b/tests/cpp/extra/test_access_path_repr.cc
new file mode 100644
index 0000000..c6cde05
--- /dev/null
+++ b/tests/cpp/extra/test_access_path_repr.cc
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/extra/dataclass.h>
+#include <tvm/ffi/reflection/access_path.h>
+
+namespace {
+
+TEST(AccessPathRepr, StreamlinedFormat) {
+ using namespace tvm::ffi;
+ using namespace tvm::ffi::reflection;
+ AccessPath p = AccessPath::Root()
+ ->Attr("config")
+ ->Attr("layers")
+ ->ArrayItem(0)
+ ->MapItem(String("name"))
+ ->AttrMissing("weights")
+ ->ArrayItemMissing(3)
+ ->MapItemMissing(String("bias"));
+ EXPECT_EQ(std::string(ReprPrint(Any(p)).c_str()),
+ "<root>.config.layers[0][\"name\"]"
+ "[<missing:\"weights\">][<missing:3>][<missing:\"bias\">]");
+}
+
+} // namespace