Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save datavudeja/a89c348b7673d8702efb6f87d7e5a759 to your computer and use it in GitHub Desktop.
Save datavudeja/a89c348b7673d8702efb6f87d7e5a759 to your computer and use it in GitHub Desktop.
One of the most painful code I've wrote, a decorator to log variables in a function every time they set/get. Notice the recursion part πŸ˜”
from functools import wraps
from typing import Any, Callable, TypeVar
F = TypeVar("F", bound=Callable[..., Any])
def log_variables(func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
if not hasattr(wrapper, "initialized"):
wrapper.call_history, wrapper.depth = [], 0
wrapper.initialized = True
# print("Initialized call history and depth.")
# print(f"Call at depth {wrapper.depth} with args: {args}")
wrapper.depth += 1
locals_copy = {func.__code__.co_varnames[i]: arg for i, arg in enumerate(args)}
locals_copy["call_depth"] = wrapper.depth - 1
wrapper.call_history.append(locals_copy)
result = func(*args, **kwargs)
wrapper.depth -= 1
if wrapper.depth == 0:
# print("Final call history:")
for call in wrapper.call_history:
print(
f"Depth {call['call_depth']}: {', '.join(f'{k}: {v}' for k, v in call.items() if k != 'call_depth')}"
)
wrapper.call_history.clear()
return result
return wrapper
@log_variables
def factorial(n: int, acc: int = 1) -> int:
"""Computes factorial of n using tail recursion with accumulator acc."""
return acc if n == 0 else factorial(n - 1, n * acc)
print(factorial(5))
In [1]: from functools import wraps
...: from typing import Any, Callable, TypeVar
In [2]: F = TypeVar("F", bound=Callable[..., Any])
In [3]: def log_variables(func: F) -> F:
...: @wraps(func)
...: def wrapper(*args, **kwargs) -> Any:
...: if not hasattr(wrapper, "initialized"):
...: wrapper.call_history, wrapper.depth = [], 0
...: wrapper.initialized = True
...: print("Initialized call history and depth.")
...:
...: print(f"Call at depth {wrapper.depth} with args: {args}")
...: wrapper.depth += 1
...:
...: locals_copy = {func.__code__.co_varnames[i]: arg for i, arg in enumerate(args)}
...: locals_copy["call_depth"] = wrapper.depth - 1
...: wrapper.call_history.append(locals_copy)
...:
...: result = func(*args, **kwargs)
...: wrapper.depth -= 1
...:
...: if wrapper.depth == 0:
...: print("Final call history:")
...: for call in wrapper.call_history:
...: print(
...: f"Depth {call['call_depth']}: {', '.join(f'{k}: {v}' for k, v in call.items() if k != 'call_depth')}"
...: )
...: wrapper.call_history.clear()
...:
...: return result
...:
...: return wrapper
In [4]: @log_variables
...: def factorial(n: int, acc: int = 1) -> int:
...: """Computes factorial of n using tail recursion with accumulator acc."""
...: return acc if n == 0 else factorial(n - 1, n * acc)
In [5]: print(factorial(5))
Initialized call history and depth.
Call at depth 0 with args: (5,)
Call at depth 1 with args: (4, 5)
Call at depth 2 with args: (3, 20)
Call at depth 3 with args: (2, 60)
Call at depth 4 with args: (1, 120)
Call at depth 5 with args: (0, 120)
Final call history:
Depth 0: n: 5
Depth 1: n: 4, acc: 5
Depth 2: n: 3, acc: 20
Depth 3: n: 2, acc: 60
Depth 4: n: 1, acc: 120
Depth 5: n: 0, acc: 120
120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment