API migrations are a common task in large codebases. Whether you’re updating a deprecated function, changing parameter names, or modifying return types, Graph-sitter makes it easy to update all call sites consistently.
When updating parameter names across an API, you need to update both the function definition and all call sites:
Copy
Ask AI
# Find the API function to updateapi_function = codebase.get_function("process_data")# Update the parameter nameold_param = api_function.get_parameter("input")old_param.rename("data")# All call sites are automatically updated:# process_data(input="test") -> process_data(data="test")
# Find all call sites before modifying the functioncall_sites = list(api_function.call_sites)# Add the new parameterapi_function.add_parameter("timeout: int")# Update all existing call sites to include the new parameterfor call in call_sites: call.add_argument("timeout=30") # Add with a default value
# Update the parameter typeparam = api_function.get_parameter("user_id")param.type = "UUID" # Change from string to UUID# Find all call sites that need type conversionfor call in api_function.call_sites: arg = call.get_arg_by_parameter_name("user_id") if arg: # Convert string to UUID arg.edit(f"UUID({arg.value})")
When deprecating an old API in favor of a new one:
Copy
Ask AI
old_api = codebase.get_function("old_process_data")new_api = codebase.get_function("new_process_data")# Add deprecation warningold_api.add_decorator('@deprecated("Use new_process_data instead")')# Update all call sites to use the new APIfor call in old_api.call_sites: # Map old arguments to new parameter names args = [ f"data={call.get_arg_by_parameter_name('input').value}", f"timeout={call.get_arg_by_parameter_name('wait').value}" ] # Replace the old call with the new API call.replace(f"new_process_data({', '.join(args)})")
When updating chained method calls, like database queries or builder patterns:
Copy
Ask AI
# Find all query chains ending with .execute()for execute_call in codebase.function_calls: if execute_call.name != "execute": continue # Get the full chain chain = execute_call.call_chain # Example: Add .timeout() before .execute() if "timeout" not in {call.name for call in chain}: execute_call.insert_before("timeout(30)")
When making breaking changes to an API, it’s important to:
Identify all affected call sites
Make changes consistently
Update related documentation
Consider backward compatibility
Here’s a comprehensive example:
Copy
Ask AI
def migrate_api_v1_to_v2(codebase): old_api = codebase.get_function("create_user_v1") # Document all existing call patterns call_patterns = {} for call in old_api.call_sites: args = [arg.source for arg in call.args] pattern = ", ".join(args) call_patterns[pattern] = call_patterns.get(pattern, 0) + 1 print("Found call patterns:") for pattern, count in call_patterns.items(): print(f" {pattern}: {count} occurrences") # Create new API version new_api = old_api.copy() new_api.rename("create_user_v2") # Update parameter types new_api.get_parameter("email").type = "EmailStr" new_api.get_parameter("role").type = "UserRole" # Add new required parameters new_api.add_parameter("tenant_id: UUID") # Update all call sites for call in old_api.call_sites: # Get current arguments email_arg = call.get_arg_by_parameter_name("email") role_arg = call.get_arg_by_parameter_name("role") # Build new argument list with type conversions new_args = [ f"email=EmailStr({email_arg.value})", f"role=UserRole({role_arg.value})", "tenant_id=get_current_tenant_id()" ] # Replace old call with new version call.replace(f"create_user_v2({', '.join(new_args)})") # Add deprecation notice to old version old_api.add_decorator('@deprecated("Use create_user_v2 instead")')# Run the migrationmigrate_api_v1_to_v2(codebase)
Analyze First: Before making changes, analyze all call sites to understand usage patterns
Copy
Ask AI
# Document current usagefor call in api.call_sites: print(f"Called from: {call.parent_function.name}") print(f"With args: {[arg.source for arg in call.args]}")
Make Atomic Changes: Update one aspect at a time
Copy
Ask AI
# First update parameter namesparam.rename("new_name")# Then update typesparam.type = "new_type"# Finally update call sitesfor call in api.call_sites: # ... update calls
Maintain Backwards Compatibility:
Copy
Ask AI
# Add new parameter with defaultapi.add_parameter("new_param: str = None")# Later make it requiredapi.get_parameter("new_param").remove_default()
Remember to test thoroughly after making bulk changes to APIs. While Graph-sitter ensures syntactic correctness, you’ll want to verify the semantic correctness of the changes.