<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Notes from David Beazley's Python3 Metaprogramming tutorial (2013)
"ported" to Python 2.7, unless noted otherwise
A Debugging Decorator
Step1:  Decorators with arguments
Calling convention
python
@decorator(args)
def func()
Step2:  Decorators with arguments
Step3:  Debug with arguments
Step4:  Decorators with arguments
Step5:  Class decorators
decorate all methods of a class at once
NOTE
Step6:  Class decoration
Step7:  Debug all the classes?
TODO
Step8:  Can we inject the debugging code into all known classes?
<ASSISTANT_TASK:>
Python Code:
from functools import wraps
def debug(func):
    msg = func.__name__
    # wraps is used to keep the metadata of the original function
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper
@debug
def add(x,y):
    return x+y
add(2,3)
def add(x,y):
    return x+y
debug(add)
debug(add)(2,3)
Explanation: Notes from David Beazley's Python3 Metaprogramming tutorial (2013)
"ported" to Python 2.7, unless noted otherwise
A Debugging Decorator
End of explanation
def debug_with_args(prefix=''):
    def decorate(func):
        msg = prefix + func.__name__
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(msg)
            return func(*args, **kwargs)
        return wrapper
    return decorate
@debug_with_args(prefix='***')
def mul(x,y):
    return x*y
mul(2,3)
def mul(x,y):
    return x*y
debug_with_args(prefix='***')
debug_with_args(prefix='***')(mul)
debug_with_args(prefix='***')(mul)(2,3)
Explanation: Decorators with arguments
Calling convention
python
@decorator(args)
def func():
    pass
Evaluation
python
func = decorator(args)(func)
End of explanation
from functools import wraps, partial
def debug_with_args2(func=None, prefix=''):
    if func is None: # no function was passed
        return partial(debug_with_args2, prefix=prefix)
    
    msg = prefix + func.__name__
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper
@debug_with_args2(prefix='***')
def div(x,y):
    return x / y
div(4,2)
def div(x,y):
    return x / y
debug_with_args2(prefix='***')
debug_with_args2(prefix='***')(div)
debug_with_args2(prefix='***')(div)(4,2)
f = debug_with_args2(prefix='***')
def div(x,y):
    return x / y
debug_with_args2(prefix='***')(div)
Explanation: Decorators with arguments: a reformulation
TODO: show what happens without the partial application to itself!
End of explanation
def debug_with_args_nonpartial(func, prefix=''):    
    msg = prefix + func.__name__
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper
def plus1(x):
    return x+1
debug_with_args_nonpartial(plus1, prefix='***')(23)
@debug_with_args_nonpartial
def plus1(x):
    return x+1
plus1(23)
@debug_with_args_nonpartial(prefix='***')
def plus1(x):
    return x+1
Explanation: Debug with arguments: without partial()
this won't work with arguments
End of explanation
def debug_with_args3(*args, **kwargs):
    def inner(func, **kwargs):
        if 'prefix' in kwargs:
            msg = kwargs['prefix'] + func.__name__
        else:
            msg = func.__name__
        print(msg)
        return func
    # decorator without arguments
    if len(args) == 1 and callable(args[0]):
        func = args[0]
        return inner(func)
    # decorator with keyword arguments
    else:
        return partial(inner, prefix=kwargs['prefix'])
def plus2(x):
    return x+2
debug_with_args3(plus2)(23)
debug_with_args3(prefix='***')(plus2)(23)
@debug_with_args3 # WRONG: this shouldn't print anything during creation
def plus2(x):
    return x+2
plus2(12) # WRONG: this should print the function name and the prefix
@debug_with_args3(prefix='###') # WRONG: this shouldn't print anything during creation
def plus2(x):
    return x+2
plus2(12) # WRONG: this should print the function name and the prefix
Explanation: Decorators with arguments: memprof-style
this doesn't work at all
```python
def memprof(args, kwargs):
    def inner(func):
        return MemProf(func, args, **kwargs)
# To allow @memprof with parameters
if len(args) and callable(args[0]):
    func = args[0]
    args = args[1:]
    return inner(func)
else:
    return inner
```
End of explanation
def debugmethods(cls):
    for name, val in vars(cls).items():
        if callable(val):
            setattr(cls, name, debug(val))
    return cls
@debugmethods
class Spam(object):
    def foo(self):
        pass
    def bar(self):
        pass
s = Spam()
s.foo()
s.bar()
Explanation: Class decorators
decorate all methods of a class at once
NOTE: only instance methods will be wrapped, i.e. this won't work with static- or class methods
End of explanation
def debugattr(cls):
    orig_getattribute = cls.__getattribute__
    
    def __getattribute__(self, name):
        print('Get:', name)
        return orig_getattribute(self, name)
    cls.__getattribute__ = __getattribute__
    
    return cls
@debugattr
class Ham(object):
    def foo(self):
        pass
    def bar(self):
        pass
h = Ham()
h.foo()
h.bar
Explanation: Class decoration: debug access to attributes
End of explanation
class debugmeta(type):
    def __new__(cls, clsname, bases, clsdict):
        clsobj = super(cls).__new__(cls, clsname, bases, clsdict)
        clsobj = debugmethods(clsobj)
        return clsobj
# class Base(metaclass=debugmeta): # won't work in Python 2.7
#     pass
# class Bam(Base):
#     pass
# cf. minute 27
Explanation: Debug all the classes?
TODO: this looks Python3-specific
Solution: A Metaclass
End of explanation
class Spam:
    pass
s = Spam()
from copy import deepcopy
current_vars = deepcopy(globals())
for var in current_vars:
    if callable(current_vars[var]):
        print var,
frozendict
for var in current_vars:
    cls = getattr(current_vars[var], '__class__')
    if cls:
        print var, cls
print current_vars['Spam']
type(current_vars['Spam'])
callable(Spam)
callable(s)
isinstance(Spam, classobj)
__name__
sc = s.__class__
type('Foo', (), {})
Explanation: Can we inject the debugging code into all known classes?
End of explanation
<END_TASK>