Improve error for misused positional argument (#1984) - #3028
Improve error for misused positional argument (#1984)#3028αI (Alphaxiaoteng) wants to merge 1 commit into
Conversation
Calling an op with a non-Value positional argument that was meant to be an attribute/keyword (e.g. `op.LeakyRelu(x, 1.0)`) used to surface a cryptic `AttributeError: 'float' object has no attribute '_add_usage'` raised deep inside onnx_ir. Validate positional inputs in `BuilderBase.call_op` and raise a clear, actionable `TypeError` that names the op and suggests passing the value as a keyword argument. Legitimate scalar constants are unaffected because they are promoted to constants before this point when the builder enables CAST_INPUTS (the FULL feature set used by `@script`). Fixes microsoft#1984
10e9ced to
43a7375
Compare
| with self.assertRaises(TypeError) as cm: | ||
| TapeBuilder().LeakyRelu(x, 1.0) | ||
| message = str(cm.exception) | ||
| self.assertIn("LeakyRelu", message) | ||
| self.assertIn("keyword", message) | ||
| # Make sure we did not regress to the old cryptic AttributeError. | ||
| self.assertNotIn("_add_usage", message) | ||
|
|
||
| # The same clear error is produced for other ops (e.g. Add). | ||
| with self.assertRaises(TypeError): | ||
| TapeBuilder().Add(x, 1.0) |
There was a problem hiding this comment.
You may use assertRaisesRegex to pin down the error message
| """GitHub issue #1984: passing a constant where an attribute/keyword was | ||
| expected (e.g. ``op.LeakyRelu(x, 1.0)``) used to raise a cryptic | ||
| ``AttributeError`` from onnx_ir (``'float' object has no attribute | ||
| '_add_usage'``). It should now raise a clear ``TypeError`` that tells | ||
| the user to pass the value as a keyword argument. | ||
| """ |
There was a problem hiding this comment.
Could you simplify the doctring to make it self-contained (no need to reference the issue)?
There was a problem hiding this comment.
🟡 Changes recommended
The schema-based hint logic in the new validation block can mis-handle schema.attributes (a dict) and the fallback example hard-codes alpha=..., which can produce incorrect/misleading errors.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR improves developer-facing diagnostics when an ONNX op is called with a non-ir.Value positional argument (commonly a Python scalar that was intended to be an attribute passed by keyword), by validating positional inputs in the central op-dispatch path and raising a clear TypeError instead of surfacing a cryptic error from onnx_ir.
Changes:
- Added positional-argument validation in
BuilderBase.call_opto detect non-ir.Valuepositional inputs and raise an actionableTypeError. - Added a regression test to ensure the new error is raised (and that scalar constant promotion under
BuilderFeature.FULLis unaffected).
File summaries
| File | Description |
|---|---|
| onnxscript/_internal/tape_builder.py | Adds validation and improved error messaging for misused non-Value positional args in call_op. |
| onnxscript/_internal/builder_test.py | Adds a unit test covering the clearer TypeError and non-regression for scalar promotion under FULL. |
Review details
Suppressed comments (1)
onnxscript/_internal/tape_builder.py:417
- The schema-unavailable error message hard-codes
alpha=...in the example, which is misleading for most ops (e.g.Addhas noalphaattribute). Using a generic placeholder avoids suggesting an invalid attribute name.
raise TypeError(
f"{op_type}() got a non-Value positional argument {arg!r} at "
f"position {index + 1}. If this was meant to be an attribute, "
f"pass it as a keyword argument (e.g. op.{op_type}(x, "
f"alpha={arg!r}))."
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if schema is not None: | ||
| # Suggest the most likely attribute name from the schema so the | ||
| # user knows how to pass the value correctly. | ||
| attributes = getattr(schema, "attributes", None) or () | ||
| attr_hint = "" | ||
| if index < len(attributes): | ||
| attr_hint = ( | ||
| f" It looks like this should be the " | ||
| f"'{attributes[index].name}' attribute; pass it as a " | ||
| f"keyword (e.g. op.{op_type}(x, " | ||
| f"{attributes[index].name}={arg!r}))." | ||
| ) | ||
| raise TypeError( | ||
| f"{op_type}() got a non-Value positional argument " | ||
| f"{arg!r} at position {index + 1}.{attr_hint}" | ||
| ) |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3028 +/- ##
==========================================
+ Coverage 72.64% 72.69% +0.04%
==========================================
Files 265 265
Lines 32251 32309 +58
Branches 3050 3058 +8
==========================================
+ Hits 23429 23486 +57
Misses 7786 7786
- Partials 1036 1037 +1 ☔ View full report in Codecov by Harness. |
Description
Calling an op with a non-Value positional argument that was meant to be an attribute/keyword — e.g. `op.LeakyRelu(x, 1.0)` where `alpha` should be a keyword — used to surface a cryptic error raised deep inside onnx_ir:
```
AttributeError: 'float' object has no attribute '_add_usage'
```
`BuilderBase.call_op` (the single point where every op dispatch builds its node) now validates positional inputs before constructing the node and raises a clear, actionable `TypeError` that names the op and suggests passing the value as a keyword argument:
```
TypeError: LeakyRelu() got a non-Value positional argument 1.0 at position 2. If this was meant to be an attribute, pass it as a keyword argument (e.g. op.LeakyRelu(x, alpha=1.0)).
```
When a schema is available, the message additionally suggests the specific attribute name.
Why this location
The `Node`/`Value` IR was moved into the separate `onnx_ir` package, so the reporter's original patch (against `ir/_core.py`) no longer applies. `call_op` is the onnxscript-side equivalent of "the node constructor" and the right place to give users a readable error without touching the dependency.
Notes / non-regression
Tests
Added `test_non_value_positional_argument_raises_clear_type_error` to `onnxscript/_internal/builder_test.py`. The full `builder_test.py` suite (91 tests) passes.
Fixes #1984