import ast import os import re def is_venv_directory(path): """ Check if the path contains virtual environment directories. Common virtual environment directory names: venv, env, .env, myenv, .venv """ venv_indicators = [ "venv", "env", ".env", "myenv", ".venv", "virtualenv", "site-packages", ] path_parts = path.lower().split(os.sep) return any(indicator in path_parts for indicator in venv_indicators) class ArgsStringVisitor(ast.NodeVisitor): """ AST visitor that finds all instances of '{args}' string usage. """ def __init__(self): self.args_locations = [] self.current_file = None def set_file(self, filename): self.current_file = filename self.args_locations = [] def visit_Str(self, node): """Check string literals for {args}""" if "{args}" in node.s: self.args_locations.append( { "line": node.lineno, "col": node.col_offset, "text": node.s, "file": self.current_file, } ) def visit_JoinedStr(self, node): """Check f-strings for {args}""" for value in node.values: if isinstance(value, ast.FormattedValue): # Check if the formatted value uses 'args' if isinstance(value.value, ast.Name) and value.value.id == "args": self.args_locations.append( { "line": node.lineno, "col": node.col_offset, "text": "f-string with {args}", "file": self.current_file, } ) def check_file_for_args_string(file_path): """ Analyzes a Python file for any usage of '{args}'. Args: file_path (str): Path to the Python file to analyze Returns: list: List of dictionaries containing information about {args} usage """ with open(file_path, "r", encoding="utf-8") as file: try: content = file.read() tree = ast.parse(content) # First check using AST for more accurate detection in strings visitor = ArgsStringVisitor() visitor.set_file(file_path) visitor.visit(tree) ast_locations = visitor.args_locations # Also check using regex for any instances we might have missed # (like in comments or docstrings) line_number = 1 additional_locations = [] for line in content.split("\n"): if "{args}" in line: # Only add if it's not already caught by the AST visitor if not any(loc["line"] == line_number for loc in ast_locations): additional_locations.append( { "line": line_number, "col": line.index("{args}"), "text": line.strip(), "file": file_path, } ) line_number += 1 return ast_locations + additional_locations except SyntaxError as e: print(f"Syntax error in {file_path}: {e}") return [] def check_directory_for_args_string(directory_path): """ Recursively checks all Python files in a directory for '{args}' usage, excluding virtual environment directories. Args: directory_path (str): Path to the directory to check """ all_violations = [] for root, dirs, files in os.walk(directory_path): # Skip virtual environment directories if is_venv_directory(root): continue for file in files: if file.endswith(".py"): file_path = os.path.join(root, file) violations = check_file_for_args_string(file_path) all_violations.extend(violations) return all_violations def main(): # Update this path to point to your codebase root directory # codebase_path = "../../litellm" # Adjust as needed codebase_path = "./litellm" violations = check_directory_for_args_string(codebase_path) if violations: print("Found '{args}' usage in the following locations:") for violation in violations: print(f"- {violation['file']}:{violation['line']} - {violation['text']}") raise Exception( f"Found {len(violations)} instances of '{{args}}' usage in the codebase" ) else: print("No '{args}' usage found in the codebase.") if __name__ == "__main__": main()