ast-grep-cli
ast-grep-cli is a Python package that provides a command-line interface (CLI) wrapper for the powerful `ast-grep` tool. `ast-grep` enables structural search and rewrite of code across various programming languages using abstract syntax tree (AST) patterns. The project is actively developed with frequent minor releases, often weekly or bi-weekly, incorporating new features, bug fixes, and language support updates. The current version is 0.42.1.
Warnings
- breaking The exit code behavior of `sg` changed in versions 0.40.2 and 0.40.5. Previously, `sg` would often return 0 even if no matches were found. Now, it correctly returns a non-zero exit code (typically 1) when no matches are found, indicating a 'failure' to find results. This affects scripting where the absence of matches might have been interpreted as success.
- gotcha Due to the rapid development of the underlying `ast-grep` tool, CLI arguments, supported languages, and default behaviors can evolve quickly between minor versions. This means scripts or configurations written for one version might behave differently or break with another.
- gotcha The `ast-grep-cli` Python package primarily serves as an installer for the `sg` command-line executable. There is no public, stable Python API designed for programmatic interaction as a traditional library (i.e., by importing classes/functions to manipulate ASTs directly within Python).
- breaking The format and validation rules for `ast-grep` configuration files (e.g., `sgconfig.yml`) can change across versions. For instance, support for `.yaml` extension was fixed in 0.40.0, and unknown keys for patterns were rejected in 0.40.2, indicating stricter parsing.
- gotcha Support for specific programming languages and their respective parsers can fluctuate or be refined across `ast-grep` versions. For example, Dart support was re-added in version 0.42.1, indicating that language availability or parser quality can change.
Install
-
pip install ast-grep-cli
Imports
- main
from ast_grep_cli import main
Quickstart
import subprocess
import os
import json
# Create a dummy file for demonstration
dummy_js_code = """
function greet(name) {
console.log("Hello, " + name);
}
greet("World");
"""
with open("example.js", "w") as f:
f.write(dummy_js_code)
try:
# Example 1: Search for 'console.log($$$)' pattern and get JSON output
print("--- Searching for console.log statements (JSON output) ---")
command_search = [
"sg",
"run",
"--pattern",
"console.log($$$)",
"--lang",
"javascript",
"--path",
"example.js",
"--json" # Use JSON output for programmatic parsing
]
search_result = subprocess.run(
command_search,
capture_output=True,
text=True,
check=False # Do not raise CalledProcessError if no matches (returns non-zero exit code)
)
print(f"Search Exit Code: {search_result.returncode}")
if search_result.stdout:
try:
json_output = json.loads(search_result.stdout)
print("Parsed JSON Output:", json.dumps(json_output, indent=2))
except json.JSONDecodeError:
print("Stdout (not valid JSON):", search_result.stdout)
if search_result.stderr:
print(f"Search Stderr:\n{search_result.stderr}")
# Example 2: Perform a dry-run rewrite from 'console.log' to 'alert'
print("\n--- Performing a dry-run rewrite (stdout output) ---")
command_rewrite_dry_run = [
"sg",
"run",
"--pattern",
"console.log($$$)",
"--rewrite",
"alert($$$)",
"--lang",
"javascript",
"--path",
"example.js",
"--dry-run" # Show changes without modifying the file
]
rewrite_result = subprocess.run(
command_rewrite_dry_run,
capture_output=True,
text=True,
check=False
)
print(f"Rewrite Dry-Run Exit Code: {rewrite_result.returncode}")
print(f"Rewrite Dry-Run Stdout:\n{rewrite_result.stdout}")
if rewrite_result.stderr:
print(f"Rewrite Dry-Run Stderr:\n{rewrite_result.stderr}")
except FileNotFoundError:
print("Error: 'sg' command not found. Ensure ast-grep-cli is installed and in your PATH.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
if os.path.exists("example.js"):
os.remove("example.js")