8
\$\begingroup\$

This is a follow-up to this question.


I've refactored my previous debugging decorator, and added a couple new features, and changed a few things. Here's a complete list of things that have changed:

  • There is only one decorator, Debug, and it now supports functions, and class methods.
  • Each debug message is prefixed with [debug] to help distinguish it from normal output.
  • The output now tells you what it's outputting, rather than just outputting unreadable data.
  • The decorator will now output local variables names, along with argument and keyword argument names as well.

I'm wondering the following:

  • Is there a way to get the values of local variables in the function, or is that just not possible?
  • Is there a shorter way to get the names of local variables than function.__code__.co_varnames?
  • Is it a good idea to create an empty string, and then add to, and re-assign it to build an output string?
  • Is this Python 3, and Python 2.7 compatible?
  • How's my documentation?
  • Is this code "pythonic"?

debug.py

from pprint import pformat
from inspect import getargspec
class Debug(object):
 """Decorator for debugging functions.
 This decorator is used to debug a function, or
 a class method. If this is applied to a normal
 function, it will print out the arguments of
 Keyword arguments:
 debug -- Whether or not you want to output debug info. Generally, a global DEBUG variable is passed in here.
 """
 def __init__(self, debug=True):
 self.debug = debug
 def __format_debug_string(self, function, *args, **kwargs):
 """Return a formatted debug string.
 This is a small private helper function that will
 return a string value with certain debug information.
 Keyword arguments:
 function -- The function to debug.
 *args -- The normal arguments of the function.
 **kwargs -- The keyword arguments of the function.
 """
 debug_string = ""
 debug_string += "[debug] {}\n".format(pformat(function))
 debug_string += "[debug] Passed args: {}\n".format(pformat(args))
 debug_string += "[debug] Passed kwargs: {}\n".format(pformat(kwargs))
 debug_string += "[debug] Locals: {}".format(pformat(function.__code__.co_varnames))
 return debug_string
 def __call__(self, function):
 def wrapper(*args, **kwargs):
 if self.debug:
 if getargspec(function).args[0] != "self":
 print(self.__format_debug_string(function, *args, **kwargs))
 else:
 print(self.__format_debug_string(function, *args, **kwargs))
 print("[debug] Parent attributes: {}".format(pformat(args[0].__dict__)))
 return function(*args, **kwargs)
 return wrapper

Here are a few small, albeit unreadable tests, but it's good enough to get the point across:

from debug import Debug
@Debug(debug=True)
def a(a, b):
 d = 10
 return a * b
print(a(10, 10))
class B(object):
 def __init__(self, a, b):
 self.a = a
 self.b = b
 @Debug(debug=True)
 def e(self, c):
 return self.a * self.b * c
c = B(10, 10)
print(c.e(10))

Here's the output of these tests:

[debug] <function a at 0x1bf9d38>
[debug] Passed args: (10, 10)
[debug] Passed kwargs: {}
[debug] Locals: ('a', 'b', 'd')
100
[debug] <function B.e at 0x1944ce8>
[debug] Passed args: (<B object at 0x1bfc838>, 10)
[debug] Passed kwargs: {}
[debug] Locals: ('self', 'c')
[debug] Parent attributes: {'a': 10, 'b': 10}
1000
dfhwze
14.1k3 gold badges40 silver badges101 bronze badges
asked Sep 9, 2015 at 21:45
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

You can improve the following:

 if getargspec(function).args[0] != "self":
 print(self.__format_debug_string(function, *args, **kwargs))
 else:
 print(self.__format_debug_string(function, *args, **kwargs))
 print("[debug] Parent attributes: {}".format(pformat(args[0].__dict__)))

If the code is executed no matter the statement, and it always goes first, move it above the condition: (and don't forget to reverse the condition)

 print(self.__format_debug_string(function, *args, **kwargs))
 if getargspec(function).args[0] == "self":
 print("[debug] Parent attributes: {}".format(pformat(args[0].__dict__)))

As for this:

 debug_string = ""
 debug_string += "[debug] {}\n".format(pformat(function))
 debug_string += "[debug] Passed args: {}\n".format(pformat(args))
 debug_string += "[debug] Passed kwargs: {}\n".format(pformat(kwargs))
 debug_string += "[debug] Locals: {}".format(pformat(function.__code__.co_varnames))
 return debug_string

You can remove the = "" entirely:

 debug_string = "[debug] {}\n".format(pformat(function))
 debug_string += "[debug] Passed args: {}\n".format(pformat(args))
 debug_string += "[debug] Passed kwargs: {}\n".format(pformat(kwargs))
 return debug_string + "[debug] Locals: {}".format(pformat(function.__code__.co_varnames))

It may not look as visually stimulating, but, it's not as redundant.

Is it a good idea to create an empty string, and then add to, and re-assign it to build an output string?

If you were directly printing these then it would be a bad idea, but in this case, not really. However, I suppose you could move them to an object, or an array and return the result of a join function.

You could even return it as an array, and print each [debug] result. Which would remove the need for the \ns at the end, and DRY up the [debug] at the beginning of the strings (put it in the loop, not altogether)


You've got a few too long lines, by PEP8 standard:

debug -- Whether or not you want to output debug info. Generally, a global DEBUG variable is passed in here.
debug_string += "[debug] Locals: {}".format(pformat(function.__code__.co_varnames))
print(self.__format_debug_string(function, *args, **kwargs))
print("[debug] Parent attributes: {}".format(pformat(args[0].__dict__)))

As for your documentation:

function, it will print out the arguments of
Keyword arguments:

I'm a bit confused by that, grammatically.

__call__ is a more complex function (in my mind, at least) than __format_debug_string, but it has no documentation.


Is this Python 3, and Python 2.7 compatible?

It ran fine when I tested it in Python 2.7.9 and 3.1.1

answered Sep 10, 2015 at 1:03
\$\endgroup\$
1
  • \$\begingroup\$ It doesn't have the ending underscores because it would imply that I'm overloading a "magic method". \$\endgroup\$ Commented Sep 10, 2015 at 16:01

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.