Разработка на коленке

"тут должна быть красивая цитата о программировании"

Как собрать python3 с историей команд

2015-03-12 13:00

Если просто взять исходники третьего питона и установить, то интерпретатор не будет уметь возвращаться к предыдущей команде. То есть нажав на стрелку вверх после последней команды, увидим что-то вроде такого:

>>> dir
<built-in function dir>
>>> ^[[A
  File "<stdin>", line 1
    ^
SyntaxError: invalid syntax
>>>

Нужно установить readline для сборки

sudo apt-get install libreadline-dev

А потом обычный набор

wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tar.xz

tar xf Python-3.4.3.tar.xz
cd Python-3.4.3/

./configure --prefix=/opt/python3.4.3 --with-ensurepip=install
make && sudo make install

После этого предыдущая команда начнёт работать

>>> dir
<built-in function dir>
>>> dir
<built-in function dir>
>>>

Избавился от Marker With Label

2014-09-04 19:50

Из-за проблем с производительностью пришлось выбросить из проекта эту библиотеку. Теперь для отображения количества точек используется заранее сгенерированный spritesheet, в котором иконки с надписями. И скорость работы повысилась, и ушёл один плавающий баг. В общем, чем проще, тем лучше.

А spritesheet сгенерировал скриптом на питоне, который выглядит примерно так:

import math

from os import path
from PIL import ImageFont, Image, ImageDraw


def here(*pathchunks):
    return path.abspath(path.join(path.dirname(__file__), *pathchunks))


def spritesheet(image, labels, columns):
    original_width, original_height = image.size

    font_name = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
    font = ImageFont.truetype(font_name, 10)

    sheet_items = []
    for label in labels:
        item_image = image.copy()

        item_draw = ImageDraw.Draw(item_image)

        _, text_height = item_draw.textsize(label)
        text_width, _ = font.getsize(label)

        item_draw.text(
            ((original_width - text_width) / 2, (original_height - text_height) / 2),
            label, (255, 255, 255), font=font)

        sheet_items.append(item_image)

    sheet_width = original_width * columns
    lines = int(math.ceil(len(labels) / float(columns)))
    sheet_height = original_height * lines
    sheet = Image.new(mode='RGBA', size=(sheet_width, sheet_height), color=(0, 0, 0, 0))

    for number, item_image in enumerate(sheet_items):
        x = number % columns
        y = int(number / columns)
        sheet.paste(item_image, (x * original_width, y * original_height))

    sheet.save(here('sheet.png'), 'PNG')


if __name__ == '__main__':
    spritesheet(
        Image.open(here('image.png')), [str(i) for i in range(25)], 10
    )

Получить текст из html, проигнорировав комментарии

2014-04-07 20:30

Нужно взять html и достать из него весь текст, проигнорировав часть тегов, комментарии. При пост-обработке удалить повторяющиеся пробелы. Желательно хотя бы худо-бедно обработать юникод. При этом сохранять оригинальное форматирование необязательно, потому что текст будет обрабатываться машиной, а не показываться человеку.

html5lib тут не особо важен, но он исправляет html, и я указываю его, чтобы избежать проблем с beautifulsoup + mod_wsgi

import bs4
import re

TAGS_TO_REMOVE = ['title', 'head', 'style', 'script']

data = '<b>bold text</b>'

soup = bs4.BeautifulSoup(data, 'html5lib')

# удалить теги с контентом, который не должен попасть в текст
for tag_to_remove in TAGS_TO_REMOVE:
    for el in soup(tag_to_remove):
        el.extract()

# удалить комментарии (в том числе и многострочные),
# тут не стоит пользоваться регулярками, как советуют на некоторых сайтах
# http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454
for comment in soup.findAll(text=lambda text: isinstance(text, bs4.Comment)):
    comment.extract()

# склейка через пробел, чтобы не склеивать два слова в одно,
# тут же html-спецсимволы конвертируются в юникод (&times; - ×)
clean_text = ' '.join(soup.findAll(text=True))

# убрать повторяющиеся пробелы (юникодные в том числе) и пропуски на концах
clean_text = re.sub('(?u)\s+', ' ', clean_text).strip()

print clean_text

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

Не все парсеры одинаково полезны

2014-04-04 17:05

Есть веб-приложение, которое запускается под Apache+mod_wsgi. Выполнение одного из запросов вешало выполнение, а в лог попадало:

Premature end of script headers: apache.py

Поиск по интернетам сказал, что проблема где-то в запросах к БД. Запросы проверил - всё нормально. При этом ошибка проявлялась только в одном окружении из двух. Потом стал отлаживать просто при помощи print. В итоге выяснил, что выполнение вешается на строке:

soup = bs4.BeautifulSoup(data)

Дальше поиск по интернетам привёл на bug/948577, вкратце lxml+BeautifulSoup под mod_wsgi вешает выполнение запроса. А в той строке BeautifulSoup в качестве парсера подтягивал как раз lxml. Прописал парсер явно:

soup = bs4.BeautifulSoup(data, 'html.parser')

Забыл похвастаться

2014-03-02 02:00

Как-то заработался, да и забыл похвастаться. Мой фикс вошёл в pyglet. Там патч-то на одну строку, точнее на плюс одну константу в строке, однако пофиксило багу с геймпадом в линуксе.

Вот такой маленький diff:

...
185     185         elif control._event_type == EV_ABS and control._event_code == ABS_Y:
186     186             have_y = True
187     187         elif control._event_type == EV_KEY and \
188         -            control._event_code == BTN_JOYSTICK:
188         +            control._event_code in (BTN_JOYSTICK, BTN_GAMEPAD):
189     189             have_button = True
190     190     if not (have_x and have_y and have_button):
191     191         return

Итак - зал славы :).