Organizing Your Codebase
Codegen SDK provides a powerful set of tools for deterministically moving code safely and efficiently. This guide will walk you through the basics of moving code with Codegen SDK.
Common use cases include:
Splitting up large files
print(f"š Processing file: {filepath}")
file = codebase.get_file(filepath)
# Get the directory path for creating new files
dir_path = file.directory.path if file.directory else ""
# Iterate through all functions in the file
for function in file.functions:
# Create new filename based on function name
new_filepath = f"{dir_path}/{function.name}.py"
print(f"š Creating new file: {new_filepath}")
# Create the new file
new_file = codebase.create_file(new_filepath)
# Move the function to the new file, including dependencies
print(f"ā”ļø Moving function: {function.name}")
function.move_to_file(new_file, include_dependencies=True)Organize code into modules
# Dictionary to track modules and their functions
module_map = {
"utils": lambda f: f.name.startswith("util_") or f.name.startswith("helper_"),
"api": lambda f: f.name.startswith("api_") or f.name.startswith("endpoint_"),
"data": lambda f: f.name.startswith("data_") or f.name.startswith("db_"),
"core": lambda f: True # Default module for other functions
}
print("š Starting code organization...")
# Create module directories if they don't exist
for module in module_map.keys():
if not codebase.has_directory(module):
print(f"š Creating module directory: {module}")
codebase.create_directory(module, exist_ok=True)
# Process each file in the codebase
for file in codebase.files:
print(f"\nš Processing file: {file.filepath}")
# Skip if file is already in a module directory
if any(file.filepath.startswith(module) for module in module_map.keys()):
continue
# Process each function in the file
for function in file.functions:
# Determine which module this function belongs to
target_module = next(
(module for module, condition in module_map.items()
if condition(function)),
"core"
)
# Create the new file path
new_filepath = f"{target_module}/{function.name}.py"
print(f" ā”ļø Moving {function.name} to {target_module} module")
# Create new file and move function
if not codebase.has_file(new_filepath):
new_file = codebase.create_file(new_filepath)
function.move_to_file(new_file, include_dependencies=True)
print("\nā
Code organization complete!")Break up import cycles
# Create a graph to detect cycles
import networkx as nx
# Build dependency graph
G = nx.DiGraph()
# Add edges for imports between files
for file in codebase.files:
for imp in file.imports:
if imp.from_file:
G.add_edge(file.filepath, imp.from_file.filepath)
# Find cycles in the graph
cycles = list(nx.simple_cycles(G))
if not cycles:
print("ā
No import cycles found!")
exit()
print(f"š Found {len(cycles)} import cycles")
# Process each cycle
for cycle in cycles:
print(f"\nā Processing cycle: {' -> '.join(cycle)}")
# Get the first two files in the cycle
file1 = codebase.get_file(cycle[0])
file2 = codebase.get_file(cycle[1])
# Find functions in file1 that are used by file2
for function in file1.functions:
if any(usage.file == file2 for usage in function.usages):
# Create new file for the shared function
new_filepath = f"shared/{function.name}.py"
print(f" ā”ļø Moving {function.name} to {new_filepath}")
if not codebase.has_directory("shared"):
codebase.create_directory("shared")
new_file = codebase.create_file(new_filepath)
function.move_to_file(new_file, include_dependencies=True)
print("\nā
Import cycles resolved!")Most operations in Graph-sitter will automatically handle updaging dependencies and imports. See Moving Symbols to learn more.
Basic Symbol Movement
To move a symbol from one file to another, you can use the move_to_file method.
# Get the symbol
symbol_to_move = source_file.get_symbol("my_function")
# Pick a destination file
dst_file = codebase.get_file("path/to/dst/location.py")
# Move the symbol, move all of its dependencies with it (remove from old file), and add an import of symbol into old file
symbol_to_move.move_to_file(dst_file, include_dependencies=True, strategy="add_back_edge")# Get the symbol
symbol_to_move = source_file.get_symbol("myFunction")
# Pick a destination file
dst_file = codebase.get_file("path/to/dst/location.ts")
# Move the symbol, move all of its dependencies with it (remove from old file), and add an import of symbol into old file
symbol_to_move.move_to_file(dst_file, include_dependencies=True, strategy="add_back_edge")This will move my_function to path/to/dst/location.py, safely updating all references to it in the process.
Updating Imports
After moving a symbol, you may need to update imports throughout your codebase. GraphSitter offers two strategies for this:
- Update All Imports: This strategy updates all imports across the codebase to reflect the new location of the symbol.
symbol_to_move = codebase.get_symbol("symbol_to_move")
dst_file = codebase.create_file("new_file.py")
symbol_to_move.move_to_file(dst_file, strategy="update_all_imports")symbol_to_move = codebase.get_symbol("symbolToMove")
dst_file = codebase.create_file("new_file.ts")
symbol_to_move.move_to_file(dst_file, strategy="update_all_imports")- Add Back Edge: This strategy adds an import in the original file that re-imports (and exports) the moved symbol, maintaining backwards compatibility. This will result in fewer total modifications, as existing imports will not need to be updated.
symbol_to_move = codebase.get_symbol("symbol_to_move")
dst_file = codebase.create_file("new_file.py")
symbol_to_move.move_to_file(dst_file, strategy="add_back_edge")symbol_to_move = codebase.get_symbol("symbolToMove")
dst_file = codebase.create_file("new_file.ts")
symbol_to_move.move_to_file(dst_file, strategy="add_back_edge")Handling Dependencies
By default, Graph-sitter will move all of a symbols dependencies along with it. This ensures that your codebase remains consistent and functional.
my_symbol = codebase.get_symbol("my_symbol")
dst_file = codebase.create_file("new_file.py")
my_symbol.move_to_file(dst_file, include_dependencies=True)my_symbol = codebase.get_symbol("mySymbol")
dst_file = codebase.create_file("new_file.ts")
my_symbol.move_to_file(dst_file, include_dependencies=True)If you set include_dependencies=False, only the symbol itself will be moved, and any dependencies will remain in the original file.
Moving Multiple Symbols
If you need to move multiple symbols, you can do so in a loop:
source_file = codebase.get_file("path/to/source_file.py")
dest_file = codebase.get_file("path/to/destination_file.py")
# Create a list of symbols to move
symbols_to_move = [source_file.get_function("my_function"), source_file.get_class("MyClass")]
# Move each symbol to the destination file
for symbol in symbols_to_move:
symbol.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")Best Practices
-
Commit After Major Changes: If you're making multiple significant changes, use
codebase.commit()between them to ensure the codebase graph is up-to-date. -
Re-fetch References: After a commit, re-fetch any file or symbol references you're working with, as they may have become stale.
-
Handle Errors: Be prepared to handle cases where symbols or files might not exist, or where moves might fail due to naming conflicts.
By following these guidelines, you can effectively move symbols around your codebase while maintaining its integrity and functionality.