[Mlir-commits] [mlir] [MLIR][Python] Register `OpAttributeMap` as `Mapping` for `match` compatibility (PR #174292)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sat Jan 10 03:07:25 PST 2026
https://github.com/MaPePeR updated https://github.com/llvm/llvm-project/pull/174292
>From 86ed86690a0a32fd06373859882a1ad9a6beec2c Mon Sep 17 00:00:00 2001
From: MaPePeR <MaPePeR at users.noreply.github.com>
Date: Wed, 31 Dec 2025 19:03:34 +0000
Subject: [PATCH 1/4] [MLIR][Python] Register OpAttributeMap as Mapping for
match compatibility
---
mlir/include/mlir/Bindings/Python/IRCore.h | 3 ++
mlir/lib/Bindings/Python/IRCore.cpp | 14 +++++
mlir/python/mlir/_mlir_libs/__init__.py | 3 +-
mlir/test/python/ir/operation.py | 63 ++++++++++++++++++++++
4 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/mlir/include/mlir/Bindings/Python/IRCore.h b/mlir/include/mlir/Bindings/Python/IRCore.h
index 8a8d2a1b2270f..b2eb6b9edb677 100644
--- a/mlir/include/mlir/Bindings/Python/IRCore.h
+++ b/mlir/include/mlir/Bindings/Python/IRCore.h
@@ -1782,6 +1782,9 @@ class MLIR_PYTHON_API_EXPORTED PyOpAttributeMap {
PyNamedAttribute dunderGetItemIndexed(intptr_t index);
+ nanobind::object getWithDefaultNamed(const std::string &key,
+ nanobind::object defaultValue);
+
void dunderSetItem(const std::string &name, const PyAttribute &attr);
void dunderDelItem(const std::string &name);
diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp
index 8396569757183..eb5ebf15ea728 100644
--- a/mlir/lib/Bindings/Python/IRCore.cpp
+++ b/mlir/lib/Bindings/Python/IRCore.cpp
@@ -2388,6 +2388,16 @@ PyOpAttributeMap::dunderGetItemNamed(const std::string &name) {
return PyAttribute(operation->getContext(), attr).maybeDownCast();
}
+nb::object PyOpAttributeMap::getWithDefaultNamed(const std::string &key,
+ nb::object defaultValue) {
+ MlirAttribute attr =
+ mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(key));
+ if (mlirAttributeIsNull(attr)) {
+ return defaultValue;
+ }
+ return PyAttribute(operation->getContext(), attr).maybeDownCast();
+}
+
PyNamedAttribute PyOpAttributeMap::dunderGetItemIndexed(intptr_t index) {
if (index < 0) {
index += dunderLen();
@@ -2450,6 +2460,10 @@ void PyOpAttributeMap::bind(nb::module_ &m) {
"Sets an attribute with the given name.")
.def("__delitem__", &PyOpAttributeMap::dunderDelItem, "name"_a,
"Deletes an attribute with the given name.")
+ .def("get", &PyOpAttributeMap::getWithDefaultNamed, nb::arg("key"),
+ nb::arg("default") = nb::none(),
+ "Gets an attribute by name or the default value, if it does not "
+ "exist.")
.def(
"__iter__",
[](PyOpAttributeMap &self) {
diff --git a/mlir/python/mlir/_mlir_libs/__init__.py b/mlir/python/mlir/_mlir_libs/__init__.py
index ce7e6bf93012a..c0e8775149d41 100644
--- a/mlir/python/mlir/_mlir_libs/__init__.py
+++ b/mlir/python/mlir/_mlir_libs/__init__.py
@@ -2,7 +2,7 @@
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-from typing import Any, Sequence
+from typing import Any, Mapping, Sequence
import os
@@ -245,6 +245,7 @@ def __str__(self):
Sequence.register(ir.OpResultList)
Sequence.register(ir.OpSuccessors)
Sequence.register(ir.RegionSequence)
+ Mapping.register(ir.OpAttributeMap)
_site_initialize()
diff --git a/mlir/test/python/ir/operation.py b/mlir/test/python/ir/operation.py
index b9242a7cc2bd9..89f78ab1932a0 100644
--- a/mlir/test/python/ir/operation.py
+++ b/mlir/test/python/ir/operation.py
@@ -652,6 +652,34 @@ def testOperationAttributes():
# CHECK: Dict mapping {'dependent': 'text', 'other.attribute': 3.0, 'some.attribute': 1}
print("Dict mapping", d)
+ # Structural pattern matching test using Mapping
+
+ # CHECK: Matched Mapping Attribute 'some.attribute': 1
+ # CHECK: Matched Mapping Attribute 'other.attribute': 3.0
+ # CHECK: Matched Mapping Attribute 'dependent': text
+ match op:
+ case OpView(attributes={"does_not_exist": a0}):
+ assert False
+ case OpView(
+ attributes={
+ "some.attribute": IntegerAttr(value=some_attr_val) as some_attr,
+ "other.attribute": FloatAttr() as other_attr,
+ "dependent": StringAttr() as dep_attr,
+ **other_attributes,
+ }
+ ):
+ print(f"Matched Mapping Attribute 'some.attribute': {some_attr_val}")
+ print(f"Matched Mapping Attribute 'other.attribute': {other_attr.value}")
+ print(f"Matched Mapping Attribute 'dependent': {dep_attr.value}")
+ assert type(other_attributes) == dict
+ assert len(other_attributes) == 0
+ assert some_attr == op.attributes.get("some.attribute")
+ assert other_attr == op.attributes.get("other.attribute", None)
+ assert dep_attr == op.attributes.get("dependent", "Default value")
+ case _:
+ print("Did not match!")
+ assert False
+
# Check that exceptions are raised as expected.
try:
op.attributes["does_not_exist"]
@@ -667,6 +695,41 @@ def testOperationAttributes():
else:
assert False, "expected IndexError on accessing an out-of-bounds attribute"
+ # Check that exceptions are raised when `get` is used with non-str arg.
+ try:
+ op.attributes.get(0)
+ except TypeError:
+ pass
+ else:
+ assert False, "expected TypeError using int as key for get()"
+
+ try:
+ op.attributes.get(0, None)
+ except TypeError:
+ pass
+ else:
+ assert False, "expected TypeError using int as key for get()"
+
+ try:
+ op.attributes.get([], None)
+ except TypeError:
+ pass
+ else:
+ assert False, "expected TypeError using list as key for get()"
+
+ try:
+ match op:
+ case OpView(attributes={0: a}):
+ assert False
+ except TypeError:
+ pass
+ else:
+ assert False, "expected TypeError matching OpAttributeMap with int-key "
+
+ # get() does not throw for non existent attributes.
+ assert op.attributes.get("does_not_exist") is None
+ assert op.attributes.get("does_not_exist", "default_value") == "default_value"
+
# CHECK-LABEL: TEST: testOperationPrint
@run
>From f05200899b88303a46241b1e822abd7e9d860482 Mon Sep 17 00:00:00 2001
From: MaPePeR <MaPePeR at users.noreply.github.com>
Date: Fri, 9 Jan 2026 23:55:20 +0100
Subject: [PATCH 2/4] Update mlir/lib/Bindings/Python/IRCore.cpp
Co-authored-by: Maksim Levental <maksim.levental at gmail.com>
---
mlir/lib/Bindings/Python/IRCore.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp
index eb5ebf15ea728..151183855a7b8 100644
--- a/mlir/lib/Bindings/Python/IRCore.cpp
+++ b/mlir/lib/Bindings/Python/IRCore.cpp
@@ -2392,9 +2392,8 @@ nb::object PyOpAttributeMap::getWithDefaultNamed(const std::string &key,
nb::object defaultValue) {
MlirAttribute attr =
mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(key));
- if (mlirAttributeIsNull(attr)) {
+ if (mlirAttributeIsNull(attr))
return defaultValue;
- }
return PyAttribute(operation->getContext(), attr).maybeDownCast();
}
>From d0ca898b41e19b882c086c3f278d193fc98878ba Mon Sep 17 00:00:00 2001
From: MaPePeR <MaPePeR at users.noreply.github.com>
Date: Sat, 10 Jan 2026 10:02:29 +0000
Subject: [PATCH 3/4] Set `OpAttributeMap.get` return type to
`Optional[Attribute]`
---
mlir/include/mlir/Bindings/Python/IRCore.h | 4 ++--
mlir/lib/Bindings/Python/IRCore.cpp | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/Bindings/Python/IRCore.h b/mlir/include/mlir/Bindings/Python/IRCore.h
index b2eb6b9edb677..ba8c876d5edf0 100644
--- a/mlir/include/mlir/Bindings/Python/IRCore.h
+++ b/mlir/include/mlir/Bindings/Python/IRCore.h
@@ -1782,8 +1782,8 @@ class MLIR_PYTHON_API_EXPORTED PyOpAttributeMap {
PyNamedAttribute dunderGetItemIndexed(intptr_t index);
- nanobind::object getWithDefaultNamed(const std::string &key,
- nanobind::object defaultValue);
+ nanobind::typed<nanobind::object, std::optional<PyAttribute>>
+ getWithDefaultNamed(const std::string &key, nanobind::object defaultValue);
void dunderSetItem(const std::string &name, const PyAttribute &attr);
diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp
index 151183855a7b8..f2fda9992d046 100644
--- a/mlir/lib/Bindings/Python/IRCore.cpp
+++ b/mlir/lib/Bindings/Python/IRCore.cpp
@@ -2388,8 +2388,8 @@ PyOpAttributeMap::dunderGetItemNamed(const std::string &name) {
return PyAttribute(operation->getContext(), attr).maybeDownCast();
}
-nb::object PyOpAttributeMap::getWithDefaultNamed(const std::string &key,
- nb::object defaultValue) {
+nb::typed<nb::object, std::optional<PyAttribute>>
+PyOpAttributeMap::getWithDefaultNamed(const std::string &key, nb::object defaultValue) {
MlirAttribute attr =
mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(key));
if (mlirAttributeIsNull(attr))
>From 159592fc4906f97fac72e7de82054172a9a729cb Mon Sep 17 00:00:00 2001
From: MaPePeR <MaPePeR at users.noreply.github.com>
Date: Sat, 10 Jan 2026 10:07:43 +0000
Subject: [PATCH 4/4] Rename function from `getWithDefaultNamed` to just `get`
---
mlir/include/mlir/Bindings/Python/IRCore.h | 2 +-
mlir/lib/Bindings/Python/IRCore.cpp | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/mlir/include/mlir/Bindings/Python/IRCore.h b/mlir/include/mlir/Bindings/Python/IRCore.h
index ba8c876d5edf0..83691ad9764f1 100644
--- a/mlir/include/mlir/Bindings/Python/IRCore.h
+++ b/mlir/include/mlir/Bindings/Python/IRCore.h
@@ -1783,7 +1783,7 @@ class MLIR_PYTHON_API_EXPORTED PyOpAttributeMap {
PyNamedAttribute dunderGetItemIndexed(intptr_t index);
nanobind::typed<nanobind::object, std::optional<PyAttribute>>
- getWithDefaultNamed(const std::string &key, nanobind::object defaultValue);
+ get(const std::string &key, nanobind::object defaultValue);
void dunderSetItem(const std::string &name, const PyAttribute &attr);
diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp
index f2fda9992d046..6f89518ce9160 100644
--- a/mlir/lib/Bindings/Python/IRCore.cpp
+++ b/mlir/lib/Bindings/Python/IRCore.cpp
@@ -2389,7 +2389,7 @@ PyOpAttributeMap::dunderGetItemNamed(const std::string &name) {
}
nb::typed<nb::object, std::optional<PyAttribute>>
-PyOpAttributeMap::getWithDefaultNamed(const std::string &key, nb::object defaultValue) {
+PyOpAttributeMap::get(const std::string &key, nb::object defaultValue) {
MlirAttribute attr =
mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(key));
if (mlirAttributeIsNull(attr))
@@ -2459,7 +2459,7 @@ void PyOpAttributeMap::bind(nb::module_ &m) {
"Sets an attribute with the given name.")
.def("__delitem__", &PyOpAttributeMap::dunderDelItem, "name"_a,
"Deletes an attribute with the given name.")
- .def("get", &PyOpAttributeMap::getWithDefaultNamed, nb::arg("key"),
+ .def("get", &PyOpAttributeMap::get, nb::arg("key"),
nb::arg("default") = nb::none(),
"Gets an attribute by name or the default value, if it does not "
"exist.")
More information about the Mlir-commits
mailing list