fix(tool_validation): tuple literals wrongly rejected as complex#2527
fix(tool_validation): tuple literals wrongly rejected as complex#2527chuenchen309 wants to merge 1 commit into
Conversation
validate_tool_attributes()'s "is this attribute a simple literal"
check whitelisted (ast.Constant, ast.Dict, ast.List, ast.Set) but not
ast.Tuple, so any Tool subclass using an ordinary tuple class attribute
(e.g. allowed_exts = (".png", ".jpg")) failed validation with "Complex
attributes should be defined in __init__, not as class attributes" -
blocking Tool.save()/push_to_hub() for a completely standard pattern.
Adding ast.Tuple alone wasn't sufficient: the check uses ast.walk() over
the whole assignment value, and for Tuple/List nodes that also yields
their ast.Load() context node (a Tuple's `ctx` field, always Load for a
value expression) - previously unnoticed because Dict/Set don't have a
ctx field. This meant *list* literal attributes had the identical latent
bug (untested until now), just never reported since tuples were the more
common case people hit. Added ast.Load to the whitelist to cover both.
The non-literal-default-value check (__init__ parameter defaults) only
needed ast.Tuple added - it inspects the top-level default node directly,
not via ast.walk(), so it doesn't have the ctx-node issue.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ErenAta16
left a comment
There was a problem hiding this comment.
Verified the root cause directly: ast.walk() on a List or Tuple node yields a Load node for its ctx field in addition to the element Constants (Set and Dict don't have a ctx field, so they were unaffected). Ran the old vs new allowlist against list_attr, tuple_attr, dict_attr, set_attr directly:
old check passes: list_attr=False, tuple_attr=False, dict_attr=True, set_attr=True
new check passes: list_attr=True, tuple_attr=True, dict_attr=True, set_attr=True
So this fix is actually broader than the title suggests, list-literal class attributes were just as broken as tuples before this (despite ast.List already being in the old allowlist, the uncaught Load child still tripped the all(...) check), and adding ast.Load fixes both. Good that list_attr got added to the test alongside tuple_attr, that's pinning a real second fix, not just a sanity check. Looks correct.
What
validate_tool_attributes()'s "is this attribute a simple literal" check whitelisted(ast.Constant, ast.Dict, ast.List, ast.Set)but notast.Tuple, so anyToolsubclass using an ordinary tuple class attribute (e.g.allowed_exts = (".png", ".jpg")) failed validation with"Complex attributes should be defined in __init__, not as class attributes"— blockingTool.save()/push_to_hub()for a completely standard pattern.Adding
ast.Tuplealone wasn't sufficient: the check usesast.walk()over the whole assignment value, and forTuple/Listnodes that also yields theirast.Load()context node (a Tuple'sctxfield, alwaysLoadfor a value expression) — previously unnoticed becauseDict/Setdon't have actxfield. This meant list literal attributes had the identical latent bug (untested until now), just never reported since tuples were the more common case people hit:The non-literal-default-value check (
__init__parameter defaults) only neededast.Tupleadded — it inspects the top-level default node directly, not viaast.walk(), so it doesn't have thectx-node issue.Fix
Add
ast.Tuple(both checks) andast.Load(theast.walk()-based check only) to the whitelists.Testing
Extended
ValidToolintests/test_tool_validation.pywithtuple_attr,list_attrclass attributes and atuple_default__init__parameter default, covered by the existingtest_validate_tool_attributes_validparametrized test. Confirmed viagit stashthat all three new cases fail against the pre-fix code and pass after;InvalidToolComplexAttrs's genuinely-complex list-comprehension case is still correctly rejected (no false negative introduced).pytest tests/test_tool_validation.py— 16 passed.pytest tests/test_utils.py— 44 passed, no regressions.ruff checkandruff format --checkboth clean on the changed files.AI disclosure
Developed with AI assistance (Claude Code), which located the bug via a targeted search for AST-node-type whitelists that looked incomplete. I reviewed the diff, independently reproduced the crash and the fix by executing both against the actual function (including diagnosing the secondary
ast.Loadissue after the first fix attempt was incomplete), and ran the tests/linters myself before opening this PR.