Apply Black formatting only in regions changed since last commit
—
AST verification utilities for ensuring code transformations preserve semantic equivalence. These functions validate that reformatting doesn't change the abstract syntax tree structure of Python code.
Core classes for verifying AST equivalence between original and reformatted code.
class ASTVerifier:
"""
Verify if reformatted TextDocument is AST-equivalent to baseline.
Keeps in-memory data about previous comparisons to improve performance.
"""
def __init__(self, baseline: TextDocument) -> None:
"""
Initialize verifier with baseline document.
Parameters:
- baseline: The baseline document to compare against
"""
def is_equivalent_to_baseline(self, document: TextDocument) -> bool:
"""
Returns true if document is AST-equivalent to baseline.
Parameters:
- document: Document to compare against baseline
Returns:
True if documents are AST-equivalent, False otherwise
"""
class BinarySearch:
"""
Effectively search the first index for which a condition is True.
Used for finding the minimal change that breaks AST equivalence.
"""
def __init__(self, low: int, high: int) -> None:
"""
Initialize binary search with range bounds.
Parameters:
- low: Lower bound of search range
- high: Upper bound of search range
"""
def get_next(self) -> int:
"""Get the next integer index to try."""
def respond(self, value: bool) -> None:
"""
Provide a False or True answer for the current integer index.
Parameters:
- value: Response for current index
"""
@property
def found(self) -> bool:
"""Return True if the lowest True response index has been found."""
@property
def result(self) -> int:
"""Return the lowest index with a True response."""Functions for parsing and comparing Python ASTs with error handling.
def parse_ast(src: str) -> ast.AST:
"""
Parse source code with fallback for type comments.
This function tries multiple Python version feature sets
and handles both type comments and syntax variations.
Parameters:
- src: Python source code to parse
Returns:
Parsed AST node
Raises:
SyntaxError: If source cannot be parsed with any supported syntax
"""
def stringify_ast(node: ast.AST) -> Iterator[str]:
"""
Generate strings to compare ASTs by content using a simple visitor.
Parameters:
- node: Root AST node to stringify
Yields:
String representations of AST nodes for comparison
"""Exceptions specific to AST verification operations.
class NotEquivalentError(Exception):
"""Exception to raise if two ASTs being compared are not equivalent."""from darker.verification import ASTVerifier
from darkgraylib.utils import TextDocument
# Set up verification with baseline code
original_code = TextDocument.from_str('''
def hello(name):
print(f"Hello, {name}!")
''')
verifier = ASTVerifier(original_code)
# Check if reformatted code is equivalent
formatted_code = TextDocument.from_str('''
def hello(name):
print(f"Hello, {name}!")
''')
is_equivalent = verifier.is_equivalent_to_baseline(formatted_code)
print(f"Code is AST-equivalent: {is_equivalent}")from darker.verification import BinarySearch
# Find first line where condition becomes true
search = BinarySearch(0, 100)
while not search.found:
line_num = search.get_next()
# Test some condition on line_num
condition_met = some_test_function(line_num)
search.respond(condition_met)
print(f"First line where condition is true: {search.result}")from darker.verification import parse_ast, stringify_ast
try:
source = '''
def example() -> None:
x: int = 42
return None
'''
ast_tree = parse_ast(source)
ast_strings = list(stringify_ast(ast_tree))
print("AST structure:")
for line in ast_strings[:10]: # Show first 10 lines
print(line)
except SyntaxError as e:
print(f"Could not parse source: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-darker