28 апреля 2010 г.

Мощь Python

Я не так давно читал про метаклассы в Питоне, и тут представился случай их использовать.
Задача: нужно отладить код сетевого приложения, дебаггером по нему не пройдёшь — при пошаговой отладке состояние сокетов теряется. Я решил печатать имя и аргументы каждого вызванного метода сетевых классов. Вариант со вставкой 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 классах и прочей магии Питона есть на Хабре.
Про обёртки, сиречь декораторы, можно почитать в блоге Романа Ворушина.

23 апреля 2010 г.

В поисках утраченных исходников

Сегодня я работал над модульными тестами для своего Python-проекта. Для простоты файлы тестов называю так же, как и файлы тестируемых модулей. Я решил перенести тесты в другой каталог и мимодумно скопировал их в каталог модулей. Исходники модулей, конечно, затёрлись.
"А-а-а-а!!!" — подумал я. К счастью, остались оригинальные *.pyc-файлы — скомпилированный байт-код Python, который, по идее, можно превратить обратно в исходник.
Быстрый поиск выдал замечательный пост на не менее замечательном StackOverflow. Бесплатная утилита UnPyc помогла почти полностью восстановить один из файлов, но споткнулась на другом, с list comprehensions. Тогда я решил воспользоваться онлайн-сервисом DePython, но у него два ограничения:
  1. размер файла должен быть меньше 5 КБ;
  2. версия Python <= 2.5.
У меня была 2.6. В том же замечательном посте привели программку для конвертирования pyc-файла в версию 2.3 — вся идея в том, чтобы заменить первые 4 байта файла на соответствующие версии Python. Ещё немножко поиска помогло узнать magic-строку для версии 2.5.
К счастью, модулей было всего два :), осталась последняя закавыка — текст на русском раскодировался в виде \xx-последовательностей. Тогда я обернул текст исходников в такой код:
print """
<здесь перекодированный код>
"""
При запуске скрипта в консоли с локалью UTF-8 вывелись строчки с чистым русским текстом. Ура!

Мораль сего такова: будьте внимательны, чаще коммитьте и не отчаивайтесь.

4 апреля 2010 г.

Ретуширование картинок

Недавно наткнулся на видео с показом тестовой фишки в PhotoShop — Content-Aware Fill (заливка, зависимая от содержимого картинки):


Open source уже два года восемь лет (спасибо, prokoudine!) как имеет в своём распоряжении такой же плагин для Gimp. :)
До После (убрал собачку и дома)

Ну разве это не красота?