From 35d5b50a0f6b8d41780108f93cefc1788f6da84b Mon Sep 17 00:00:00 2001 From: Nicholas Junge Date: Thu, 12 Jun 2025 11:40:42 +0200 Subject: [PATCH 1/4] [mlir][py] Mark all type caster `from_{cpp,python}` methods as noexcept This is mentioned as a "must" in https://nanobind.readthedocs.io/en/latest/porting.html#type-casters when implementing type casters. While most of the existing `from_cpp` methods were already marked noexcept, many of the `from_python` methods were not. This commit adds the missing noexcept declarations to all type casters found in `NanobindAdaptors.h`. --- .../mlir/Bindings/Python/NanobindAdaptors.h | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h index 2dd35c097c796..e39b1a752f8d6 100644 --- a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h +++ b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h @@ -67,7 +67,7 @@ static nanobind::object mlirApiObjectToCapsule(nanobind::handle apiObject) { template <> struct type_caster { NB_TYPE_CASTER(MlirAffineMap, const_name("MlirAffineMap")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToAffineMap(capsule.ptr()); if (mlirAffineMapIsNull(value)) { @@ -90,7 +90,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirAttribute, const_name("MlirAttribute")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToAttribute(capsule.ptr()); return !mlirAttributeIsNull(value); @@ -111,7 +111,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirBlock, const_name("MlirBlock")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToBlock(capsule.ptr()); return !mlirBlockIsNull(value); @@ -122,7 +122,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirContext, const_name("MlirContext")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { if (src.is_none()) { // Gets the current thread-bound context. // TODO: This raises an error of "No current context" currently. @@ -142,7 +142,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirDialectRegistry, const_name("MlirDialectRegistry")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToDialectRegistry(capsule.ptr()); return !mlirDialectRegistryIsNull(value); @@ -162,7 +162,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirLocation, const_name("MlirLocation")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { if (src.is_none()) { // Gets the current thread-bound context. src = nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir")) @@ -188,7 +188,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirModule, const_name("MlirModule")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToModule(capsule.ptr()); return !mlirModuleIsNull(value); @@ -209,12 +209,13 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirFrozenRewritePatternSet, const_name("MlirFrozenRewritePatternSet")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule.ptr()); return value.ptr != nullptr; } - static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, handle) { + static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, + handle) noexcept { nanobind::object capsule = nanobind::steal( mlirPythonFrozenRewritePatternSetToCapsule(v)); return nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("rewrite")) @@ -228,7 +229,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirOperation, const_name("MlirOperation")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToOperation(capsule.ptr()); return !mlirOperationIsNull(value); @@ -250,7 +251,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirValue, const_name("MlirValue")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToValue(capsule.ptr()); return !mlirValueIsNull(value); @@ -273,7 +274,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirPassManager, const_name("MlirPassManager")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToPassManager(capsule.ptr()); return !mlirPassManagerIsNull(value); @@ -284,7 +285,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirTypeID, const_name("MlirTypeID")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToTypeID(capsule.ptr()); return !mlirTypeIDIsNull(value); @@ -306,7 +307,7 @@ struct type_caster { template <> struct type_caster { NB_TYPE_CASTER(MlirType, const_name("MlirType")) - bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) { + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { nanobind::object capsule = mlirApiObjectToCapsule(src); value = mlirPythonCapsuleToType(capsule.ptr()); return !mlirTypeIsNull(value); From d7beef99f0a4ecc3287ac1c744ea0eb0ffb416b3 Mon Sep 17 00:00:00 2001 From: Nicholas Junge Date: Thu, 10 Jul 2025 16:32:14 +0200 Subject: [PATCH 2/4] refactor: Change `mlirApiObjectToCapsule` to return std::optional Instead of raising a `nanobind::type_error()`. This is necessary to honor the nanobind type caster API contract, which requires `from_python` and `from_cpp` methods to be marked `noexcept`. --- .../mlir/Bindings/Python/NanobindAdaptors.h | 88 +++++++++++-------- .../python/lib/PythonTestModuleNanobind.cpp | 6 +- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h index e39b1a752f8d6..7c21f60f844c4 100644 --- a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h +++ b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h @@ -20,6 +20,7 @@ #define MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H #include +#include #include "mlir-c/Diagnostics.h" #include "mlir-c/IR.h" @@ -43,18 +44,14 @@ namespace detail { /// with a raw handle (unowned). The returned object's lifetime may not extend /// beyond the apiObject handle without explicitly having its refcount increased /// (i.e. on return). -static nanobind::object mlirApiObjectToCapsule(nanobind::handle apiObject) { +static std::optional +mlirApiObjectToCapsule(nanobind::handle apiObject) { if (PyCapsule_CheckExact(apiObject.ptr())) return nanobind::borrow(apiObject); nanobind::object api = nanobind::getattr(apiObject, MLIR_PYTHON_CAPI_PTR_ATTR, nanobind::none()); - if (api.is_none()) { - std::string repr = nanobind::cast(nanobind::repr(apiObject)); - throw nanobind::type_error( - (llvm::Twine("Expected an MLIR object (got ") + repr + ").") - .str() - .c_str()); - } + if (api.is_none()) + return std::nullopt; return api; } @@ -68,11 +65,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirAffineMap, const_name("MlirAffineMap")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToAffineMap(capsule.ptr()); - if (mlirAffineMapIsNull(value)) { + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) return false; - } + value = mlirPythonCapsuleToAffineMap(capsule.value().ptr()); return !mlirAffineMapIsNull(value); } static handle from_cpp(MlirAffineMap v, rv_policy, @@ -91,8 +87,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirAttribute, const_name("MlirAttribute")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToAttribute(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToAttribute(capsule.value().ptr()); return !mlirAttributeIsNull(value); } static handle from_cpp(MlirAttribute v, rv_policy, @@ -112,8 +110,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirBlock, const_name("MlirBlock")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToBlock(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToBlock(capsule.value().ptr()); return !mlirBlockIsNull(value); } }; @@ -132,8 +132,8 @@ struct type_caster { .attr("Context") .attr("current"); } - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToContext(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + value = mlirPythonCapsuleToContext(capsule.value().ptr()); return !mlirContextIsNull(value); } }; @@ -143,8 +143,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirDialectRegistry, const_name("MlirDialectRegistry")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToDialectRegistry(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToDialectRegistry(capsule.value().ptr()); return !mlirDialectRegistryIsNull(value); } static handle from_cpp(MlirDialectRegistry v, rv_policy, @@ -169,8 +171,8 @@ struct type_caster { .attr("Location") .attr("current"); } - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToLocation(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + value = mlirPythonCapsuleToLocation(capsule.value().ptr()); return !mlirLocationIsNull(value); } static handle from_cpp(MlirLocation v, rv_policy, @@ -189,8 +191,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirModule, const_name("MlirModule")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToModule(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToModule(capsule.value().ptr()); return !mlirModuleIsNull(value); } static handle from_cpp(MlirModule v, rv_policy, @@ -210,8 +214,10 @@ struct type_caster { NB_TYPE_CASTER(MlirFrozenRewritePatternSet, const_name("MlirFrozenRewritePatternSet")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule.value().ptr()); return value.ptr != nullptr; } static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, @@ -230,8 +236,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirOperation, const_name("MlirOperation")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToOperation(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToOperation(capsule.value().ptr()); return !mlirOperationIsNull(value); } static handle from_cpp(MlirOperation v, rv_policy, @@ -252,8 +260,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirValue, const_name("MlirValue")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToValue(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToValue(capsule.value().ptr()); return !mlirValueIsNull(value); } static handle from_cpp(MlirValue v, rv_policy, @@ -275,8 +285,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirPassManager, const_name("MlirPassManager")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToPassManager(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToPassManager(capsule.value().ptr()); return !mlirPassManagerIsNull(value); } }; @@ -286,8 +298,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirTypeID, const_name("MlirTypeID")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToTypeID(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToTypeID(capsule.value().ptr()); return !mlirTypeIDIsNull(value); } static handle from_cpp(MlirTypeID v, rv_policy, @@ -308,8 +322,10 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirType, const_name("MlirType")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - nanobind::object capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToType(capsule.ptr()); + std::optional capsule = mlirApiObjectToCapsule(src); + if (!capsule) + return false; + value = mlirPythonCapsuleToType(capsule.value().ptr()); return !mlirTypeIsNull(value); } static handle from_cpp(MlirType t, rv_policy, diff --git a/mlir/test/python/lib/PythonTestModuleNanobind.cpp b/mlir/test/python/lib/PythonTestModuleNanobind.cpp index dd0a9f2b66ea6..ac69a3c28a767 100644 --- a/mlir/test/python/lib/PythonTestModuleNanobind.cpp +++ b/mlir/test/python/lib/PythonTestModuleNanobind.cpp @@ -113,8 +113,10 @@ NB_MODULE(_mlirPythonTestNanobind, m) { .attr(MLIR_PYTHON_CAPI_VALUE_CASTER_REGISTER_ATTR)( mlirRankedTensorTypeID)( nanobind::cpp_function([valueCls](const nb::object &valueObj) { - nb::object capsule = mlirApiObjectToCapsule(valueObj); - MlirValue v = mlirPythonCapsuleToValue(capsule.ptr()); + std::optional capsule = + mlirApiObjectToCapsule(valueObj); + // TODO(nicholasjng): Can this capsule be std::nullopt? + MlirValue v = mlirPythonCapsuleToValue(capsule.value().ptr()); MlirType t = mlirValueGetType(v); // This is hyper-specific in order to exercise/test registering a // value caster from cpp (but only for a single test case; see From db8e35c82dfffa712f38425db97f438c9a2899f6 Mon Sep 17 00:00:00 2001 From: Nicholas Junge Date: Thu, 10 Jul 2025 22:17:14 +0200 Subject: [PATCH 3/4] refactor: Assign in if statements everywhere, adjust test expectations Modeled after a patch posted in the conversation in PR #143866: https://gist.github.com/makslevental/b224ffca7f15e273a4897975cda28b4c. --- .../mlir/Bindings/Python/NanobindAdaptors.h | 74 +++++++------------ mlir/test/python/dialects/python_test.py | 8 +- .../python/lib/PythonTestModuleNanobind.cpp | 2 +- 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h index 7c21f60f844c4..039831c7b9737 100644 --- a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h +++ b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h @@ -51,7 +51,7 @@ mlirApiObjectToCapsule(nanobind::handle apiObject) { nanobind::object api = nanobind::getattr(apiObject, MLIR_PYTHON_CAPI_PTR_ATTR, nanobind::none()); if (api.is_none()) - return std::nullopt; + return {}; return api; } @@ -65,10 +65,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirAffineMap, const_name("MlirAffineMap")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToAffineMap(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToAffineMap(capsule->ptr()); return !mlirAffineMapIsNull(value); } static handle from_cpp(MlirAffineMap v, rv_policy, @@ -87,10 +85,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirAttribute, const_name("MlirAttribute")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToAttribute(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToAttribute(capsule->ptr()); return !mlirAttributeIsNull(value); } static handle from_cpp(MlirAttribute v, rv_policy, @@ -110,10 +106,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirBlock, const_name("MlirBlock")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToBlock(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToBlock(capsule->ptr()); return !mlirBlockIsNull(value); } }; @@ -133,7 +127,7 @@ struct type_caster { .attr("current"); } std::optional capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToContext(capsule.value().ptr()); + value = mlirPythonCapsuleToContext(capsule->ptr()); return !mlirContextIsNull(value); } }; @@ -143,10 +137,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirDialectRegistry, const_name("MlirDialectRegistry")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToDialectRegistry(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToDialectRegistry(capsule->ptr()); return !mlirDialectRegistryIsNull(value); } static handle from_cpp(MlirDialectRegistry v, rv_policy, @@ -171,8 +163,8 @@ struct type_caster { .attr("Location") .attr("current"); } - std::optional capsule = mlirApiObjectToCapsule(src); - value = mlirPythonCapsuleToLocation(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToLocation(capsule->ptr()); return !mlirLocationIsNull(value); } static handle from_cpp(MlirLocation v, rv_policy, @@ -191,10 +183,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirModule, const_name("MlirModule")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToModule(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToModule(capsule->ptr()); return !mlirModuleIsNull(value); } static handle from_cpp(MlirModule v, rv_policy, @@ -214,10 +204,8 @@ struct type_caster { NB_TYPE_CASTER(MlirFrozenRewritePatternSet, const_name("MlirFrozenRewritePatternSet")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule->ptr()); return value.ptr != nullptr; } static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, @@ -236,10 +224,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirOperation, const_name("MlirOperation")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToOperation(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToOperation(capsule->ptr()); return !mlirOperationIsNull(value); } static handle from_cpp(MlirOperation v, rv_policy, @@ -260,10 +246,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirValue, const_name("MlirValue")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToValue(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToValue(capsule->ptr()); return !mlirValueIsNull(value); } static handle from_cpp(MlirValue v, rv_policy, @@ -285,10 +269,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirPassManager, const_name("MlirPassManager")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToPassManager(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToPassManager(capsule->ptr()); return !mlirPassManagerIsNull(value); } }; @@ -298,10 +280,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirTypeID, const_name("MlirTypeID")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToTypeID(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToTypeID(capsule->ptr()); return !mlirTypeIDIsNull(value); } static handle from_cpp(MlirTypeID v, rv_policy, @@ -322,10 +302,8 @@ template <> struct type_caster { NB_TYPE_CASTER(MlirType, const_name("MlirType")) bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { - std::optional capsule = mlirApiObjectToCapsule(src); - if (!capsule) - return false; - value = mlirPythonCapsuleToType(capsule.value().ptr()); + if (auto capsule = mlirApiObjectToCapsule(src)) + value = mlirPythonCapsuleToType(capsule->ptr()); return !mlirTypeIsNull(value); } static handle from_cpp(MlirType t, rv_policy, diff --git a/mlir/test/python/dialects/python_test.py b/mlir/test/python/dialects/python_test.py index fd678f8321fd9..694616696a9e2 100644 --- a/mlir/test/python/dialects/python_test.py +++ b/mlir/test/python/dialects/python_test.py @@ -361,7 +361,9 @@ def testCustomAttribute(): try: TestAttr(42) except TypeError as e: - assert "Expected an MLIR object" in str(e) + assert "Expected an MLIR object (got 42)" in str(e) + except ValueError as e: + assert "Cannot cast attribute to TestAttr (from 42)" in str(e) else: raise @@ -406,7 +408,9 @@ def testCustomType(): try: TestType(42) except TypeError as e: - assert "Expected an MLIR object" in str(e) + assert "Expected an MLIR object (got 42)" in str(e) + except ValueError as e: + assert "Cannot cast type to TestType (from 42)" in str(e) else: raise diff --git a/mlir/test/python/lib/PythonTestModuleNanobind.cpp b/mlir/test/python/lib/PythonTestModuleNanobind.cpp index ac69a3c28a767..108df8da86a7e 100644 --- a/mlir/test/python/lib/PythonTestModuleNanobind.cpp +++ b/mlir/test/python/lib/PythonTestModuleNanobind.cpp @@ -115,7 +115,7 @@ NB_MODULE(_mlirPythonTestNanobind, m) { nanobind::cpp_function([valueCls](const nb::object &valueObj) { std::optional capsule = mlirApiObjectToCapsule(valueObj); - // TODO(nicholasjng): Can this capsule be std::nullopt? + assert(capsule.has_value() && "capsule is not null"); MlirValue v = mlirPythonCapsuleToValue(capsule.value().ptr()); MlirType t = mlirValueGetType(v); // This is hyper-specific in order to exercise/test registering a From 88e80384d3fd83c2f3b7895faa38e96fc5c3e14e Mon Sep 17 00:00:00 2001 From: Nicholas Junge Date: Fri, 11 Jul 2025 11:23:45 +0200 Subject: [PATCH 4/4] Relax conversions to `nanobind::try_cast` This avoids a nb::cast_error throw in certain class conversions. --- .../mlir/Bindings/Python/NanobindAdaptors.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h index 039831c7b9737..8dcf91e5806bd 100644 --- a/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h +++ b/mlir/include/mlir/Bindings/Python/NanobindAdaptors.h @@ -457,9 +457,10 @@ class mlir_attribute_subclass : public pure_subclass { nanobind::object newCf = nanobind::cpp_function( [superCls, isaFunction, captureTypeName]( nanobind::object cls, nanobind::object otherAttribute) { - MlirAttribute rawAttribute = - nanobind::cast(otherAttribute); - if (!isaFunction(rawAttribute)) { + MlirAttribute rawAttribute; + if (!nanobind::try_cast(otherAttribute, + rawAttribute) || + !isaFunction(rawAttribute)) { auto origRepr = nanobind::cast(nanobind::repr(otherAttribute)); throw std::invalid_argument( @@ -538,8 +539,9 @@ class mlir_type_subclass : public pure_subclass { nanobind::object newCf = nanobind::cpp_function( [superCls, isaFunction, captureTypeName](nanobind::object cls, nanobind::object otherType) { - MlirType rawType = nanobind::cast(otherType); - if (!isaFunction(rawType)) { + MlirType rawType; + if (!nanobind::try_cast(otherType, rawType) || + !isaFunction(rawType)) { auto origRepr = nanobind::cast(nanobind::repr(otherType)); throw std::invalid_argument((llvm::Twine("Cannot cast type to ") + @@ -620,8 +622,9 @@ class mlir_value_subclass : public pure_subclass { nanobind::object newCf = nanobind::cpp_function( [superCls, isaFunction, captureValueName](nanobind::object cls, nanobind::object otherValue) { - MlirValue rawValue = nanobind::cast(otherValue); - if (!isaFunction(rawValue)) { + MlirValue rawValue; + if (!nanobind::try_cast(otherValue, rawValue) || + !isaFunction(rawValue)) { auto origRepr = nanobind::cast(nanobind::repr(otherValue)); throw std::invalid_argument((llvm::Twine("Cannot cast value to ") +