Skip to content

Constable lets you be lazy by inserting prints directly into your AST for stateful debugging 🤹‍♂️

License

Notifications You must be signed in to change notification settings

saurabh0719/constable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


📝 This is an experimental project that tweaks the AST. Use at your own risk in mission-critical environments, or with unknown agents, as compiling and executing code during runtime can cause unwanted side effects. For all use cases that matter, use pdb instead.


If you find yourself aimlessly adding ✨ print ✨ statements while debugging your code, this is for you. 🤝

Constable inserts print statements directly into the AST at runtime to print variable assignments and other details.

It turns this 🔽 ....

@constable.trace('a', 'b')
def do_something(a, b):
    a = a + b

.... into this 🔽 during runtime

# During runtime, print statements will be added for every assignment on 'a' & 'b'.
# Resulting in something like -
def do_something(a, b):
    a = a + b
    print(f"wowww i wonder who put this print here! a = {a}")

See the examples below

$ pip install constable

How does it work?

The constable.trace decorator uses Python's Abstract Syntax Tree (AST) in much the same way we add print(s) to debug states. During runtime, it prepares and inserts print statements into the function's AST after every assignment operation (ast.Assign, ast.AugAssign and ast.AnnAssign), and then executes the modified code in a separate namespace with exec.

Print variable assignments and execution info.

Monitor the state of specified variables at each assignment operation with a step-by-step view of variable changes!

import constable

@constable.trace('a', 'b')
def example(a, b):
    a = a + b
    c = a
    a = "Experimenting with the AST"
    b = c + b
    a = c + b
    return a

example(5, 6)

Output -

constable: example: line 5
    a = a + b
    a = 11
    type(a) = <class 'int'>

constable: example: line 7
    a = "Experimenting with the AST"
    a = Experimenting with the AST
    type(a) = <class 'str'>

constable: example: line 8
    b = c + b
    b = 17
    type(b) = <class 'int'>

constable: example: line 9
    a = c + b
    a = 28
    type(a) = <class 'int'>

constable: example: line 3 to 10
    args: (5, 6)
    kwargs: {}
    returned: 28
    execution time: 0.00018480 seconds

You can also use it on its own to track function execution info.

import constable

@constable.trace()
def add(a, b):
    return a + b

add(5, 6)

Output -

constable: add: line 3 to 5
    args: (5, 6)
    kwargs: {}
    returned: 11
    execution time: 0.00004312 seconds

@trace

The trace function is the decorator to add print statements into the AST.

def trace(
    *variables,
    exec_info=True,
    verbose=True,
    use_spaces=True,
    max_len=None,
):
    """
    An experimental decorator for tracing function execution using AST.

    Args:
        variables (list): List of variable names to trace.
        exec_info (bool, optional): Whether to print execution info.
        verbose (bool, optional): Whether to print detailed trace info.
        use_spaces (bool, optional): Whether to add empty lines for readability.
        max_len (int, optional): Max length of printed values. Truncates if exceeded.

    Returns:
        function: Decorator for function tracing.
    """

About

Constable lets you be lazy by inserting prints directly into your AST for stateful debugging 🤹‍♂️

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages