Задача: нужно отладить код сетевого приложения, дебаггером по нему не пройдёшь — при пошаговой отладке состояние сокетов теряется. Я решил печатать имя и аргументы каждого вызванного метода сетевых классов. Вариант со вставкой print в каждый метод — это явно не наш метод. :)
Получился вот такой метакласс:
class DebugMetaclass(type):
'''Вывод вызова каждого метода объекта с параметрами, удобно при отладке
сетевых приложений. ;)'''
def __new__(cls, name, bases, attrs):
cls.last_signature = None
for elem in attrs:
if inspect.isfunction(attrs[elem]):
attrs[elem] = DebugMetaclass.decorator(attrs[elem])
return super(DebugMetaclass, cls).__new__(cls, name, bases, attrs)
@classmethod
def decorator(cls, func):
'''Возвращает обёрнутую функцию.'''
@wraps(func)
def wrapper(*args, **kwargs):
signature = '%s.%s(%s, %s)' % (args[0].__class__.__name__,
func.__name__, ', '.join([str(a) for a in args]),
kwargs)
if cls.last_signature != signature: # Добавлено для подавления повторяющихся сообщений
print signature
cls.last_signature = signature
return func(*args, **kwargs)
return wrapper
Класс, который будет использовать этот метакласс, должен наследоваться от object. В моём случае выглядит примерно так:
class UdpTransport(asyncore.dispatcher, object):
__metaclass__ = DebugMetaclassСам dispatcher — это old-style class, поэтому я добавляю ещё одного родителя, но если поместить его первым, лезут баги.
Прекрасные материалы о метаклассах, new-style классах и прочей магии Питона есть на Хабре.
Про обёртки, сиречь декораторы, можно почитать в блоге Романа Ворушина.

