Показаны сообщения с ярлыком Python. Показать все сообщения
Показаны сообщения с ярлыком Python. Показать все сообщения

14 марта 2011 г.

Делимся Питоном с миром

Когда питоновские скрипты приобретают какую-то готовую форму, и ими хочется поделиться с другими, возникает вопрос: как это правильно и красиво сделать?

Стандартом для распространения программ на Python является Python Package Index, своего рода центральный Python-репозиторий. О процессе публикации много написано, покажу, как это делается, на своём примере.

7 марта 2011 г.

Реанимация данных

Жена с дочкой используют внешний USB-винт для хранения видео и всего такого. На днях прикупил новый, стал копировать данные со старого, и — бац! — I/O error! Битые сектора, они же бэд-блоки.

В итоге не удалось скопировать 4 видеофайла. Тут на помощь приходит старая добрая утилитка ddrescue — при ошибке ввода-вывода продолжает копировать посекторно, пропуская плохие участки, в итоге удаётся бóльшую часть данных вытащить; позволяет копировать как целое устройство, так и отдельный файл. Видео спасти получилось, благо небольшая доля мусора внутри файла не мешает его воспроизводить.

К сожалению, ddrescue не позволяет копировать целые директории рекурсивно. Тогда я написал простой скрипт на Python, rddr (Recursive DDRescue), копирующий каталог целиком с помощью того же ddrescue.

Но этот эпизод с винтом меня смутил — ведь есть же SMART, почему Ubuntu мне не сказал о непригодном состоянии диска? Оказалось, что состояние его "хорошее", но есть несколько плохих секторов. Есть у SMART такой атрибут, показывающий количество секторов, ожидающих замены из специальной дисковой зоны "хороших" секторов, причём замена происходит при попытке записи, а не чтения. Теперь на помощь приходит программа badblocks — она позволяет прогонять тесты чтения/записи по всему устройству. Тесты записи на винт заняли около одиннадцати часов, данные при этом затёрлись. 19 секторов из 20 восстановились из резервной области.

Вердикт: винчестер можно использовать для некритичных данных, видео хранить вполне годится.

P.S. Прекрасное руководство для восстановления данных с помощью Linux можно здесь почитать.

4 марта 2011 г.

Скоро выйдет Django 1.3

3 марта вышел кандидат в релизы Django 1.3. Я не вытерпел и полюбовался на внушительный список изменений будущей версии.

2 марта 2011 г.

Статический анализ кода в Python

В статье на Хабре наткнулся на упоминание статических анализаторов кода для Python. Инструмент этот очень полезен при регулярном его использовании. Немножко подробнее расскажу о pep8, pylint и pyflakes.

pep8

Этот анализатор проверяет соответствие кода знаменитому PEP 8 — руководству по стилю кода Python. В больших и не очень командах хорошая штука, ибо "Readability counts"!

pylint

Самый придирчивый анализатор из всех трёх, хотя стиль кода проверяет немножко иначе, чем pep8 — акцент на "дурно пахнущем" и неоптимальном коде, выдаёт и предложения о рефакторинге. Бонусная опция — генерация UML-диаграмм с помощью команды pyreverse.
Угодить pylint весьма сложно, порой он ошибается — к счастью, настройки достаточно гибкие.

pyflakes

В отличие от pylint, проверяет только на логические ошибки в программе, стиль не учитывает.


На мой взгляд, золотая середина — pyflakes, поначалу можно использовать и pep8, дабы привить соблюдение стиля.

Бонус для пользователей Vim — вызов pyflakes или pylint после каждого сохранения редактируемого файла, сообщения об ошибках помещаются в окошко "quickfix":
command Pylint :call Pylint()
function! Pylint()
    setlocal makeprg=(echo\ '[%]';\ pylint\ %)
    setlocal efm=%+P[%f],%t:\ %#%l:%m
    silent make
    cwindow
endfunction

command Pyflakes :call Pyflakes()
function! Pyflakes()
    let tmpfile = tempname()
    execute "w" tmpfile
    execute "set makeprg=(pyflakes\\ " . tmpfile . "\\\\\\|sed\\ s@" . tmpfile ."@%@)"
    silent make
    cwindow
endfunction

autocmd BufWrite *.{py} :call Pyflakes()

22 февраля 2011 г.

Репликация и устойчивое хэширование в memcached

Memcached работает просто и быстро, как электровеник, и не поддерживает репликации, которая может понадобиться при горизонтальном масштабировании серверов memcached.

Есть как минимум два варианта решения этой задачи. Первый используется для репликации мастер-мастер и не зависит от языка программирования — repcached, патч к memcached, добавляющий репликацию между серверами. (Инструкция по установке.) Из минусов — устанавливать придётся, скорее всего, из исходников и поддерживается не самая последняя версия memcached (примерно полуторагодовалой давности).

Другой способ специфичен для PHP (впрочем, наверняка есть схожие решения и для других языков). Модуль php5-memcache позволяет реализовать подобие репликации с помощью хранения каждого ключа на N серверах. В случае, если один из серверов падает, ключ будет храниться на оставшемся и каком-то N+1 сервере. С помощью этого модуля можно хранить и сессии с избыточностью. Выдержка из readme:
# When enabled the client sends requests to N servers in parallel, resulting in
# a somewhat crude reduncancy or mirroring, suitable when used as a session
# storage.
#
# If data integrity is of greater importance a real replicating memcached
# backend such as "repcached" (http://sourceforge.net/projects/repcached/) is
# recommended

memcache.redundancy = # default 1
memcache.session_redundancy = # default 2


Для большинства приложений такой надёжности достаточно.

Вдобавок этот модуль использует т.н. "устойчивое хэширование" ("consistent hashing"). Вкратце, этот алгоритм позволяет привязывать ключи к определённым серверам memcached и при удалении/добавлении серверов бóльшая часть ключей останется привязана к тем же серверам. Таким образом, для распределения нагрузки без использования репликации достаточно встроенных средств php5-memcache.

Для Python такую функциональность предоставляет модуль hash_ring.

8 сентября 2010 г.

Server-side jQuery

jQuery настолько прекрасен, что портируется и на серверные языки, дабы упростить написание всевозможных парсеров. Я использую два порта, pyquery и phpquery, соответственно — для Python и PHP. Весьма облегчают жизнь.

9 июня 2010 г.

О MySQL в многопоточном Python'е

В проекте возникла необходимость доступа к MySQL из разных потоков (threads). Я внимаааааааааательно прочитал официальную документацию библиотеки для доступа MySQL из Python, и она заявляет, что нельзя использовать одно и то же подключение к БД в разных потоках.

На помощь спешит PySQLPool, в нём есть методы для создания пула подключений к MySQL.

Я использую движок таблиц (table engine) InnoDB с нормальной поддержкой транзакций. Совсем неинтересно каждый раз писать BEGIN TRANSACTION/COMMIT, это ж Python! Должен быть способ! И он нашёлся.

В версии 2.5 добавили оператор with и контекстные менеджеры. Я написал простенький патч, и использование транзакций стало простым и красивым:
q = PySQLPool.getNewQuery(connection)
with q: # внутренние запросы обёрнуты в транзакцию
            q.Query('INSERT INTO peer SET PeerName=%s', (name,))

Есть и маленькая ложка дёгтя в виде заявленного автором бага при работе с транзакциями из нескольких тредов. В общем-то, ко времени релиза нашего проекта либо я исправлю этот баг, либо автор, либо вы. ;)

14 мая 2010 г.

Сетевое программирование в Python

Нашёл хороший туториал (PDF) на английском про сетевое программирование в Python — чётко изложена сама теория, кое-чего я и сам не знал, и даны хорошие базовые примеры на Python.
На сайте автора есть и другие неплохие туториалы про Python.

5 мая 2010 г.

UDP-траспорт

В проекте, над которым я работаю, понадобилось использовать асинхронный UDP-траспорт. Как выяснилось, есть в этой задаче несколько подводных камней — о них и пойдёт речь.

Хотелось что-то максимально простое с помощью встроенных в Python средств.

Первый вариант — класс SocketServer.UDPServer. Минус его в том, что он использует блокирующие сокеты; для обхода этого в том же пакете есть и неблокирующие версии, реализованные с помощью тредов или даже отдельных процессов на каждого клиента. Но это не совсем асинхронность.

Второй способ — пакет asyncore, который как раз для асинхронных сообщений и предназначен. Правда, только для TCP. Создать нужный класс несложно, но есть несколько тонкостей:
class UdpTransport(asyncore.dispatcher, object):
    def __init__(self, addr=None):
        super(UdpTransport, self).__init__()
        self.ignore_log_types = set() # Для вывода дополнительных ошибок в asyncore, подсмотрел в исходниках asyncore :)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        # Размер этого буфера очень важен
        # Если входящий пакет будет больше него, остаток отбрасывается
        self.ReadBufferSize = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
        if addr:
            self.bind(addr) # Единственное отличие серверного сокета в использовании bind()
        
    def handle_close(self):
        self.close()

    def writable(self):
        return False

    def handle_read(self):
        try:
            data, addr = self.recvfrom(self.ReadBufferSize) 
        except socket.error: # Исключение возникает при чтении данных из сокета: когда они заканчиваются, следующая попытка вылетает
            pass
        # обработка данных

    def send(self, msg, toaddr):
        self.socket.sendto(msg, toaddr)

    handle_connect = handle_read # Нарыл где-то на просторах инета
    # Без этого asyncore не работает с UDP, первую порцию данных он интерпретирует как инициализацию подключения
Наследуемся и от object для использования super и отладки с помощью метакласса.

Вот, собственно, и всё, все хитрости прокомментированы в коде. Естественно, нам не требуется connect() для серверных сокетов, а вместо socket.send/recv нужно использовать методы sendto/recvfrom. Сперва я путался, в каких случаях что нужно применять — документация лаконична — но нашёл хороший источник с примерами, это наш родной `man`. ;) Например, `man bind` (или `man 2 bind`, если быть точным) показывает документацию С-шного сетевого API, обёрткой вокруг которого socket, собственно, и является. Если на вашем *nix такой странички нет, нужно установить пакет manpages-dev (в Debian и Ubuntu, по крайней мере).

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 вывелись строчки с чистым русским текстом. Ура!

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

11 августа 2009 г.

Программисты и путешественники

Объективно прекрасный проект — Tripster. Мало того, он написан на Django и ищет разработчика.



Сколько ещё мест не видел!