From 36a846e36603ab43217b7734b419e81749fe6f6e Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Thu, 12 Feb 2026 23:05:38 +0000 Subject: [PATCH 1/5] fix --- .../version_converter/_version_converter.py | 5 ++ .../_version_converter_test.py | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/onnxscript/version_converter/_version_converter.py b/onnxscript/version_converter/_version_converter.py index 7470df0ead..aec4743c27 100644 --- a/onnxscript/version_converter/_version_converter.py +++ b/onnxscript/version_converter/_version_converter.py @@ -310,6 +310,11 @@ def visit_graph_or_function(self, graph_or_function: ir.Graph | ir.Function) -> node_version = node.version or self._default_onnx_opset if node_version is None: raise VersionConverterError(f"Node {node} has no version.") + # RefAttr is not supported by adapters for now. + if any(attr.is_ref() for attr in node.attributes.values()): + raise VersionConverterError( + f"Node {node} has ref attribute, which is not supported by version converter." + ) # Iterate each node from current node version -> target version # and updating node based on the correct adapter # Up-conversion [ver->ver+1] or down-conversion [ver->ver-1] diff --git a/onnxscript/version_converter/_version_converter_test.py b/onnxscript/version_converter/_version_converter_test.py index 2b615a8f7f..87382e4053 100644 --- a/onnxscript/version_converter/_version_converter_test.py +++ b/onnxscript/version_converter/_version_converter_test.py @@ -448,6 +448,73 @@ def test_metadata_is_copied_to_multiple_replacement_nodes(self): f"Node {i} ({node.op_type}) should have metadata copied", ) + def test_version_convert_raises_on_function_node_with_ref_attribute(self): + """Test that version conversion raises when a function contains a node with a ref attribute.""" + # Build a function with a LeakyRelu node that uses a RefAttr for 'alpha' + func_input = ir.Value(name="x") + ref_attr = ir.RefAttr("alpha", "alpha", ir.AttributeType.FLOAT) + func_output = ir.Value(name="result") + leaky_relu_node = ir.Node( + domain="", + op_type="LeakyRelu", + inputs=[func_input], + outputs=[func_output], + attributes=[ref_attr], + version=18, + ) + func_graph = ir.Graph( + inputs=[func_input], + outputs=[func_output], + nodes=[leaky_relu_node], + opset_imports={"": 18}, + ) + func_attr_param = ir.Attr("alpha", ir.AttributeType.FLOAT, 0.01) + function = ir.Function( + domain="pkg.custom", + name="leaky_relu_func", + graph=func_graph, + attributes=[func_attr_param], + ) + + # Build a main graph that calls the function + main_input = ir.Value(name="input_x") + main_output = ir.Value(name="output") + call_node = ir.Node( + domain="pkg.custom", + op_type="leaky_relu_func", + inputs=[main_input], + outputs=[main_output], + version=18, + ) + main_graph = ir.Graph( + inputs=[main_input], + outputs=[main_output], + nodes=[call_node], + opset_imports={"": 18, "pkg.custom": 1}, + ) + model = ir.Model( + main_graph, + ir_version=8, + functions=[function], + ) + + target_version = 20 + with self.assertRaises( + ( + version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access` + ir.passes.PassError, + ) + ) as ctx: + version_converter.convert_version(model, target_version=target_version) + # Check the error message, unwrapping PassError if needed + error = ctx.exception + if isinstance(error, ir.passes.PassError) and error.__cause__ is not None: + error = error.__cause__ + self.assertIn( + "has ref attribute, which is not supported by version converter", + str(error), + ) + class VersionConverter25to26Test(unittest.TestCase): @pytest.mark.xfail(strict=True, reason="Version upgrade beyond 25 not yet supported.") From eac3cf881db12d990044140e9ab718611e095cb5 Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Thu, 12 Feb 2026 15:13:14 -0800 Subject: [PATCH 2/5] Update onnxscript/version_converter/_version_converter_test.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxscript/version_converter/_version_converter_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxscript/version_converter/_version_converter_test.py b/onnxscript/version_converter/_version_converter_test.py index 87382e4053..814f35e114 100644 --- a/onnxscript/version_converter/_version_converter_test.py +++ b/onnxscript/version_converter/_version_converter_test.py @@ -501,7 +501,7 @@ def test_version_convert_raises_on_function_node_with_ref_attribute(self): target_version = 20 with self.assertRaises( ( - version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access` + version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access ir.passes.PassError, ) ) as ctx: From 5ce524fa85dc44a5112c2c5d1135e736a9abfe86 Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Thu, 12 Feb 2026 15:37:35 -0800 Subject: [PATCH 3/5] Update onnxscript/version_converter/_version_converter.py Co-authored-by: Justin Chu --- onnxscript/version_converter/_version_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxscript/version_converter/_version_converter.py b/onnxscript/version_converter/_version_converter.py index aec4743c27..b890cf7e60 100644 --- a/onnxscript/version_converter/_version_converter.py +++ b/onnxscript/version_converter/_version_converter.py @@ -313,7 +313,7 @@ def visit_graph_or_function(self, graph_or_function: ir.Graph | ir.Function) -> # RefAttr is not supported by adapters for now. if any(attr.is_ref() for attr in node.attributes.values()): raise VersionConverterError( - f"Node {node} has ref attribute, which is not supported by version converter." + f"Node '{node!r}' has ref attribute, which is not supported by version converter." ) # Iterate each node from current node version -> target version # and updating node based on the correct adapter From 5ce8ae9a2ac57df13a3724c9e09db451a4093e45 Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Fri, 13 Feb 2026 18:04:53 +0000 Subject: [PATCH 4/5] move out version converter logic from sequential --- onnxscript/version_converter/__init__.py | 20 +++++++++++++------ .../_version_converter_test.py | 13 +++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/onnxscript/version_converter/__init__.py b/onnxscript/version_converter/__init__.py index bd020c83a6..c29fb4c989 100644 --- a/onnxscript/version_converter/__init__.py +++ b/onnxscript/version_converter/__init__.py @@ -37,18 +37,26 @@ def __init__(self, target_version: int, fallback: bool = False) -> None: super().__init__() self.target_version = target_version self.fallback = fallback - self.convert_pass = ir.passes.Sequential( - _ConvertVersionPass( - target_version=target_version, - fallback=fallback, - ), + self._convert_pass = _ConvertVersionPass( + target_version=target_version, + fallback=fallback, + ) + self._cleanup_passes = ir.passes.Sequential( common_passes.RemoveUnusedNodesPass(), common_passes.RemoveUnusedFunctionsPass(), common_passes.RemoveUnusedOpsetsPass(), ) def call(self, model: ir.Model) -> ir.passes.PassResult: - return self.convert_pass(model) + # Run the conversion pass outside of Sequential so that errors + # (e.g. VersionConverterError) propagate directly without being + # wrapped in PassError. + result = self._convert_pass(model) + cleanup_result = self._cleanup_passes(result) + return ir.passes.PassResult( + cleanup_result.model, + result.modified or cleanup_result.modified, + ) class _ConvertVersionPass(ir.passes.InPlacePass): diff --git a/onnxscript/version_converter/_version_converter_test.py b/onnxscript/version_converter/_version_converter_test.py index 0637d051b3..e41a9cec00 100644 --- a/onnxscript/version_converter/_version_converter_test.py +++ b/onnxscript/version_converter/_version_converter_test.py @@ -507,19 +507,12 @@ def test_version_convert_raises_on_function_node_with_ref_attribute(self): target_version = 20 with self.assertRaises( - ( - version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access - ir.passes.PassError, - ) + version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access ) as ctx: version_converter.convert_version(model, target_version=target_version) - # Check the error message, unwrapping PassError if needed - error = ctx.exception - if isinstance(error, ir.passes.PassError) and error.__cause__ is not None: - error = error.__cause__ - self.assertIn( + self.assertRegex( + str(ctx.exception), "has ref attribute, which is not supported by version converter", - str(error), ) From 97e08a48a3e21759e954ebd9ec8b37c12dd24335 Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Fri, 13 Feb 2026 18:19:47 +0000 Subject: [PATCH 5/5] assertraisesregex --- onnxscript/version_converter/_version_converter_test.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/onnxscript/version_converter/_version_converter_test.py b/onnxscript/version_converter/_version_converter_test.py index e41a9cec00..c920746d7b 100644 --- a/onnxscript/version_converter/_version_converter_test.py +++ b/onnxscript/version_converter/_version_converter_test.py @@ -506,14 +506,11 @@ def test_version_convert_raises_on_function_node_with_ref_attribute(self): ) target_version = 20 - with self.assertRaises( + with self.assertRaisesRegex( version_converter._version_converter.VersionConverterError, # pylint: disable=protected-access - ) as ctx: - version_converter.convert_version(model, target_version=target_version) - self.assertRegex( - str(ctx.exception), "has ref attribute, which is not supported by version converter", - ) + ): + version_converter.convert_version(model, target_version=target_version) class VersionConverter25to26Test(unittest.TestCase):