Skip to content

Commit 1f4ca53

Browse files
fix: Ensure backward compatibility for Pydantic v1/v2 in hasattr checks
- Fix inconsistent hasattr checks in ZeroScript_AI_TestExecutor.ipynb - Update fix_notebook.py to handle both method call and hasattr replacement - Add comprehensive backward compatibility test suite - Ensure Pydantic v1 models fallback to str() when model_dump() unavailable Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
1 parent b9411e5 commit 1f4ca53

File tree

3 files changed

+118
-5
lines changed

3 files changed

+118
-5
lines changed

examples/cookbooks/Programming_Code_Analysis_Agents/ZeroScript_AI_TestExecutor.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@
319319
" out = Path(\"data/screenshots\") / f\"{t['id']}.png\"\n",
320320
" out.write_bytes(base64.b64decode(imgs[-1]))\n",
321321
" result_obj = r.final_result() if hasattr(r, \"final_result\") else str(r)\n",
322-
" res[\"result\"] = result_obj.model_dump() if hasattr(result_obj, \"dict\") else str(result_obj)\n",
322+
" res[\"result\"] = result_obj.model_dump() if hasattr(result_obj, \"model_dump\") else str(result_obj)\n",
323323
" except Exception as e:\n",
324324
" res[\"error\"] = str(e)\n",
325325
" results.append(res)\n",

fix_notebook.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ def fix_notebook():
2020
# Convert source to string if it's a list
2121
source_str = ''.join(source) if isinstance(source, list) else source
2222

23-
# Check if this cell contains the problematic .dict() usage
24-
if 'result_obj.dict()' in source_str:
23+
# Check if this cell contains the problematic .dict() usage or hasattr check
24+
if 'result_obj.dict()' in source_str or 'hasattr(result_obj, "dict")' in source_str:
2525
print(f"Found cell with .dict() usage, fixing...")
26-
# Replace .dict() with .model_dump()
27-
fixed_source = source_str.replace('result_obj.dict()', 'result_obj.model_dump()')
26+
# Replace .dict() with .model_dump() and fix hasattr check for backward compatibility
27+
fixed_source = source_str.replace('result_obj.dict()', 'result_obj.model_dump()').replace('hasattr(result_obj, "dict")', 'hasattr(result_obj, "model_dump")')
2828

2929
# Convert back to list format if needed
3030
if isinstance(source, list):

test_backward_compatibility.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test the backward compatibility fix for Pydantic .dict() → .model_dump() migration
4+
"""
5+
6+
class MockPydanticV1:
7+
"""Mock Pydantic v1 model that has .dict() but not .model_dump()"""
8+
def __init__(self, data):
9+
self.data = data
10+
11+
def dict(self):
12+
return self.data
13+
14+
def __str__(self):
15+
return str(self.data)
16+
17+
class MockPydanticV2:
18+
"""Mock Pydantic v2 model that has both .dict() and .model_dump()"""
19+
def __init__(self, data):
20+
self.data = data
21+
22+
def dict(self):
23+
return self.data
24+
25+
def model_dump(self):
26+
return self.data
27+
28+
def __str__(self):
29+
return str(self.data)
30+
31+
class MockNonPydantic:
32+
"""Mock non-Pydantic object"""
33+
def __init__(self, data):
34+
self.data = data
35+
36+
def __str__(self):
37+
return str(self.data)
38+
39+
def test_notebook_backward_compatibility():
40+
"""Test the fixed notebook logic with different object types"""
41+
42+
print("Testing notebook backward compatibility fix...")
43+
44+
# Test cases
45+
test_objects = [
46+
("Pydantic v1 mock", MockPydanticV1({"test": "data1"})),
47+
("Pydantic v2 mock", MockPydanticV2({"test": "data2"})),
48+
("Non-Pydantic object", MockNonPydantic({"test": "data3"})),
49+
("String object", "just a string"),
50+
("None object", None),
51+
]
52+
53+
for name, result_obj in test_objects:
54+
print(f"\nTesting {name}:")
55+
56+
# This is the fixed logic from the notebook
57+
try:
58+
result = result_obj.model_dump() if hasattr(result_obj, "model_dump") else str(result_obj)
59+
print(f" ✅ Success: {result}")
60+
except Exception as e:
61+
print(f" ❌ Failed: {e}")
62+
return False
63+
64+
print("\n✅ All backward compatibility tests passed!")
65+
return True
66+
67+
def test_api_files_syntax():
68+
"""Test that the API files have valid syntax"""
69+
70+
print("\nTesting API files syntax...")
71+
72+
files_to_check = [
73+
"examples/python/concepts/reasoning-extraction.py",
74+
"examples/python/api/secondary-market-research-api.py"
75+
]
76+
77+
for file_path in files_to_check:
78+
try:
79+
print(f" Checking {file_path}...")
80+
with open(file_path, 'r') as f:
81+
code = f.read()
82+
83+
# Check that .model_dump() is used instead of .dict()
84+
if '.dict()' in code and 'result_obj.dict()' in code:
85+
print(f" ❌ Found remaining .dict() usage in {file_path}")
86+
return False
87+
88+
# Compile the code to check syntax
89+
compile(code, file_path, 'exec')
90+
print(f" ✅ {file_path} has valid syntax")
91+
92+
except Exception as e:
93+
print(f" ❌ Error checking {file_path}: {e}")
94+
return False
95+
96+
print("✅ All API files have valid syntax!")
97+
return True
98+
99+
if __name__ == "__main__":
100+
print("=" * 60)
101+
print("TESTING BACKWARD COMPATIBILITY FIXES")
102+
print("=" * 60)
103+
104+
success = True
105+
success &= test_notebook_backward_compatibility()
106+
success &= test_api_files_syntax()
107+
108+
if success:
109+
print("\n🎉 All backward compatibility tests passed!")
110+
print("The fixes ensure compatibility with both Pydantic v1 and v2")
111+
else:
112+
print("\n❌ Some tests failed")
113+
exit(1)

0 commit comments

Comments
 (0)