python
Скороговорка
Пайтонячья скороговорка:
import pprint as pprint
pprint.pprint(pprint)Добавлено 24 декабря 2009 14:08: вариант от Бобука,
from pprint import pprint as pprint
locals()[pprint.__name__](pprint)Сколько памяти съела программа на Python?
Некоторое время назад мне хотелось выяснить, сколько съедает памяти программа на Python и чем эта память занята. Тогда я не смог быстро найти решение и обошёлся без него, а вчера, совершенно случайно, наткнулся на целых два.
Приведу их у себя полностью, чтобы потом можно было быстро найти.
Следующий код (используется модуль Heapy):
from guppy import hpy
h = hpy()
print h.heap()выведет вот такую, достаточно информативную, табличку:
Partition of a set of 132527 objects. Total size = 8301532 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 35144 27 2140412 26 2140412 26 str
1 38397 29 1309020 16 3449432 42 tuple
2 530 0 739856 9 4189288 50 dict (no owner)Другое решение — использовать Dowser, его интересная особенность — можно наблюдать за работающим кодом через
# memdebug.py
import cherrypy
import dowser
def start(port):
cherrypy.tree.mount(dowser.Root())
cherrypy.config.update({
'environment': 'embedded',
'server.socket_port': port
})
cherrypy.server.quickstart()
cherrypy.engine.start(blocking=False)Подключаем в программу memdebug и вызываем memdebug.start.
Там же упоминается модуль PySizer, но его автор забросил работу и рекомендует использовать Heapy.
Пайтон такой Пайтон
В regexp никаких тебе «(?R)», нет
Пятиминутка адского гнева. Нет, я не хочу поговорить об этом. Но если
Комментарии в Python
Меня очень раздражает, что в Пайтоне нет многострочных комментариев. И не говорите, что комментарии можно сделать тройными кавычками (или апострофами), это неудобно.
mylist = [\
'раз',
'два',
"""'три',
'четыре',"""
'пять'
]Всем очевидно почему не подойдут? Я не понимаю почему до сих пор в языке нет многострочных комментариев.
Утечка памяти в urllib2
Наткнулся сегодня на печальное поведение библиотеки urllib2 — в версии, которая используется в Ubuntu Hardy (Python 2.5.2) утекает память. Не знаю точно в какой версии баг исправили, но в, возможно, 2.5.4 и 2.6.2 бага уже нет.
За ночь с демона, использующего urllib2, «натекло» 9Гб.
Добавлено 14 августа 2009 15:15: urllib с httplib тоже утекают.
Такой молодой Python
Как сменить имя процесса в Python
Написал тут небольшой демон на Пайтоне и обнаружил, что «тушить» его через
Засада в том, что демон на Пайтоне и имя процесса у меня — «python». Я в этом языке программирования пока новичок (программирую с сентября), возможно поэтому не нашёл в документации как это делается (Ваня, расскажи, если есть простой способ).
Я помню, что в Perl было очень просто — записал в переменную $0 имя и в списке процессов видно записанное значение. В Python запись в sys.arvg[0] ничего не даёт.
В общем, мой велосипед (только для Linux!):
import os.path, sys, ctypes
ctypes.CDLL('libc.so.6').prctl(15, os.path.basename(sys.argv[0]), 0, 0, 0)Не все утилиты «увидят» новое имя,
Feedparser и санитайзер в Python
Странно, но в модуле feedparser для Python не нашёл способа как выключить внутренний санитайзер. Зачем его выключать?
Сделал monkey patch:
import feedparser
feedparser._sanitizeHTML = lambda html, encoding: htmlРемейк старой шутки
# -*- coding: utf-8 -*-
True, False = False, True # happy debugging!Это Python, для тех, кто не понял.
Определить начало дня в Python
Искал в Python способ переставить время на начало дня. В Python для манипулирования датами есть довольно удобный (если не считать временных зон) модуль datetime, получилось два варианта: первый придумал сам, второй подсказал (ещё раз спасибо) Иван Сагалаев:
import datetime
currentdate = datetime.datetime.today()
print currentdate.replace(hour=0, minute=0, second=0, microsecond=0)import datetime
currentdate = datetime.datetime.today()
print currentdate.combine(currentdate.date(), currentdate.min.time())Python 2.4 не освобождает память
Как я уже не раз говорил, libmapi нещадно течёт. Я сейчас работаю с версией из транка,
Поэтому я изолирую libmapi в отдельном процессе, получаю данные, пока могу и отдаю их «наверх» — родителю, потом делаю новый процесс и получаю следующую пачку. Для того, чтобы отдать данные родителю я использую os.pipe, а данные туда можно передавать только строкой. Так как данные у меня сложные, я их сериализую при помощи cPickle, а для сериализации нужна память.
Где взять память, если всё, что было доступно процессу, съела libmapi? Я пытался выделать память при помощи list(range(20 * 1024 * 1024)), а перед сериализацией удалять, но это не работало. Сегодня я узнал почему. Оказывается Python до версии 2.5 никогда не освобождает память. У меня как раз Python 2.4, приехали.
P.S. Чтобы предотвратить вопросы для чего я делаю list(range(…)), расскажу сразу: для совместимости с Python 3.0, на который мы все
Добавлено позднее.
Я сейчас немного поисследовал, сделал замеры и выяснил, что если обращаться к аллокатору памяти Python напрямую (например, через ctypes), то память возвращается:
from ctypes import *
mem = pythonapi.PyMem_Malloc(c_size_t(100*1024*1024))
pythonapi.PyMem_Free(mem)Век живи, век учись, от нехватки памяти умрёшь
Наступил недавно на отличные питонячьи грабли. Хорошие такие, массивные, со специальными шипами на рукоятке.
Есть модуль, внутри него происходит fork — внутри потомка изолируется libmapi, у которой нещадно течёт память. Модуль используется внутри программы, которая перехватывает все exceptions (!). Знаю, что это неправильно, но она это делает в режиме отладки, для отладки и используется.
Загвоздка в том, что
sys.exit пораждает иключение и выходит с его помощью. Если
Python: while 1
Starting with Py2.3, the interpreter optimizes «while 1» to just a single jump. In contrast «while True» takes several more steps. While the latter is preferred for clarity,
time-critical code should use the first form.
Ctypes и alloc
В биндинге для Python libmapi (который я пишу, с использованием ctypes), возникла следующая задача: есть куски кода, где надо выделить память через вызовы talloc. Желательно, чтобы память сама освобождалась, когда переменная, в которой у меня указатель на эту структуру, исчезает.
Оказывается, в ctypes можно легко обернуть эти вызовы в свой класс и использовать обычный деструктор, чтобы это сделать. При этом, ctypes, чтобы получить внутреннее значение, просто берёт свойство «_as_parameter_» класса.
Получился вот такой код:
class mapi_talloc(object):
def __init__(self, lib):
self._lib = lib
self._as_parameter_ = lib.talloc_init('python-mapi-object:' + str(hash(self)))
def __del__(self):
self._lib.talloc_free(self._as_parameter_)Магия libmapi
Можно я опять про libmapi?
В общем, из libmapi мне приходят два магических числа, они содержат диапазон — начало и конец периода, данные за который возвращает функция GetUserFreeBusyData. По внешнему виду видно, что это не unix timestamp (для несведующих, Unix timestamp — это число секунд с 1 января 1970 года).
По коду libmapi пытался выяснить что это. Код разбросан по файлам, но, в итоге, выглядит так:
// в t приходит <магическое число>
uint32_t t;
uint64_t time;
struct timespec ret;
time = t
time *= 60;
time *= 10000000;
ret.tv_nsec = (long) ((time % (1000 * 1000 * 10)) * 100);
d /= 1000*1000*10;
d -= 11644473600L;
// тут ещё парочка условий, которые не добавляют смысла
ret.tv_sec = (time_t)d;В таком виде я так и не смог понять что это за число. Порыскав по поисковикам, наткнулся на пост, где упоминается, что это всего лишь число минут с 1 января 1601 года.
Так что мой итоговый код на Python выглядит (без системных вызовов) так:
publish_start, publish_end = map(
lambda x: x * 60 - 11644473600, # 369 лет
(publish_start, publish_end)
)Libmapi писали индусы?
Есть ощущение, что некоторые части libmapi писали индусы. Напоминаю, что libmapi — библиотека для общения с Microsoft Exchange Server, к которой я пишу биндинг в Python с использованием ctypes.
Некоторые вызовы я предпочёл переписать на Python, так как код содержит баги и глупости. Например, мне приходит (в терминах Python) массив событий. Приходят два «слова», два числа, которые содержат дату/время старта и окончания. Числа приходят в минутах, например, чтобы узнать какой там указан день, нужно нацело разделить число на 1440 (количество минут в сутках) и прибавить один.
В итоге, я вычисляю всё вот так (часовой пояс я ещё не умею определять):
last = event_end - event_start
day = event_start // 1440
hour = (event_start - day * 1440) // 60
minute = event_start % 60
day += 1Авторы libmapi делают куда затейлевее:
for (hour = 0; hour < 24; hour++) {
if (!((event_start - (60 * hour)) % 1440)) {
// значит мы нашли час, который указан в событии
day = ((event_start - (60 * hour)) / 1440) + 1;
last = event_end - event_start;
}
}Эпопея с MAPI
Все эти пляски с ctypes — следствие одной большой задачи, с которой мы столкнулись в группе внутренних сервисов. Интеграция нашего интранета с Microsoft Exchange Server — это сервер для обмена сообщениями и совместной работы. Основной протокол, по которому этот сервер работает — MAPI.
После небольшого исследования выяснилось, что направлений для дальнейшего исследования три: можно попытаться работать с MAPI через RPC, можно использовать библиотеки, которые
Последний вариант,
Я долго не мог выяснить что за RPC применяет Microsoft, пока не начал перебирать всё, что приходит на ум. Оказалось, что Microsoft, в основном, использует диалект
Документации на него — кот наплакал, чуть примеров и в Сети есть несколько эксплоитов, написанных при помощи этого модуля. Этого хватило, чтобы понять как примерно им пользоваться и несколько дней и я пытался сделать прототип для общения с Exchange на нём.
Довольно быстро оказалось, что задача очень трудоёмкая. Даже для того, чтоб описать только вызовы верхнего уровня, ушёл бы не один день, а описывать
Готовых библиотек для работы с MAPI немного. Бесплатная, кажется, так и вовсе одна — libmapi. Сейчас стабильной версией считается 0.7, поскольку библиотека требует присутствия Samba4, то решено было ставить её из исходников. Оказалось, дело это очень непростое, так как база пакетов на машине, куда пришлось её ставить была разрушена, «Пайтонов» было два, от чего у исталлятора уносило крышу и имелась собственная Samba3, которая используется в работе.
Наши админы попробовали её поставить, но после нескольких проблем, сказали, что это очень сыро и ставить не получается. Я их понимаю — работы у них масса, слишком много времени на исталляцию недописанного софта они выделять не в состоянии. Поэтому библиотеку я ставил сам. В рунете я не смог найти никаких описаний того, что это
Для начала, лучше всего ставить версию из транка. Встаёт она значительно проще, чем стабильная. Далее. Сначала надо поставить Samba4, для этого в каталоге «script» есть специальный скрипт, который выкачивает её и ставит. Посколько API Samba меняется очень часто (это «альфа»), выкачивать нужно определённую ревизию, скрипт это умеет, но ему нужен git. Git — это распределённая система контроля версий, это пакет в Debian называется
Как оказалось, Samba4 в памяти не нужна. Всё что он неё надо, чтобы она была скомпилена со всеми библиотеками и небольшой конфиг, образец которого есть в дебрях libmapi. Не забудьте правильно выставить язык в конфиге, в небольшой инструкции по установке есть описание того, как это делается. В установочных файлах конфига проследите, чтобы все пути были указаны правильно. Например, мне пришлось поправить пути до исходников Samba с «samba4» на «samba4/source4», а так же руками добавлять PKG_CONFIG_DIR в файлы configure.
Дайте libmapi и Samba4 весь софт, который она попросит. Даже опциональный. И самый свежий (к Python это не относится, у меня взлетело с 2.4). Это важно. Некоторые файлы нужно будет перенести вручную, например, библиотеки для Python. После компиляции создайте профиль (в wiki libmapi есть инструкция) и запустите openchangeclient, чтобы проверить соединение. Если у вас домен, то не забудьте про утилиту provision (описание можно найти в инете, важно пускать её именно так как в примерах — чтобы текущей была указанная в примерах папка).
Если openchangeclient выдаёт
Кажется, это всё. Да, если у вас несколько «Пайтонов» на машине, удалите все, кроме одного, или перепишите место, где в конфигах определяется версия «Пайтона» руками (я так и сделал), иначе часть модулей, возможно, встанет не туда, куда ожидается. Python нужен для работы provision (кстати, некоторые утилиты написаны на Perl, но с ним проблем у меня не было).
После всех боданий, из Си удалось подключиться к Exchange и получить данные. Из Python пока удаётся только залогиниться, но это дело времени — нужно описать нужные структуры и понять что куда передавать. В документации, кстати, есть неточности, лучше читать исходники.
Python Ctypes: автоматическая генерация
Столкнулся с проблемой: нужно дёргать из Python библиотеку, очень богатую на структуры. Я эксперементирую с ctypes, которая ничего не знает
Оказывается, есть утилита, которая генерирует структуры по заголовочным файлам автоматически. Называется h2xml.py, в интернете есть небольшое описание. Попробую её.
Единственный недосток: язык — Cи++, с Си
Ctypes
У PHP есть модуль из которого можно «дёрнуть» любую dll/so. Следовало ожидать, что и у Python есть
Вот что получилось (finfo.py):
# -*- coding: utf8 -*-
from ctypes import *
NONE = 0 # No special handling
DEBUG = 1 # Print debugging messages to stderr
SYMLINK = 2 # If the file queried is a symlink, follow it
COMPRESS = 4 # If the file is compressed, unpack it and look at the contents
DEVICES = 8 # If the file is a block or character special device, then open the device and try to look in its content
MIME = 16 # Return a mime string, instead of a textual description
CONTINUE = 32 # Return all matches, not just the first
CHECK = 64 # Check the magic database for consistency and print warnings to stderr
PRESERVE_ATIME = 128 # On systems that support utime(2) or utimes(2), attempt to preserve the access time of files analyzed
RAW = 256 # Don’t translate unprintable characters to a ooo octal representation.
class finfo:
def __init__(self, magicfile = None, flag = NONE):
"""Инициализация. Передаются имя magic-файла (или используется размещение
по умолчанию и флаги (см. константы выше)"""
self.lib = cdll.LoadLibrary('libmagic.so.1')
# следующие функции возвращают указатель на char, что я указываю
self.lib.magic_buffer.restype = c_char_p
self.lib.magic_file.restype = c_char_p
self.lib.error.restype = c_char_p
self.magic = self.lib.magic_open(flag)
self.lib.magic_load(self.magic, magicfile)
def buffer(self, content):
"""определить содержимое из строки"""
return self.lib.magic_buffer(self.magic, content, len(content))
def file(self, filename):
"""определить содержимое файла"""
return self.lib.magic_file(self.magic, filename)
def __del__(self):
self.lib.magic_close(self.magic)Вызывается модуль просто:
import finfo
mime = finfo.finfo('/usr/share/file/magic', finfo.MIME)
print mime.file('more.png')Church Encoding in Python
Вывихнул себе мозг статьёй «Church Encoding in Python», теперь смотрю на Пайтон под новым углом.