How would you write a @debuggable decorator in python?

前端 未结 5 752
孤街浪徒
孤街浪徒 2020-12-24 03:57

When debugging, I like to print out all the inputs and outputs of a function (I know I need a better IDE, but humour me, this could be used for error reporting). So, I\'d id

相关标签:
5条回答
  • 2020-12-24 04:21

    You can use as decorator for debug; (@debug)

    def debug(func):
        import functools
    
        @functools.wraps(func)
        def wrapper_debug(*args, **kwargs):
            args_repr = [repr(a) for a in args]  # 1
            kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]  # 2
            signature = ", ".join(args_repr + kwargs_repr)  # 3
            print(f"Call {func.__name__}({signature})")
            value = func(*args, **kwargs)
            print(f"{func.__name__!r} return {value!r}")  # 4
            return value
    
        return wrapper_debug
    
    0 讨论(0)
  • 2020-12-24 04:32

    Use a debugger. Seriously. Decorating every function you want to keep track is a bad idea.

    Python has a debugger included, so you don't need a good IDE.

    If you don't want to use a debugger, you can use the trace function.

    import sys
    
    @sys.settrace
    def trace_debug(frame, event, arg):
        if event == 'call':
            print ("calling %r on line %d, vars: %r" % 
                    (frame.f_code.co_name, 
                     frame.f_lineno,
                     frame.f_locals))
            return trace_debug
        elif event == "return":
            print "returning", arg
    
    def fun1(a, b):
        return a + b
    
    print fun1(1, 2)
    

    That prints:

    calling 'fun1' on line 14, vars: {'a': 1, 'b': 2}
    returning 3
    3
    

    Even easier would be to use Winpdb:

    It is a platform independent graphical GPL Python debugger with support for remote debugging over a network, multiple threads, namespace modification, embedded debugging, encrypted communication and is up to 20 times faster than pdb.

    Features:

    • GPL license. Winpdb is Free Software.
    • Compatible with CPython 2.3 or later.
    • Compatible with wxPython 2.6 or later.
    • Platform independent, and tested on Ubuntu Gutsy and Windows XP.
    • User Interfaces: rpdb2 is console based, while winpdb requires wxPython 2.6 or later.


    (source: winpdb.org)

    0 讨论(0)
  • 2020-12-24 04:35

    I think what you're after isn't really a debugging decorator, but more of a logging decorator.

    It might make sense to use Python's logging module so you can have more fine grained control over the logging itself. For example you would be able to output to a file for later analysing the output.

    The decorator might then look something more like:

    
    import logging
    
    logger = logging.getLogger('TraceLog')
    # TODO configure logger to write to file/stdout etc, it's level etc
    
    
    def logthis(level):
        def _decorator(fn):
            def _decorated(*arg,**kwargs):
                logger.log(level, "calling '%s'(%r,%r)", fn.func_name, arg, kwargs)
                ret=fn(*arg,**kwargs)
                logger.log(level, "called '%s'(%r,%r) got return value: %r", fn.func_name, arg, kwargs, ret)
                return ret
            return _decorated
        return _decorator
    
    @logthis(logging.INFO)
    def myfunc(this,that):
        return this+that
    
    

    Then if you configure the logger to output to stderr you'd see:

    
    >>> logger.setLevel(logging.INFO)
    >>> handler=logging.StreamHandler()
    >>> logger.addHandler(handler)
    >>> myfunc(1,2)
    calling 'myfunc'((1, 2),{})
    called 'myfunc'((1, 2),{}) got return value: 3
    
    
    0 讨论(0)
  • 2020-12-24 04:35

    I second what nosklo said.

    Another thing to notice is that your function is a bit dangerous:

    b = myfunc(1,3)
    

    In this case, "b" is None, because the decorated function doesn't return anything.

    0 讨论(0)
  • 2020-12-24 04:38

    I agree with nosklo using a debugger is much better than writing your own. I'll post an improvement to your code. But I still think you should follow nosklo's advice.

    Use decorator classes to make your debugger neater:

    class Debugger(object):
        enabled = False
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            if self.enabled:
                print 'Entering', self.func.func_name 
                print '    args:', args, kwargs
            return self.func(*args, **kwargs)
    
    Debugger.enabled = True
    
    @Debugger
    def myfunc(a, b, c, d):
        pass
    
    0 讨论(0)
提交回复
热议问题