Your are looking for python decorators:
from functools import wraps
def debugger(func):
@wraps(func)
def with_logging(*args, **kwargs):
print('"'+func.__name__+'({},{})"'.format(*args, **kwargs)+" was invoked")
# -------------------
# Your logic here
# -------------------
return func(*args, **kwargs)
return with_logging
@debugger
def test(i : int, s: str) -> int:
print('We are in test', i, s)
test(10, 'hello')
EDIT
Since the decorator method mentioned above interferes with the source code (have to apply the @ decorators), I propose the following:
# This is source code to observe, should not be _touched_!
class SourceCode:
def __init__(self, label):
self.label = label
def test1(self, i, s):
print('For object labeled {}, we are in {} with param {}, {}'.format(self.label, 'test1', i, s))
def test2(self, k):
print('For object labeled {}, we are in {} with param {}'.format(self.label, 'test2', k))
What I propose is perform some manual effort in writing the hooks, I am not sure if this is feasible (just occured to me, hence adding):
from functools import wraps
# the following is pretty much syntactic and generic
def hook(exist_func, debugger):
@wraps(exist_func)
def run(*args, **kwargs):
return debugger(exist_func, *args, **kwargs)
return run
# here goes your debugger
def myobserver(orig_func, *args, **kwargs):
# -----------------------
# Your logic goes here
# -----------------------
print('Inside my debugger')
return orig_func(*args, **kwargs)
# ---------------------------------
obj = SourceCode('Test')
# making the wrapper ready to receive
no_iterference_hook1 = hook(obj.test1, myobserver)
no_iterference_hook2 = hook(obj.test2, myobserver)
# call your debugger on the function of your choice
no_iterference_hook1(10, 'hello')
no_iterference_hook2('Another')