вторник, 24 июля 2012 г.

Перелезая на Python

Раньше, когда мне нужно было написать что-то скриптообразное, я использовал Perl. Коллеги за это частенько троллили меня, рассказывая, что Perl - write-only язык. На самом деле это не так, на перле можно писать вполне читабельный код, и дело тут не столько в языке, сколько в программисте.

С другой стороны, у Perl объективно есть ряд неприятных особенностей, которые я здесь перечислять не буду потому, что пост не об этом. Кроме того, мне нравится осваивать новые технологии, это расширяет кругозор и сознание.

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

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

Сегодня передо мной стояла следующая задача. Есть сервер с Apache, на котором установлен mod_deflate. Надо написать клиента, который пошлет серверу POST-запрос, в теле которого будет XML, для эффективности чем-нибудь пожатый. Распаковку писать не надо, mod_deflate все сделает за нас, надо только прописать правильные HTTP-заголовки. На перле это делается так:
use LWP::UserAgent;

my $agent = LWP::UserAgent-new;
my $request = HTTP::Request->new(POST => $url);
$request->header('Accept-Encoding' => 'gzip, deflate');
$request->content_type('text/xml');
$request->content($body);
$request->encode('gzip');
$request->authorization_basic($username, $password);
my $response = $agent->request($request);
Вот и все. HTTP-клиент-библиотека, победившая на перле, самостоятельно сжимает тело запроса и добавляет нужный заголовок.
А что же лаконичный и красивый Python? А вот что...

import gzip
import requests
from cStringIO import StringIO

session = requests.session(
 auth=(username, password),
 headers={
  'Content-Type': 'text/xml',
  'Content-Encoding': 'gzip',
  'Accept-Encoding': 'gzip, deflate'})

buf = StringIO()
gzip.GzipFile(fileobj=buf, mode='wb', compresslevel=6).write(message)
compressed = buf.getvalue()
response = session.post(url, data=compressed)
Ок, я смирюсь с тем, что HTTP-библиотека не желает сжимать тело и добавлять "Content-Enconding: gzip" за меня, я не гордый.
Но выяснилось, что для того, чтобы пожать что-нибудь во gzip, это что-то должно лежать в файле, а не в памяти, как у меня. И чтобы преодолеть эту нелепую особенность применяем StringIO, который позволяет притвориться файлом простому буферу в памяти. Местная обертка над zlib, которая умеет сжимать данные из памяти без дополнительных изворотов, создает при этом такие архивы, которые не может распаковать mod_deflate, я не понял почему.

Как же так, товарищи? Может я плохо искал и все можно сделать прямо и лаконично?

Забавным образом эта кривость особенно заметна на фоне чистенького кода на питоне, тогда как среди нагромождений кода, скажем, на C или на том же перле она смотрелась бы не так страшно.

Комментариев нет:

Отправить комментарий