<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Technology@Work &#187; python</title>
	<atom:link href="http://techwork.ru/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://techwork.ru</link>
	<description>Русскоязычная информация о Cloud Computing и других технологиях</description>
	<lastBuildDate>Wed, 16 Sep 2009 09:34:43 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Как скачать свой код с App Engine?</title>
		<link>http://techwork.ru/2008/09/04/downloading-app-code/</link>
		<comments>http://techwork.ru/2008/09/04/downloading-app-code/#comments</comments>
		<pubDate>Thu, 04 Sep 2008 06:35:23 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=380</guid>
		<description><![CDATA[На форумах часто появляются сообщения разработчиков, которые создали проект, опубликовали его на App Engine, а потом вдруг потеряли его исходники (всякое бывает). Несмотря на то, что платформа имеет очень продвинутую панель управления приложениями и их версиями, к сожалению, возможности скачать уже опубликованный код стандартными средствами нет.
Конечно же, существует отдельный вариант загрузить вместе с проектом специальный [...]]]></description>
			<content:encoded><![CDATA[<p>На форумах часто появляются сообщения разработчиков, которые создали проект, опубликовали его на App Engine, а потом вдруг потеряли его исходники (всякое бывает). Несмотря на то, что платформа имеет очень продвинутую панель управления приложениями и их версиями, к сожалению, возможности скачать уже опубликованный код стандартными средствами нет.</p>
<p>Конечно же, существует отдельный вариант загрузить вместе с проектом специальный AJAX shell, который позволит просматривать код опубликованного приложения. Скачать подобное решение можно <a href="http://code.google.com/p/google-app-engine-samples/downloads/list" target="_blank">отсюда</a>.</p>
<p>Давайте рассмотрим пример просмотра кода самой AJAX-shell, которая расположена по адресу <a href="http://shell.appspot.com">http://shell.appspot.com</a>.</p>
<ol>
<li>Сначала узнаем, где мы находимся:
<pre>
<div class="python" style="font-family:monospace;color:#000000;">&gt;&gt;&gt; <span style="color:#dc143c;">os</span>.<span style="color:black;">getcwd</span><span style="color:black;">(</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
<span style="color:#483d8b;">'base/data/home/apps/shell/1.29/'</span></div>
</pre>
</li>
<li>Выведем содержимое каталога:
<pre>
<div class="python" style="font-family:monospace;color:#000000;">&gt;&gt;&gt; <span style="color:#dc143c;">os</span>.<span style="color:black;">listdir</span><span style="color:black;">(</span><span style="color:#483d8b;">"."</span><span style="color:black;">)</span>
<span style="color:black;">[</span><span style="color:#483d8b;">'shell.py'</span>, <span style="color:#483d8b;">'templates'</span><span style="color:black;">]</span></div>
</pre>
</li>
<li>Далее откроем интересующий нас файл с помощью вывода его содержимого на экран:
<pre>
<div class="python" style="font-family:monospace;color:#000000;">&gt;&gt;&gt; <span style="color:#008000;">file</span> = <span style="color:#008000;">open</span><span style="color:black;">(</span><span style="color:#483d8b;">"shell.py"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
&gt;&gt;&gt; <span style="font-weight:bold;color:#ff7700;">for</span> line <span style="font-weight:bold;color:#ff7700;">in</span> <span style="color:#008000;">file</span>:
    <span style="font-weight:bold;color:#ff7700;">print</span> line
<span style="font-style:italic;color:#808080;">#!/usr/bin/python</span>
<span style="color:black;">[</span>далее содержимое файла<span style="color:black;">]</span></div>
</pre>
</li>
</ol>
<p>Возможно в будущем появятся какие-то комплексные удобные решения, которые позволят интерактивно взаимодействовать с кодом приложения. Чем не идея для нового приложения?</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/09/04/downloading-app-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XML сервисы, часть вторая</title>
		<link>http://techwork.ru/2008/08/28/xml-services-part-2/</link>
		<comments>http://techwork.ru/2008/08/28/xml-services-part-2/#comments</comments>
		<pubDate>Thu, 28 Aug 2008 13:03:10 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[CallManager]]></category>
		<category><![CDATA[Cisco]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[unicode]]></category>
		<category><![CDATA[utf-8]]></category>
		<category><![CDATA[voip]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=323</guid>
		<description><![CDATA[Недавно мы рассматривали создание приложения на платформе App Engine, которое позволяет IP телефонам Cisco получать информацию о прогнозе погоды, новостях и курсе валют.
К сожалению, пользователи, которые же в первые дни появились у этого сервиса, обнаружили в нем два существенных недостатка:

Мы отображаем только погоду только по Москве, хотя сервис у нас глобальный
Существуют проблемы на разных версиях [...]]]></description>
			<content:encoded><![CDATA[<p>Недавно <a href="http://techwork.ru/2008/08/09/xml-services-for-ip-phones/">мы рассматривали</a> создание приложения на платформе App Engine, которое позволяет IP телефонам Cisco получать информацию о прогнозе погоды, новостях и курсе валют.</p>
<p>К сожалению, пользователи, которые же в первые дни появились у этого сервиса, обнаружили в нем два существенных недостатка:</p>
<ul>
<li>Мы отображаем только погоду только по Москве, хотя сервис у нас глобальный</li>
<li>Существуют проблемы на разных версиях прошивок для телефонов, которые ожидают данные на русском языке в кодировке cp-1251.</li>
</ul>
<p>Первый недочет решено было исправить, добавив опциональный параметр <em>city</em> к URL сервиса, равный коду города на сайте <a href="http://gismeteo.ru" target="_blank">ГисМетео</a>, на котором мы берем данные погоды. Таким образом строка для телефонов Питера выглядит как <a href="http://xmlphones.appspot.com/?city=26063" target="_blank">http://xmlphones.appspot.com/?city=26063</a>. Второй аналогично будет называться <em>encode</em> и содержать при необходимости задание кодировки windows-1251.</p>
<p><span id="more-323"></span></p>
<p>Перекодировка в Python делается элегантно. Допустим, у нас через URLFetch получены данные в UTF-8, а вывод требуется в windows-1251:</p>
<pre>
<div class="python" style="font-family:monospace;color:#000000;">resp = <span style="color:#008000;">unicode</span><span style="color:black;">(</span>result.<span style="color:black;">content</span>, <span style="color:#483d8b;">'utf-8'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
resp = resp.<span style="color:black;">encode</span><span style="color:black;">(</span><span style="color:#483d8b;">"cp1251"</span><span style="color:black;">)</span></div>
</pre>
<p>Ну и наоборот:</p>
<pre>
<div class="python" style="font-family:monospace;color:#000000;">resp = <span style="color:#008000;">unicode</span><span style="color:black;">(</span>result.<span style="color:black;">content</span>, <span style="color:#483d8b;">'cp1251'</span><span style="color:black;">)</span></div>
</pre>
<p>В первом случае мы из байтового массива конструируем объект unicode, который затем кодируем с указанием кодека cp1251. Во втором случае одним действием из байтового массива конструируем также объект unicode с заданием кодека cp1251, но далее просто отдаем его в таком виде платформе App Engine (подразумевается что весь вывод по умолчанию в кодировке UTF-8).</p>
<p>Все кажется замечательным, но тут опять обнаруживается криворукость писателей прошивок некоторых моделей IP телефонов, заключающаяся в том, что телефоны сами добавляют к параметрам запросов дополнительные переменные вида <strong>?locale=English_United_States&amp;name=SEP001B541415EE</strong>, что приводит к проблемам обработке меню сами же телефонами.</p>
<p>Проведенный небольшой эксперимент показал, что если, например, параметры задавать с помощью многоуровневых URL, то все работает отлично. Допустим, кодировку windows-1251 мы будем задавать как <a href="http://xmlphones.appspot.com/windows-1251/">http://xmlphones.appspot.com/windows-1251/</a> (подразумевая, по умолчанию корневой адрес аналогичный <a href="http://xmlphones.appspot.com/utf-8/">http://xmlphones.appspot.com/utf-8/</a>), а город как <a href="http://xmlphones.appspot.com/utf-8/city/26063">http://xmlphones.appspot.com/utf-8/city/26063</a> (и опять это Питер).</p>
<p>Выкидываем из кода несколько обработчиков, привязанных к URL, и заменяем их одним Dispatcher, который будет решать куда направить тот или иной запрос. Так как нашим сервисом уже пользуются люди, старые параметры <em>?encode</em> и <em>?city</em> также оставляем для совместимости, но делаем их второстепенными по сравнению со схемой параметров, заданных через URL.</p>
<p>Вот что получилось в итоге:</p>
<pre>
<div class="python" style="font-family:monospace;color:#000000;"><span style="font-style:italic;color:#808080;"># coding=UTF-8</span>
<span style="font-style:italic;color:#808080;"># -*- coding: utf-8 -*-</span>

<span style="font-weight:bold;color:#ff7700;">import</span> wsgiref.<span style="color:black;">handlers</span>
<span style="font-weight:bold;color:#ff7700;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">ext</span> <span style="font-weight:bold;color:#ff7700;">import</span> webapp
<span style="font-weight:bold;color:#ff7700;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">api</span> <span style="font-weight:bold;color:#ff7700;">import</span> urlfetch
<span style="font-weight:bold;color:#ff7700;">import</span> <span style="color:#dc143c;">os</span>
<span style="font-weight:bold;color:#ff7700;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">ext</span>.<span style="color:black;">webapp</span> <span style="font-weight:bold;color:#ff7700;">import</span> template
<span style="font-weight:bold;color:#ff7700;">from</span> <span style="color:#dc143c;">xml</span>.<span style="color:black;">dom</span> <span style="font-weight:bold;color:#ff7700;">import</span> minidom
<span style="font-weight:bold;color:#ff7700;">import</span> <span style="color:#dc143c;">re</span>
<span style="font-weight:bold;color:#ff7700;">import</span> <span style="color:#dc143c;">time</span>

WEATHER_URL = <span style="color:#483d8b;">'http://informer.gismeteo.ru/rss/%s.xml'</span>

<span style="font-weight:bold;color:#ff7700;">def</span> get_city<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    city = <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">get</span><span style="color:black;">(</span><span style="color:#483d8b;">"city"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
    <span style="font-weight:bold;color:#ff7700;">if</span> city <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">''</span>:
        <span style="font-style:italic;color:#808080;"># Москва</span>
        city = <span style="color:#483d8b;">'27612'</span>
    ccode = <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/.+/meteo/(.+)'</span><span style="color:#ff0099;">,</span> <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">path</span><span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> ccode <span style="color:#1c6c9d;">!=</span> <span style="color:#008000;">None</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> ccode.<span style="color:black;">group</span><span style="color:black;">(</span><span style="color:#ff4500;">1</span><span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> city

<span style="font-weight:bold;color:#ff7700;">def</span> get_encode_templ<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    encode = <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">get</span><span style="color:black;">(</span><span style="color:#483d8b;">"encode"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
    <span style="font-weight:bold;color:#ff7700;">if</span> encode <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">'windows-1251'</span><span style="color:#ff0099;">:</span>
        <span style="font-weight:bold;color:#ff7700;">return</span> encode
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> <span style="color:#483d8b;">'utf-8'</span>

<span style="font-weight:bold;color:#ff7700;">def</span> render_template<span style="color:black;">(</span><span style="color:#008000;">self</span>, templ, template_values=<span style="color:black;">{</span><span style="color:black;">}</span><span style="color:#ff0099;">,</span> encode=<span style="color:#483d8b;">'utf-8'</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    path = <span style="color:#dc143c;">os</span>.<span style="color:black;">path</span>.<span style="color:black;">join</span><span style="color:black;">(</span><span style="color:#dc143c;">os</span>.<span style="color:black;">path</span>.<span style="color:black;">dirname</span><span style="color:black;">(</span>__file__<span style="color:black;">)</span><span style="color:#ff0099;">,</span> <span style="color:#483d8b;">'templates/'</span>+templ<span style="color:black;">)</span>
    resp = template.<span style="color:black;">render</span><span style="color:black;">(</span>path, template_values<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> encode <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">'windows-1251'</span>:
        <span style="font-style:italic;color:#808080;"># Перекодируем из UTF-8 в CP1251</span>
        resp = <span style="color:#008000;">unicode</span><span style="color:black;">(</span>resp, <span style="color:#483d8b;">'utf-8'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
        resp = resp.<span style="color:black;">encode</span><span style="color:black;">(</span><span style="color:#483d8b;">"cp1251"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
    <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">out</span>.<span style="color:black;">write</span><span style="color:black;">(</span>resp<span style="color:black;">)</span>

<span style="font-weight:bold;color:#ff7700;">def</span> is_ipphone<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">match</span><span style="color:black;">(</span><span style="color:#483d8b;">'Allegro'</span><span style="color:#ff0099;">,</span> <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">headers</span>.<span style="color:black;">get</span><span style="color:black;">(</span><span style="color:#483d8b;">'User-Agent'</span>, <span style="color:#483d8b;">''</span><span style="color:#ff0099;"><span style="color:black;">)</span></span><span style="color:black;">)</span> <span style="color:#1c6c9d;">==</span> <span style="color:#008000;">None</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> <span style="color:#008000;">False</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> <span style="color:#008000;">True</span>     

<span style="font-weight:bold;color:#ff7700;">def</span> parse_rss_data<span style="color:black;">(</span>content<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    result = <span style="color:#483d8b;">''</span>
    dom = minidom.<span style="color:black;">parseString</span><span style="color:black;">(</span>content<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">for</span> item <span style="font-weight:bold;color:#ff7700;">in</span> dom.<span style="color:black;">getElementsByTagName</span><span style="color:black;">(</span><span style="color:#483d8b;">'item'</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
        result = result <span style="color:#1c6c9d;">+</span> item.<span style="color:black;">getElementsByTagName</span><span style="color:black;">(</span><span style="color:#483d8b;">'title'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span><span style="color:black;">[</span><span style="color:#ff4500;">0</span><span style="color:black;">]</span>.<span style="color:black;">firstChild</span>.<span style="color:black;">nodeValue</span> <span style="color:#1c6c9d;">+</span> <span style="color:#483d8b;">'<span style="font-weight:bold;color:#000099;">\n</span>'</span> <span style="color:#1c6c9d;">+</span> item.<span style="color:black;">getElementsByTagName</span><span style="color:black;">(</span><span style="color:#483d8b;">'description'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span><span style="color:black;">[</span><span style="color:#ff4500;">0</span><span style="color:black;">]</span>.<span style="color:black;">firstChild</span>.<span style="color:black;">nodeValue</span> <span style="color:#1c6c9d;">+</span> <span style="color:#483d8b;">'<span style="font-weight:bold;color:#000099;">\n</span><span style="font-weight:bold;color:#000099;">\n</span>'</span>
    <span style="font-weight:bold;color:#ff7700;">return</span> result

<span style="font-weight:bold;color:#ff7700;">def</span> rss_to_text<span style="color:black;">(</span><span style="color:#008000;">self</span>, url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    result = urlfetch.<span style="color:black;">fetch</span><span style="color:black;">(</span>url<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> result.<span style="color:black;">status_code</span> <span style="color:#1c6c9d;">==</span> <span style="color:#ff4500;">200</span>:
        resp = parse_rss_data<span style="color:black;">(</span>result.<span style="color:black;">content</span><span style="color:black;">)</span>
        charset = get_uri_encode<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> charset <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">'windows-1251'</span>:
            <span style="font-style:italic;color:#808080;"># Перекодируем из UTF-8 в CP1251</span>
            resp = resp.<span style="color:black;">encode</span><span style="color:black;">(</span><span style="color:#483d8b;">"cp1251"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
        <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">headers</span><span style="color:black;">[</span><span style="color:#483d8b;">'Content-type'</span><span style="color:black;">]</span>=<span style="color:#483d8b;">'text/plain; charset='</span>+charset
        <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">out</span>.<span style="color:black;">write</span><span style="color:black;">(</span>resp<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="color:#008000;">self</span>.<span style="color:black;">redirect</span><span style="color:black;">(</span><span style="color:#483d8b;">'/error.xml'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>

<span style="font-weight:bold;color:#ff7700;">def</span> cbrf<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    charset = get_uri_encode<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
    <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">headers</span><span style="color:black;">[</span><span style="color:#483d8b;">'Content-type'</span><span style="color:black;">]</span>=<span style="color:#483d8b;">'text/plain; charset='</span>+charset
    result = urlfetch.<span style="color:black;">fetch</span><span style="color:black;">(</span><span style="color:#483d8b;">'http://www.cbr.ru/currency_base/D_print.asp?date_req='</span>+<span style="color:#dc143c;">time</span>.<span style="color:black;">strftime</span><span style="color:black;">(</span><span style="color:#483d8b;">'%d.%m.%Y'</span><span style="color:#ff0099;">,</span> <span style="color:#dc143c;">time</span>.<span style="color:black;">localtime</span><span style="color:black;">(</span><span style="color:#ff0099;"><span style="color:black;">)</span></span><span style="color:black;">)</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
    <span style="font-weight:bold;color:#ff7700;">if</span> result.<span style="color:black;">status_code</span> <span style="color:#1c6c9d;">==</span> <span style="color:#ff4500;">200</span>:
        content = <span style="color:#008000;">unicode</span><span style="color:black;">(</span>result.<span style="color:black;">content</span>, <span style="color:#483d8b;">'windows-1251'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
        data=<span style="color:#dc143c;">re</span>.<span style="color:black;">split</span><span style="color:black;">(</span><span style="color:#483d8b;">'&lt;td align="right" &gt;036&lt;/td&gt;'</span><span style="color:#ff0099;">,</span> content<span style="color:black;">)</span><span style="color:black;">[</span><span style="color:#ff4500;">1</span><span style="color:black;">]</span>
        <span style="font-weight:bold;color:#ff7700;">for</span> block <span style="font-weight:bold;color:#ff7700;">in</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">split</span><span style="color:black;">(</span><span style="color:#483d8b;">'&lt;tr bgcolor="#ffffff"&gt;'</span><span style="color:#ff0099;">,</span> data<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            block = <span style="color:#dc143c;">re</span>.<span style="color:black;">sub</span><span style="color:black;">(</span><span style="color:#483d8b;">'<span style="font-weight:bold;color:#000099;">\r</span><span style="font-weight:bold;color:#000099;">\n</span>'</span>, <span style="color:#483d8b;">''</span><span style="color:#ff0099;">,</span> block<span style="color:black;">)</span>
            cells = <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'&lt;td align="left" &gt;&amp;nbsp;&amp;nbsp;(.*?)&lt;/td&gt;&lt;td align="right" &gt;(.*?)&lt;/td&gt;&lt;td&gt;&amp;nbsp;&amp;nbsp;(.*?)&lt;/td&gt;&lt;td align="right"&gt;(.*?)&lt;/td&gt;'</span><span style="color:#ff0099;">,</span> block<span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">if</span> cells <span style="color:#1c6c9d;">!=</span> <span style="color:#008000;">None</span>:
                resp = cells.<span style="color:black;">group</span><span style="color:black;">(</span><span style="color:#ff4500;">2</span><span style="color:black;">)</span><span style="color:#1c6c9d;">+</span><span style="color:#483d8b;">' '</span>+cells.<span style="color:black;">group</span><span style="color:black;">(</span><span style="color:#ff4500;">3</span><span style="color:black;">)</span><span style="color:#1c6c9d;">+</span><span style="color:#483d8b;">' = '</span>+cells.<span style="color:black;">group</span><span style="color:black;">(</span><span style="color:#ff4500;">4</span><span style="color:black;">)</span><span style="color:#1c6c9d;">+</span><span style="color:#483d8b;">'<span style="font-weight:bold;color:#000099;">\n</span>'</span>
                <span style="font-weight:bold;color:#ff7700;">if</span> charset <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">'windows-1251'</span>:
                    <span style="font-style:italic;color:#808080;"># Перекодируем из UTF-8 в CP1251</span>
                    resp = resp.<span style="color:black;">encode</span><span style="color:black;">(</span><span style="color:#483d8b;">"cp1251"</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>
                <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">out</span>.<span style="color:black;">write</span><span style="color:black;">(</span>resp<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="color:#008000;">self</span>.<span style="color:black;">redirect</span><span style="color:black;">(</span><span style="color:#483d8b;">'/error.xml'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>

<span style="font-weight:bold;color:#ff7700;">def</span> error<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">headers</span><span style="color:black;">[</span><span style="color:#483d8b;">'Content-type]'</span><span style="color:black;">]</span>=<span style="color:#483d8b;">'text/plain'</span>
    <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">out</span>.<span style="color:black;">write</span><span style="color:black;">(</span><span style="color:#483d8b;">'Произошла ошибка при работе сервиса'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>

<span style="font-weight:bold;color:#ff7700;">def</span> gismeteo<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    city = get_city<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
    rss_to_text<span style="color:black;">(</span><span style="color:#008000;">self</span>, WEATHER_URL <span style="color:#1c6c9d;">%</span> city<span style="color:black;">)</span>

<span style="font-weight:bold;color:#ff7700;">def</span> yandex_news<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    rss_to_text<span style="color:black;">(</span><span style="color:#008000;">self</span>, <span style="color:#483d8b;">'http://news.yandex.ru/Russia/index.rss'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>

<span style="font-weight:bold;color:#ff7700;">def</span> get_uri_encode<span style="color:black;">(</span><span style="color:#008000;">self</span>, default = <span style="color:#483d8b;">'utf-8'</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    urcode = <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/windows-1251/.*'</span><span style="color:#ff0099;">,</span> <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">url</span><span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:black;">(</span>urcode <span style="color:#1c6c9d;">!=</span> <span style="color:#008000;">None</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
        <span style="font-weight:bold;color:#ff7700;">return</span> <span style="color:#483d8b;">'windows-1251'</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="font-weight:bold;color:#ff7700;">return</span> default

<span style="font-weight:bold;color:#ff7700;">def</span> main_page<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    <span style="font-weight:bold;color:#ff7700;">if</span> is_ipphone<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
        citytmpl = get_city<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
        encode = get_encode_templ<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
        urlcity = <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/(.+)/city/(.+)'</span><span style="color:#ff0099;">,</span> <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">path</span><span style="color:black;">)</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:black;">(</span>urlcity <span style="color:#1c6c9d;">!=</span> <span style="color:#008000;">None</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            citytmpl = urlcity.<span style="color:black;">group</span><span style="color:black;">(</span><span style="color:#ff4500;">2</span><span style="color:black;">)</span>
        encode = get_uri_encode<span style="color:black;">(</span><span style="color:#008000;">self</span>, encode<span style="color:black;">)</span>
        <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">headers</span><span style="color:black;">[</span><span style="color:#483d8b;">'Content-type'</span><span style="color:black;">]</span>=<span style="color:#483d8b;">'text/xml; charset='</span>+encode
        render_template<span style="color:black;">(</span><span style="color:#008000;">self</span>, <span style="color:#483d8b;">'main.xml'</span>, <span style="color:black;">{</span> <span style="color:#483d8b;">'city'</span><span style="color:#ff0099;">:</span> citytmpl, <span style="color:#483d8b;">'encode'</span><span style="color:#ff0099;">:</span> encode <span style="color:black;">}</span><span style="color:#ff0099;">,</span> encode=encode<span style="color:black;">)</span>
    <span style="font-weight:bold;color:#ff7700;">else</span>:
        <span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">headers</span><span style="color:black;">[</span><span style="color:#483d8b;">'Content-type'</span><span style="color:black;">]</span>=<span style="color:#483d8b;">'text/html; charset=utf-8'</span>
        render_template<span style="color:black;">(</span><span style="color:#008000;">self</span>, <span style="color:#483d8b;">'main.html'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>   

<span style="font-weight:bold;color:#ff7700;">class</span> Dispatcher<span style="color:black;">(</span>webapp.<span style="color:black;">RequestHandler</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    <span style="font-weight:bold;color:#ff7700;">def</span> get<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
        <span style="font-style:italic;color:#808080;"># Анализируем только URI до знака ?</span>
        url = <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">url</span>.<span style="color:black;">split</span><span style="color:black;">(</span><span style="color:#483d8b;">'?'</span><span style="color:#ff0099;"><span style="color:black;">)</span></span><span style="color:black;">[</span><span style="color:#ff4500;">0</span><span style="color:black;">]</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/$'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            main_page<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/.+/city/.+'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            main_page<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/utf-8/$'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            main_page<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/windows-1251/$'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            main_page<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/(.+)/yandex_news.xml'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            yandex_news<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/(.+)/meteo.+'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            gismeteo<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        <span style="font-weight:bold;color:#ff7700;">if</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">search</span><span style="color:black;">(</span><span style="color:#483d8b;">'/(.+)/cbrf.xml'</span><span style="color:#ff0099;">,</span> url<span style="color:black;">)</span><span style="color:#ff0099;">:</span>
            cbrf<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>
            <span style="font-weight:bold;color:#ff7700;">return</span>
        error<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>

<span style="font-weight:bold;color:#ff7700;">def</span> main<span style="color:black;">(</span><span style="color:black;">)</span><span style="color:#ff0099;">:</span>
    application = webapp.<span style="color:black;">WSGIApplication</span><span style="color:black;">(</span>
                                         <span style="color:black;">[</span><span style="color:#ff0099;"><span style="color:black;">(</span></span><span style="color:#483d8b;">'/.*'</span><span style="color:#ff0099;">,</span> Dispatcher<span style="color:black;">)</span>
                                         <span style="color:black;">]</span><span style="color:#ff0099;">,</span>
                                         debug=<span style="color:#008000;">True</span><span style="color:black;">)</span>
    wsgiref.<span style="color:black;">handlers</span>.<span style="color:black;">CGIHandler</span><span style="color:black;">(</span><span style="color:#ff0099;"><span style="color:black;">)</span></span>.<span style="color:black;">run</span><span style="color:black;">(</span>application<span style="color:black;">)</span>

<span style="font-weight:bold;color:#ff7700;">if</span> __name__ <span style="color:#1c6c9d;">==</span> <span style="color:#483d8b;">'__main__'</span><span style="color:#ff0099;">:</span>
    main<span style="color:black;">(</span><span style="color:#ff0099;"><span style="color:black;">)</span></span></div>
</pre>
<p><strong>Какие еще будут предложения по добавлению новых сервисов?</strong> Напишите комментарий к этой статье.</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/28/xml-services-part-2/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>Подсказки по использованию App Engine</title>
		<link>http://techwork.ru/2008/08/20/tips-and-tricks-on-app-engine/</link>
		<comments>http://techwork.ru/2008/08/20/tips-and-tricks-on-app-engine/#comments</comments>
		<pubDate>Wed, 20 Aug 2008 09:05:31 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[datastore]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=278</guid>
		<description><![CDATA[Как из приложения получить его идентификатор и версию?
Используйте функцию os.getcwd() или переменную окружения os.environ['PATH_TRANSLATED']
&#62;&#62;&#62; os.getcwd()
'/base/data/home/apps/shell/1.21'
&#62;&#62;&#62; os.getcwd().split('/')[-2]
'shell'
&#62;&#62;&#62; os.getcwd().split('/')[-1]
'1.21'

&#62;&#62;&#62; os.environ['PATH_TRANSLATED']
'/base/data/home/apps/shell/1.21/shell.py'
&#62;&#62;&#62; os.environ['PATH_TRANSLATED'].split('/')[-3]
'shell'
Как определить текущий хост?
Есть один очень интересный файл, который уникален на каждом сервере:
&#62;&#62;&#62; open('/base/python_dist/search.config').read()
'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
mpm_python_dist_v12.apphosting.77627982/bigfiledata/466024'

&#62;&#62;&#62; open('/base/python_dist/search.config').read()
'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
mpm_python_dist_v12.apphosting.77627739/bigfiledata/465336'
Вы можете идентифицировать машину, на которой работает данный процесс, проанализировав хэш содержимого из этого файла. К примеру [...]]]></description>
			<content:encoded><![CDATA[<h3>Как из приложения получить его идентификатор и версию?</h3>
<p>Используйте функцию <em>os.getcwd()</em> или переменную окружения <em>os.environ['PATH_TRANSLATED']</em></p>
<pre style="color:#000000;"><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> os<span style="color:#808030;">.</span>getcwd<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
<span style="color:#0000e6;">'/base/data/home/apps/shell/1.21'</span>
<span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> os<span style="color:#808030;">.</span>getcwd<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>split<span style="color:#808030;">(</span><span style="color:#0000e6;">'/'</span><span style="color:#808030;">)</span><span style="color:#808030;">[</span><span style="color:#808030;">-</span><span style="color:#008c00;">2</span><span style="color:#808030;">]</span>
<span style="color:#0000e6;">'shell'</span>
<span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> os<span style="color:#808030;">.</span>getcwd<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>split<span style="color:#808030;">(</span><span style="color:#0000e6;">'/'</span><span style="color:#808030;">)</span><span style="color:#808030;">[</span><span style="color:#808030;">-</span><span style="color:#008c00;">1</span><span style="color:#808030;">]</span>
<span style="color:#0000e6;">'1.21'</span>

<span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> os<span style="color:#808030;">.</span>environ<span style="color:#808030;">[</span><span style="color:#0000e6;">'PATH_TRANSLATED'</span><span style="color:#808030;">]</span>
<span style="color:#0000e6;">'/base/data/home/apps/shell/1.21/shell.py'</span>
<span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> os<span style="color:#808030;">.</span>environ<span style="color:#808030;">[</span><span style="color:#0000e6;">'PATH_TRANSLATED'</span><span style="color:#808030;">]</span><span style="color:#808030;">.</span>split<span style="color:#808030;">(</span><span style="color:#0000e6;">'/'</span><span style="color:#808030;">)</span><span style="color:#808030;">[</span><span style="color:#808030;">-</span><span style="color:#008c00;">3</span><span style="color:#808030;">]</span>
<span style="color:#0000e6;">'shell'</span></pre>
<h3>Как определить текущий хост?</h3>
<p>Есть один очень интересный файл, который уникален на каждом сервере:</p>
<pre style="color:#000000;"><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> <span style="color:#e34adc;">open</span><span style="color:#808030;">(</span><span style="color:#0000e6;">'/base/python_dist/search.config'</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>read<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
'datapath <span style="color:#808030;">.</span>\nsorttempdir <span style="color:#808030;">.</span>\ndisk <span style="color:#808030;">/</span>export<span style="color:#808030;">/</span>hdc3<span style="color:#808030;">/</span>borgletdata<span style="color:#808030;">/</span>dirs<span style="color:#808030;">/</span>prod<span style="color:#808030;">-</span>appengine<span style="color:#808030;">.</span>\
mpm_python_dist_v12<span style="color:#808030;">.</span>apphosting<span style="color:#808030;">.</span><span style="color:#008c00;">77627982</span><span style="color:#808030;">/</span>bigfiledata<span style="color:#808030;">/</span><span style="color:#008c00;">466024</span>'

<span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span><span style="color:#808030;">&gt;</span> <span style="color:#e34adc;">open</span><span style="color:#808030;">(</span><span style="color:#0000e6;">'/base/python_dist/search.config'</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>read<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
'datapath <span style="color:#808030;">.</span>\nsorttempdir <span style="color:#808030;">.</span>\ndisk <span style="color:#808030;">/</span>export<span style="color:#808030;">/</span>hdc3<span style="color:#808030;">/</span>borgletdata<span style="color:#808030;">/</span>dirs<span style="color:#808030;">/</span>prod<span style="color:#808030;">-</span>appengine<span style="color:#808030;">.</span>\
mpm_python_dist_v12<span style="color:#808030;">.</span>apphosting<span style="color:#808030;">.</span><span style="color:#008c00;">77627739</span><span style="color:#808030;">/</span>bigfiledata<span style="color:#808030;">/</span><span style="color:#008c00;">465336</span>'</pre>
<p>Вы можете идентифицировать машину, на которой работает данный процесс, проанализировав хэш содержимого из этого файла. К примеру так:</p>
<pre style="color:#000000;"><span style="font-weight:bold;color:#800000;">def</span> get_server_id<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    <span style="font-weight:bold;color:#800000;">try</span><span style="color:#808030;">:</span>
        fd   <span style="color:#808030;">=</span> <span style="color:#e34adc;">open</span><span style="color:#808030;">(</span><span style="color:#0000e6;">'/base/python_dist/search.config'</span><span style="color:#808030;">)</span>
        data <span style="color:#808030;">=</span> fd<span style="color:#808030;">.</span>read<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
        fd<span style="color:#808030;">.</span>close<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
    <span style="font-weight:bold;color:#800000;">except</span> <span style="color:#e34adc;">IOError</span><span style="color:#808030;">:</span>
        <span style="font-weight:bold;color:#800000;">return</span> <span style="color:#0000e6;">'unknown'</span>

    <span style="font-weight:bold;color:#800000;">return</span> <span style="color:#0000e6;">'%s'</span> <span style="color:#808030;">%</span> data<span style="color:#808030;">.</span><span style="color:#e34adc;">__hash__</span><span style="color:#808030;">(</span><span style="color:#808030;">)</span></pre>
<p>Google не сообщает, на скольких серверах работает ваше приложение (и скорее всего это будет зависеть от генерируемого вашим сайтом трафика). Для определения того, сколько машин используется для функционирования приложения можно воспользоваться следующей методикой: включить <em>server_id</em> в содержимое страницы сайта. Затем достаточно провести несколько загрузок, чтобы понять сколько уникальных идентификаторов сервера будет выдано.</p>
<pre style="color:#000000;">$ <span style="font-weight:bold;color:#800000;">for</span> i <span style="font-weight:bold;color:#800000;">in</span> <span style="background:#ffffe8 none repeat scroll 0 0;color:#000000;">`seq </span><span style="background:#ffffe8 none repeat scroll 0 0;color:#008c00;">20</span><span style="background:#ffffe8 none repeat scroll 0 0;color:#000000;">`</span><span style="color:#800080;">;</span> <span style="font-weight:bold;color:#800000;">do</span>
    curl <span style="color:#44aadd;">-s</span> http<span style="color:#808030;">:</span><span style="color:#40015a;">/</span><span style="color:#40015a;">/cometchat</span><span style="font-weight:bold;color:#800000;">.</span>appspot<span style="font-weight:bold;color:#800000;">.</span>com<span style="color:#e34adc;">|</span><span style="color:#0f69ff;">\</span>
    grep server_id<span style="color:#800080;">;</span> <span style="color:#0f69ff;">\</span>
  <span style="font-weight:bold;color:#800000;">done</span>    <span style="color:#e34adc;">|</span>sort -n<span style="color:#e34adc;">|</span>uniq <span style="color:#44aadd;">-c</span>

     <span style="color:#008c00;">20</span> server_id<span style="color:#808030;">:</span> <span style="color:#0000e6;">'7341146770217830363'</span></pre>
<p>В примере видно, что приложение работает только на одном сервере.</p>
<h3>Как можно идентифицировать текущий процесс?</h3>
<p>И следующий вопрос: сколько процессов моего приложения запущено на одном сервере? Можно проанализировать с помощью глобальной переменной:</p>
<pre style="color:#000000;">the_process_global <span style="color:#808030;">=</span> <span style="color:#0000e6;">"something"</span>

<span style="font-weight:bold;color:#800000;">def</span> get_process_id<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    <span style="font-weight:bold;color:#800000;">return</span> <span style="color:#0000e6;">'%s'</span> <span style="color:#808030;">%</span> <span style="color:#e34adc;">id</span><span style="color:#808030;">(</span>the_process_global<span style="color:#808030;">)</span></pre>
<p>Теперь мне известно, что приложение задействует два процесса:</p>
<pre style="color:#000000;">$ <span style="font-weight:bold;color:#800000;">for</span> i <span style="font-weight:bold;color:#800000;">in</span> <span style="background:#ffffe8 none repeat scroll 0 0;color:#000000;">`seq </span><span style="background:#ffffe8 none repeat scroll 0 0;color:#008c00;">20</span><span style="background:#ffffe8 none repeat scroll 0 0;color:#000000;">`</span><span style="color:#800080;">;</span> <span style="font-weight:bold;color:#800000;">do</span>
    curl <span style="color:#44aadd;">-s</span> http<span style="color:#808030;">:</span><span style="color:#40015a;">/</span><span style="color:#40015a;">/cometchat</span><span style="font-weight:bold;color:#800000;">.</span>appspot<span style="font-weight:bold;color:#800000;">.</span>com<span style="color:#e34adc;">|</span><span style="color:#0f69ff;">\</span>
    grep _id<span style="color:#800080;">;</span>
  <span style="font-weight:bold;color:#800000;">done</span>    <span style="color:#e34adc;">|</span>sort -n<span style="color:#e34adc;">|</span>uniq <span style="color:#44aadd;">-c</span>

    <span style="color:#008c00;">13</span> process_id<span style="color:#808030;">:</span> <span style="color:#0000e6;">'12457625149327067176'</span>
     <span style="color:#008c00;">7</span> process_id<span style="color:#808030;">:</span> <span style="color:#0000e6;">'3996238433791648184'</span></pre>
<h3>Работаем ли мы в среде разработки или на сервере?</h3>
<p>Я использую такой шаблон:</p>
<pre style="color:#000000;"><span style="font-weight:bold;color:#800000;">if</span> os<span style="color:#808030;">.</span>environ<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'SERVER_SOFTWARE'</span><span style="color:#808030;">,</span><span style="color:#0000e6;">''</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>startswith<span style="color:#808030;">(</span><span style="color:#0000e6;">'Devel'</span><span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    HOST<span style="color:#808030;">=</span><span style="color:#0000e6;">'local'</span>
<span style="font-weight:bold;color:#800000;">elif</span> os<span style="color:#808030;">.</span>environ<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'SERVER_SOFTWARE'</span><span style="color:#808030;">,</span><span style="color:#0000e6;">''</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>startswith<span style="color:#808030;">(</span><span style="color:#0000e6;">'Goog'</span><span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    HOST<span style="color:#808030;">=</span><span style="color:#0000e6;">'google'</span>
<span style="font-weight:bold;color:#800000;">else</span><span style="color:#808030;">:</span>
    <span style="color:#696969;"># logging.error('Неизвестный сервер?')</span>
    HOST<span style="color:#808030;">=</span><span style="color:#0000e6;">'unknown'</span></pre>
<h3>Cookies?</h3>
<p>Google в своем интерфейсе определило объекты <a href="http://googleappengine.ru/docs/webapp/requestclass.html" target="_blank">request</a> и <a href="http://googleappengine.ru/docs/webapp/responseclass.html" target="_blank">response</a> как наследники соответствующих классов из библиотеки <a href="http://pythonpaste.org/webob/reference.html" target="_blank">WebOb</a>. Таким образом мы можем из объекта <em>request</em> получить содержимое cookie запроса:</p>
<pre style="color:#000000;">username <span style="color:#808030;">=</span> self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>cookies<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'username'</span><span style="color:#808030;">,</span> <span style="color:#0000e6;">''</span><span style="color:#808030;">)</span></pre>
<p>К сожалению, вы не сможете напрямую использовать метод <em>response.set_cookie </em>из библиотеки WebOb. Но это можно всегда сделать вручную:</p>
<pre style="color:#000000;">self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>headers<span style="color:#808030;">.</span>add_header<span style="color:#808030;">(</span>
        <span style="color:#0000e6;">'Set-Cookie'</span><span style="color:#808030;">,</span>
        <span style="color:#0000e6;">'username=%s; expires=Fri, 31-Dec-2020 23:59:59 GMT'</span> \
          <span style="color:#808030;">%</span> username<span style="color:#808030;">.</span>encode<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span></pre>
<p>Дополнительные методы работы с cookie есть <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/6120f16c2db05f7e/e8262c5cb4d6685c?lnk=gst" target="_blank">в группе обсуждения</a> на английском языке.</p>
<h3>Отладка работы с данными хранилища</h3>
<p>Я создал <a href="http://ai.pjwstk.edu.pl/%7Emajek/dump/debug.py" target="_blank">простой отладчик для работы с хранилищем</a>. Он добавляет отладочную информацию в конец каждой создаваемой страницы. Для его использования требуется, чтобы классы обработчиков наследовались от <em>debug.DebugMiddleware</em> вместо <em>webapp.RequestHandler</em>.</p>
<p>К примеру:</p>
<pre style="color:#000000;"><span style="font-weight:bold;color:#800000;">class</span> List<span style="color:#808030;">(</span>debug<span style="color:#808030;">.</span>DebugMiddleware<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    <span style="font-weight:bold;color:#800000;">def</span> get<span style="color:#808030;">(</span>self<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
        <span style="color:#808030;">.</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span> blabla <span style="color:#808030;">.</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span></pre>
<p>Отдалочная информация будет выглядеть так:</p>
<pre style="color:#000000;"><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span> Request took<span style="color:#808030;">:</span>   830ms<span style="color:#808030;">/</span>170ms <span style="color:#808030;">(</span>real time<span style="color:#808030;">/</span>cpu time<span style="color:#808030;">)</span>
<span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span> GQLs<span style="color:#808030;">,</span> datastore accessed <span style="color:#008c00;">1</span> times<span style="color:#808030;">.</span>
98ms GQL app<span style="color:#808030;">:</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">:self</span><span style="color:#800000;">"</span>
            kind<span style="color:#808030;">:</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">Image</span><span style="color:#800000;">"</span>
            Order <span style="color:#808030;">{</span>
            property<span style="color:#808030;">:</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">modified</span><span style="color:#800000;">"</span>
            direction<span style="color:#808030;">:</span> <span style="color:#008c00;">2</span>
            <span style="color:#808030;">}</span>
            args<span style="color:#808030;">:</span> <span style="color:#808030;">(</span><span style="color:#008c00;">50</span><span style="color:#808030;">,</span><span style="color:#808030;">)</span> <span style="color:#808030;">{</span><span style="color:#808030;">}</span></pre>
<p>GQL запрос, который сгенерировал ее:</p>
<pre style="color:#000000;">ims <span style="color:#808030;">=</span> Image<span style="color:#808030;">.</span>all<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>order<span style="color:#808030;">(</span><span style="color:#0000e6;">"-modified"</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>fetch<span style="color:#808030;">(</span><span style="color:#008c00;">50</span><span style="color:#808030;">)</span></pre>
<p>Другой пример отладочной информации:</p>
<pre style="color:#000000;"><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span> Request took<span style="color:#808030;">:</span>   150ms<span style="color:#808030;">/</span>130ms <span style="color:#808030;">(</span>real time<span style="color:#808030;">/</span>cpu time<span style="color:#808030;">)</span>
<span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span><span style="color:#808030;">*</span> GQLs<span style="color:#808030;">,</span> datastore accessed xx times<span style="color:#808030;">.</span>
  219ms PUT <span style="color:#808030;">(</span><span style="color:#808030;">{</span><span style="color:#800000;">'</span><span style="color:#0000e6;">full</span><span style="color:#800000;">'</span><span style="color:#808030;">:</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span>
  178ms PUT <span style="color:#808030;">(</span><span style="color:#808030;">{</span><span style="color:#800000;">'</span><span style="color:#0000e6;">full</span><span style="color:#800000;">'</span><span style="color:#808030;">:</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span><span style="color:#808030;">.</span>
    6ms GET <span style="color:#808030;">(</span><span style="color:#808030;">[</span>datastore<span style="color:#808030;">_</span>types<span style="color:#008c00;">.</span>Key<span style="color:#008c00;">.</span>from<span style="color:#808030;">_</span>path<span style="color:#808030;">(</span><span style="color:#800000;">'</span><span style="color:#0000e6;">Image</span><span style="color:#800000;">'</span><span style="color:#808030;">,</span> 350L<span style="color:#808030;">,</span> <span style="color:#808030;">_</span>app<span style="color:#808030;">=</span>u<span style="color:#0000e6;">'</span>srv'<span style="color:#808030;">)</span><span style="color:#808030;">]</span><span style="color:#808030;">,</span><span style="color:#808030;">)</span> <span style="color:#808030;">{</span><span style="color:#808030;">}</span>
    2ms GET <span style="color:#808030;">(</span><span style="color:#808030;">[</span>datastore<span style="color:#808030;">_</span>types<span style="color:#008c00;">.</span>Key<span style="color:#008c00;">.</span>from<span style="color:#808030;">_</span>path<span style="color:#808030;">(</span><span style="color:#800000;">'</span><span style="color:#0000e6;">Image</span><span style="color:#800000;">'</span><span style="color:#808030;">,</span> 349L<span style="color:#808030;">,</span> <span style="color:#808030;">_</span>app<span style="color:#808030;">=</span>u<span style="color:#0000e6;">'</span>srv'<span style="color:#808030;">)</span><span style="color:#808030;">]</span><span style="color:#808030;">,</span><span style="color:#808030;">)</span> <span style="color:#808030;">{</span><span style="color:#808030;">}</span>
    2ms GET <span style="color:#808030;">(</span><span style="color:#808030;">[</span>datastore<span style="color:#808030;">_</span>types<span style="color:#008c00;">.</span>Key<span style="color:#008c00;">.</span>from<span style="color:#808030;">_</span>path<span style="color:#808030;">(</span><span style="color:#800000;">'</span><span style="color:#0000e6;">Image</span><span style="color:#800000;">'</span><span style="color:#808030;">,</span> 348L<span style="color:#808030;">,</span> <span style="color:#808030;">_</span>app<span style="color:#808030;">=</span>u<span style="color:#0000e6;">'</span>srv'<span style="color:#808030;">)</span><span style="color:#808030;">]</span><span style="color:#808030;">,</span><span style="color:#808030;">)</span> <span style="color:#808030;">{</span><span style="color:#808030;">}</span></pre>
<p>Этот отладчик очень удобно использовать в Django.</p>
<h3>Динамическая загрузка изображений на сервер</h3>
<p>Вот код, который я использую:</p>
<pre style="color:#000000;"><span style="color:#a65700;">&lt;</span><span style="font-weight:bold;color:#800000;">form</span><span style="color:#274796;"> </span><span style="color:#074726;">action</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"."</span><span style="color:#274796;"> </span><span style="color:#074726;">method</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"post"</span><span style="color:#274796;"> </span><span style="color:#074726;">enctype</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"multipart/form-data"</span><span style="color:#a65700;">&gt;</span>
    <span style="color:#a65700;">&lt;</span><span style="font-weight:bold;color:#800000;">label</span><span style="color:#a65700;">&gt;</span>File: <span style="color:#a65700;">&lt;/</span><span style="font-weight:bold;color:#800000;">label</span><span style="color:#a65700;">&gt;</span><span style="color:#a65700;">&lt;</span><span style="font-weight:bold;color:#800000;">input</span><span style="color:#274796;"> </span><span style="color:#074726;">name</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"file"</span><span style="color:#274796;"> </span><span style="color:#074726;">type</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"file"</span><span style="color:#a65700;">&gt;</span><span style="color:#a65700;">&lt;</span><span style="font-weight:bold;color:#800000;">br</span><span style="color:#274796;"> </span><span style="color:#a65700;">/&gt;</span>
    <span style="color:#a65700;">&lt;</span><span style="font-weight:bold;color:#800000;">input</span><span style="color:#274796;"> </span><span style="color:#074726;">type</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"submit"</span><span style="color:#a65700;">&gt;</span>
<span style="color:#a65700;">&lt;/</span><span style="font-weight:bold;color:#800000;">form</span><span style="color:#a65700;">&gt;</span></pre>
<p>На стороне сервера:</p>
<pre style="color:#000000;"><span style="font-weight:bold;color:#800000;">class</span> Image<span style="color:#808030;">(</span>db<span style="color:#808030;">.</span>Model<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    name        <span style="color:#808030;">=</span> db<span style="color:#808030;">.</span>StringProperty<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
    content     <span style="color:#808030;">=</span> db<span style="color:#808030;">.</span>BlobProperty<span style="color:#808030;">(</span><span style="color:#808030;">)</span>

<span style="font-weight:bold;color:#800000;">class</span> UploadImage<span style="color:#808030;">(</span>webapp<span style="color:#808030;">.</span>RequestHandler<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    <span style="font-weight:bold;color:#800000;">def</span> post<span style="color:#808030;">(</span>self<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
        <span style="font-weight:bold;color:#800000;">if</span> <span style="color:#0000e6;">'file'</span> <span style="font-weight:bold;color:#800000;">not</span> <span style="font-weight:bold;color:#800000;">in</span> self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>POST<span style="color:#808030;">:</span>
            self<span style="color:#808030;">.</span>error<span style="color:#808030;">(</span><span style="color:#008c00;">400</span><span style="color:#808030;">)</span>
            self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>out<span style="color:#808030;">.</span>write<span style="color:#808030;">(</span><span style="color:#0000e6;">"Файл не указан!"</span><span style="color:#808030;">)</span>
            <span style="font-weight:bold;color:#800000;">return</span>

        <span style="font-weight:bold;color:#800000;">if</span> <span style="color:#808030;">(</span>self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>POST<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'file'</span><span style="color:#808030;">,</span> <span style="color:#e34adc;">None</span><span style="color:#808030;">)</span> <span style="font-weight:bold;color:#800000;">is</span> <span style="color:#e34adc;">None</span> <span style="font-weight:bold;color:#800000;">or</span>
           <span style="font-weight:bold;color:#800000;">not</span> self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>POST<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'file'</span><span style="color:#808030;">,</span> <span style="color:#e34adc;">None</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>filename<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
            self<span style="color:#808030;">.</span>error<span style="color:#808030;">(</span><span style="color:#008c00;">400</span><span style="color:#808030;">)</span>
            self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>out<span style="color:#808030;">.</span>write<span style="color:#808030;">(</span><span style="color:#0000e6;">"Файл не указан!"</span><span style="color:#808030;">)</span>
            <span style="font-weight:bold;color:#800000;">return</span>

        file_data <span style="color:#808030;">=</span> self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>POST<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'file'</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span><span style="color:#e34adc;">file</span><span style="color:#808030;">.</span>read<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
        file_name <span style="color:#808030;">=</span> self<span style="color:#808030;">.</span>request<span style="color:#808030;">.</span>POST<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span><span style="color:#0000e6;">'file'</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>filename

        im <span style="color:#808030;">=</span> Image<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
        im<span style="color:#808030;">.</span>name    <span style="color:#808030;">=</span> file_name
        im<span style="color:#808030;">.</span>content <span style="color:#808030;">=</span> file_data
        im<span style="color:#808030;">.</span>save<span style="color:#808030;">(</span><span style="color:#808030;">)</span>
        self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>out<span style="color:#808030;">.</span>write<span style="color:#808030;">(</span><span style="color:#0000e6;">"Изображение %r сохранено."</span> <span style="color:#808030;">%</span> im<span style="color:#808030;">.</span>name<span style="color:#808030;">)</span></pre>
<h3>Как определить размер и тип изображения</h3>
<p>Существует реализации функции <a href="http://ai.pjwstk.edu.pl/%7Emajek/dump/getimageinfo.py" target="_blank">getImageInfo</a>, которая позволяет определять размер изображения без использования внешних библиотек. Использование очень простое:</p>
<pre style="color:#000000;">content_type<span style="color:#808030;">,</span> width<span style="color:#808030;">,</span> height <span style="color:#808030;">=</span> getImageInfo<span style="color:#808030;">(</span>im<span style="color:#808030;">.</span>content<span style="color:#808030;">)</span></pre>
<h3>Динамическая работа с изображениями</h3>
<p>По этому поводу существует отдельная <a href="http://googleappengine.ru/articles/images.html" target="_blank">статья</a> в официальной документации. Ниже приведен мой упрощенный код, который я использую:</p>
<pre style="color:#000000;"><span style="font-weight:bold;color:#800000;">class</span> ServeImage<span style="color:#808030;">(</span>webapp<span style="color:#808030;">.</span>RequestHandler<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
    <span style="font-weight:bold;color:#800000;">def</span> get<span style="color:#808030;">(</span>self<span style="color:#808030;">,</span> key<span style="color:#808030;">)</span><span style="color:#808030;">:</span>
        im <span style="color:#808030;">=</span> db<span style="color:#808030;">.</span>get<span style="color:#808030;">(</span>db<span style="color:#808030;">.</span>Key<span style="color:#808030;">(</span>key<span style="color:#808030;">)</span><span style="color:#808030;">)</span>
        <span style="font-weight:bold;color:#800000;">if</span> <span style="font-weight:bold;color:#800000;">not</span> im<span style="color:#808030;">:</span>
            self<span style="color:#808030;">.</span>error<span style="color:#808030;">(</span><span style="color:#008c00;">404</span><span style="color:#808030;">)</span>
            <span style="font-weight:bold;color:#800000;">return</span>

        content_type<span style="color:#808030;">,</span> width<span style="color:#808030;">,</span> height <span style="color:#808030;">=</span> getImageInfo<span style="color:#808030;">(</span>im<span style="color:#808030;">.</span>content<span style="color:#808030;">)</span>
        self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>headers<span style="color:#808030;">.</span>add_header<span style="color:#808030;">(</span><span style="color:#0000e6;">"Expires"</span><span style="color:#808030;">,</span> <span style="color:#0000e6;">"Thu, 01 Dec 2014 16:00:00 GMT"</span><span style="color:#808030;">)</span>
        self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>headers<span style="color:#808030;">[</span><span style="color:#0000e6;">"Content-Type"</span><span style="color:#808030;">]</span> <span style="color:#808030;">=</span> content_type
        self<span style="color:#808030;">.</span>response<span style="color:#808030;">.</span>out<span style="color:#808030;">.</span>write<span style="color:#808030;">(</span>im<span style="color:#808030;">.</span>content<span style="color:#808030;">)</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/20/tips-and-tricks-on-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Web2py &#8211; первый взгляд</title>
		<link>http://techwork.ru/2008/08/18/web2py-first-look/</link>
		<comments>http://techwork.ru/2008/08/18/web2py-first-look/#comments</comments>
		<pubDate>Mon, 18 Aug 2008 09:11:58 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web2py]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=255</guid>
		<description><![CDATA[Web2Py (ранее называвшийся Gluon) &#8211; это новое слово в фреймворках для Python. Он очень похож на Django, но позволяет создавать и разрабатывать приложения прямо в онлайне. Он делает за вас большую часть работы и теперь появилась возможность запустить его на платформе Google App Engine.
На сайте Vimeo выложен пример, показывающий начало разработки с Web2Py:

]]></description>
			<content:encoded><![CDATA[<p><strong>Web2Py</strong> (ранее называвшийся Gluon) &#8211; это новое слово в фреймворках для Python. Он очень похож на Django, но позволяет создавать и разрабатывать приложения прямо в онлайне. Он делает за вас большую часть работы и теперь появилась возможность запустить его на платформе Google App Engine.</p>
<p>На сайте Vimeo <a href="http://www.vimeo.com/moogaloop.swf?clip_id=932708&amp;server=www.vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=0" target="_blank">выложен пример</a>, показывающий начало разработки с Web2Py:</p>
<p><a href="http://techwork.ru/wp-content/uploads/2008/08/web2py1.png"><img class="aligncenter size-full wp-image-257" src="http://techwork.ru/wp-content/uploads/2008/08/web2py1.png" alt="" width="431" height="271" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/18/web2py-first-look/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Загрузка файлов через форму Django Forms</title>
		<link>http://techwork.ru/2008/08/18/uploading-files-via-django-forms/</link>
		<comments>http://techwork.ru/2008/08/18/uploading-files-via-django-forms/#comments</comments>
		<pubDate>Mon, 18 Aug 2008 08:37:12 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=253</guid>
		<description><![CDATA[Поле FileField из библиотеки Django Forms можно привязать к свойству типа BlobProperty объекта хранилища. Ниже описано как это делается:
Модель:
from appengine_django.models import BaseModel
from google.appengine.ext import db

class AlbumItem(BaseModel):
  creationDate = db.DateTimeProperty(auto_now_add=True)
  image = db.BlobProperty(required=True)
  note = db.StringProperty()
Отображение формы:
class AlbumItemForm(ModelForm):
 image = FileField()
 note = CharField(widget=Textarea)

 class Meta:
  model = AlbumItem
  exclude = [...]]]></description>
			<content:encoded><![CDATA[<p>Поле FileField из библиотеки Django Forms можно привязать к свойству типа BlobProperty объекта хранилища. Ниже описано как это делается:</p>
<p><strong>Модель:</strong></p>
<pre>from appengine_django.models import BaseModel
from google.appengine.ext import db

class AlbumItem(BaseModel):
  creationDate = db.DateTimeProperty(auto_now_add=True)
  image = db.BlobProperty(required=True)
  note = db.StringProperty()</pre>
<p><strong>Отображение формы:</strong></p>
<pre>class AlbumItemForm(ModelForm):
 image = FileField()
 note = CharField(widget=Textarea)

 class Meta:
  model = AlbumItem
  exclude = ("creationDate")

def albumentry(request):
 if request.POST:
  form = AlbumItemForm(request.POST, request.FILES)
  if form.is_valid():
   albumEntry = form.save()
   return HttpResponseRedirect('/showalbumentry/%s' % albumEntry.key())
  else:
   return render_to_response('albumentry.html', locals())
 else:
  form = AlbumItemForm()
  return render_to_response('albumentry.html', locals())</pre>
<p>Содержимое указанного в коде файла шаблона <em>albumentry.html</em> тривиально, мы не будем полностью описывать его. Главное поместите в код формы шаблонную переменную <strong>{{form}}</strong> и не забудьте выставить атрибут <strong>enctype=&raquo;multipart/form-data&raquo;</strong> в теге &lt;form&gt;.</p>
<p>Все работает автоматически &#8211; содержимое файла с изображением, загружаемое на сервер, будет помещено в свойство <em>image</em>. Используемый код работает на версиях  App Engine SDK 1.1, Django Helper r30 и Django 0.97.</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/18/uploading-files-via-django-forms/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Использование reCAPTCHA в Google App Engine</title>
		<link>http://techwork.ru/2008/08/17/using-recaptcha-with-google-app-engine/</link>
		<comments>http://techwork.ru/2008/08/17/using-recaptcha-with-google-app-engine/#comments</comments>
		<pubDate>Sun, 17 Aug 2008 11:00:53 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[reCAPTCHA]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=251</guid>
		<description><![CDATA[Борьба со спамом приобретает промышленные масштабы, и одним из способов помешать искусственному интеллекту присылать нашему приложению &#171;рекламные подарки&#187;, является установка на сайт CAPTCHA.
Одним из популярных разновидностей такого сервиса является reCAPTCHA, созданная в университете Карнеги Меллон, которая не только дает наилучшие результаты из себе подобных, но и помогает человечеству оцифровывать книги.
С чего начать?
Сначала необходимо получить ключ [...]]]></description>
			<content:encoded><![CDATA[<p>Борьба со спамом приобретает промышленные масштабы, и одним из способов помешать искусственному интеллекту присылать нашему приложению &laquo;рекламные подарки&raquo;, является установка на сайт <a href="http://ru.wikipedia.org/wiki/CAPTCHA" target="_blank">CAPTCHA</a>.</p>
<p>Одним из популярных разновидностей такого сервиса является <a href="http://recaptcha.net/">reCAPTCHA</a>, созданная в университете Карнеги Меллон, которая не только дает наилучшие результаты из себе подобных, но и помогает человечеству оцифровывать книги.</p>
<h3>С чего начать?</h3>
<p>Сначала необходимо получить <a href="http://recaptcha.net/api/getkey" target="_blank">ключ сервиса</a> на сайте recaptcha.net.<br />
После регистрации вы сможете создать неограниченное количество сервисных ключей для вашего домена.<br />
<strong>Примечание:</strong> Если вы хотите, чтобы reCAPTCHA корректно работала в отладочной среде, то необходимо также получить ключ для localhost.<br />
После генерации пары ключей, вы увидите следующую страницу:</p>
<p><a href="http://bp2.blogger.com/_lBJLUOoqxdY/SBhXGtPCKWI/AAAAAAAAAOE/m9wzHNAB51A/s1600-h/recaptcha_yourdomain.PNG"><img style="display:block;text-align:center;cursor:pointer;margin:0 auto 10px;" src="http://bp2.blogger.com/_lBJLUOoqxdY/SBhXGtPCKWI/AAAAAAAAAOE/m9wzHNAB51A/s400/recaptcha_yourdomain.PNG" border="0" alt="" /></a>Сохраните этот публичный и приватный ключ &#8211; они понадобятся вам позже. Если вдруг они будут случайно потеряны, то сможете вернуться на сайт recaptcha.net и снова их получить. Ни в коем случае не сообщайте свой приватный ключ.</p>
<p>После регистрации в сервисе, вам потребуется простой класс на языке <a href="http://bp1.blogger.com/_lBJLUOoqxdY/SBhqddPCKbI/AAAAAAAAAOs/F_Lvw7wbT_4/s1600-h/helloworld_package_structure.png"><img style="float:right;cursor:pointer;margin:0 0 10px 10px;" src="http://bp1.blogger.com/_lBJLUOoqxdY/SBhqddPCKbI/AAAAAAAAAOs/F_Lvw7wbT_4/s320/helloworld_package_structure.png" border="0" alt="" /></a></p>
<p>Python, <a href="http://pypi.python.org/pypi/recaptcha-client" target="_blank">адаптированный для App Engine.</a> Скачать его можно <a href="http://dev.feth.com/Python/Google%20App%20Engine/recaptcha/captcha.source" target="_blank">здесь</a>. После загрузки переименуйте его в captcha.py и скопируйте в каталог приложения (я предпочитаю размещать его в подкаталоге recaptcha/client/ &#8211; не забудьте добавить к каждому подкаталогу пустой файл __init__.py).</p>
<p>Для демонстрации работы мы дополним стандартный пример из Руководства для начинающих Google App Engine.</p>
<p>Откройте файл helloworld.py и добавьте строчки импорта модулей captcha и environ:</p>
<pre><span style="color:#3366ff;">from </span>os <span style="color:#3366ff;">import </span>environ
<span style="color:#3366ff;">from </span>recaptcha.client <span style="color:#3366ff;">import </span>captcha</pre>
<p>Затем перейдите к контроллеру <span style="font-family:courier new;">MainPage</span> и добавьте следующее:</p>
<pre>chtml = captcha.displayhtml(
  public_key = <span style="color:#33cc00;">"ВАШ-ПУБЛИЧНЫЙ-КЛЮЧ"</span>,
  use_ssl = <span style="color:#3366ff;">False</span>,
  error = <span style="color:#3366ff;">None</span>)

template_values = {
<span style="color:#000000;">   ...</span>
<span style="color:#ff0000;">   'captchahtml': chtml</span>
}</pre>
<p>Соответственно замените выражение <span style="font-family:courier new;">ВАШ-ПУБЛИЧНЫЙ-КЛЮЧ</span> своим ключом, иначе получите ошибку:<br />
<span style="font-weight:bold;font-style:italic;">&laquo;Invalid public key. Make sure you copy and pasted it correctly.&raquo;</span></p>
<p>Теперь перейдите к файлу с шаблоном и добавьте тэг <span style="font-family:courier new;">captchahtml</span> внутри формы:</p>
<pre>&lt;form&gt;
  ...
<span style="color:#ff0000;">    {{ captchahtml }}</span>
&lt;/form&gt;</pre>
<p>После этого можно открыть свой браузер и увидеть reCAPTCHA в отдельном iframe:</p>
<p><a href="http://bp3.blogger.com/_lBJLUOoqxdY/SBhhD9PCKYI/AAAAAAAAAOU/78Lp0rAsBjk/s1600-h/recaptcha_example_box.PNG"><img style="display:block;text-align:center;cursor:pointer;margin:0 auto 10px;" src="http://bp3.blogger.com/_lBJLUOoqxdY/SBhhD9PCKYI/AAAAAAAAAOU/78Lp0rAsBjk/s320/recaptcha_example_box.PNG" border="0" alt="" /></a></p>
<p>Теперь после того, как получены отправленные через форму данные, нам необходимо выполнить проверку того, что в катпчу введен правильный код. Мы проведем изменение метода <span style="font-family:courier new;">post</span> класса <span style="font-family:courier new;">Guestbook</span> и добавим код:</p>
<pre><span style="color:#3366ff;">def </span>post(<span style="font-style:italic;">self</span>):
  challenge = <span style="font-style:italic;color:#000000;">self.</span>request.get(<span style="color:#33cc00;">'recaptcha_challenge_field'</span>)
  response  = <span style="font-style:italic;color:#000000;">self</span>.request.get(<span style="color:#33cc00;">'recaptcha_response_field'</span>)
  remoteip  = environ[<span style="color:#33cc00;">'REMOTE_ADDR'</span>]

  cResponse = captcha.submit(
                 challenge,
                 response,
                 <span style="color:#33cc00;">"ВАШ-ПРИВАТНЫЙ-КЛЮЧ"</span>,
                 remoteip)

<span style="color:#3366ff;">    if </span>cResponse.is_valid:
<span style="color:#999999;">        # код введен верный</span>
<span style="color:#999999;">        # продолжаем работу приложения
<span style="color:#000000;">    <span style="color:#3366ff;">else</span>:
  </span></span>    <span style="color:#999999;"><span style="color:#000000;">error = cResponse.error_code
</span></span>    <span style="color:#999999;"><span style="color:#000000;">...
</span></span></pre>
<p>Аналогично, замените выражение <span style="font-family:courier new;">ВАШ-ПРИВАТНЫЙ-КЛЮЧ</span> ранее полученным приватным ключом.</p>
<p>Информация, введенная пользователем в поле reCAPTCHA будет отправлена на проверку вместе с его IP адресом. Мы получим ответ от сервера reCAPTCHA в формате объекта <span style="font-family:courier new;">RecaptchaResponse</span>.</p>
<p>Объект <span style="font-family:courier new;">RecaptchaResponse</span> имеет два атрибута:</p>
<ul>
<li> <span style="font-family:courier new;">is_valid</span>, установленный в True, если тест был пройден удачно (в противном случае False)</li>
<li><span style="font-family:courier new;">error_code</span> , содержащий <a href="http://recaptcha.net/apidocs/captcha/" target="_blank">код ошибки API</a>, если произошла какая-то проблема.</li>
</ul>
<p><strong>Примечание</strong>: В Руководстве для начинающих Google App Engine, информация введенная в формы, передавалась контроллеру Guestbook. В обычном случае мы будем передавать ее в контроллер MainPage, и обрабатывать код ошибки из объекта RecaptchaResponse (если он будет) методом <span style="font-family:courier new;">displayhtml</span> класса captcha:</p>
<pre>chtml = captcha.displayhtml(
  public_key = <span style="color:#33cc00;">"ВАШ-ПУБЛИЧНЫЙ-КЛЮЧ"</span>,
  use_ssl = <span style="color:#3366ff;">False</span>,
  <span style="color:#ff0000;">error = </span><span style="color:#ff0000;">cResponse.error_code</span>)</pre>
<p>Это позволит вывести читаемое сообщение об ошибке пользователю:</p>
<p><a href="http://bp1.blogger.com/_lBJLUOoqxdY/SBhmGdPCKZI/AAAAAAAAAOc/JTuR2GDmQVg/s1600-h/recaptcha_example_box_wrong.PNG"><img style="display:block;text-align:center;cursor:pointer;margin:0 auto 10px;" src="http://bp1.blogger.com/_lBJLUOoqxdY/SBhmGdPCKZI/AAAAAAAAAOc/JTuR2GDmQVg/s320/recaptcha_example_box_wrong.PNG" border="0" alt="" /></a></p>
<p>и дать ему возможность еще раз ввести ответ на CAPTCHA без необходимости повторного ввода других данных в поля формы.</p>
<p><strong>Имейте ввиду:</strong> Для каждой отправленной пользователем CAPTCHA, выполняется удаленный запрос к серверу reCAPTCHA. Этот запрос является <span style="font-weight:bold;">синхронным</span>, и таким образом посетитель будет ждать некоторое время, пока сервер обработает этот запрос. В том случае, если сервер reCAPTCHA в этот момент не был доступен, будет возвращен код ответа <span style="font-family:courier new;">recaptcha-not-reachable</span>.</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/17/using-recaptcha-with-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Операции AVG и SUM в Google App Engine</title>
		<link>http://techwork.ru/2008/08/14/avg-and-sum-in-app-engine/</link>
		<comments>http://techwork.ru/2008/08/14/avg-and-sum-in-app-engine/#comments</comments>
		<pubDate>Thu, 14 Aug 2008 09:26:39 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[datastore]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[тесты]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=219</guid>
		<description><![CDATA[Разработчики, которые ранее использовали обычные реляционные базы данных часто сразу не могут переложить свои навыки на хранилище App Engine и заваливают буквально шквалом вопросов. Один из них: Как выполнить операции SUM и AVG с данными хранилища?. Вы не можете напрямую выполнять в хранилище подобные запросы, вместо этого необходимо постоянно вести учет итогов и хранить их [...]]]></description>
			<content:encoded><![CDATA[<p>Разработчики, которые ранее использовали обычные реляционные базы данных часто сразу не могут переложить свои навыки на хранилище App Engine и заваливают буквально шквалом вопросов. Один из них: <strong>Как выполнить операции SUM и AVG с данными хранилища?</strong>. Вы не можете напрямую выполнять в хранилище подобные запросы, вместо этого необходимо постоянно вести учет итогов и хранить их в специальном счетчике. Вот пример такой реализации:</p>
<p>Файл  <strong>models.py:</strong></p>
<pre>from appengine_django.models import BaseModel
from google.appengine.ext import db
from google.appengine.ext.db import NotSavedError
from decimal import Decimal, getcontext

class Callable:
 # это класс выполняет обертывание метода, так что мы можем вызвать его напрямую без создания экземпляра.

 def __init__(self, anycallable):
  self.__call__ = anycallable

class GlobalCounter(BaseModel):
 name = db.StringProperty(required=True)
 value = db.IntegerProperty(required=True)

class Rating(BaseModel):
 user = db.UserProperty(required=True)
 rating = db.IntegerProperty(required=True) # скрываем его
 oldRating = 0

 def set_rating(self, val):
  self.oldRating = self.rating
  self.rating = val

 def put(self):
  self.save()

 def save(self):

  # получаем значения счетчиков из хранилища
  votes = GlobalCounter.all().filter("name = ", "numberOfVotes").get()
  total = GlobalCounter.all().filter("name = ", "totalAmountOfVotes").get()

  if (votes == None):
   votes = GlobalCounter(name = "numberOfVotes", value = 0)
   votes.save()

  if (total == None):
   total  = GlobalCounter(name = "totalAmountOfVotes", value = 0)
   votes.save()

  # проверяем, является ли рейтинг новым или пользователь просто меняет свой выбор
  isSaved = False
  try:
   self.key()
   isSaved = True
  except NotSavedError:
   isSaved = False

  if (isSaved):
   # если пользователь пересохраняет свой выбор (возможно меняя его) мы вычитаем из итогов старое значение и добавляем новое
   total.value = total.value - self.oldRating + self.rating
  else:
   votes.value += 1
   total.value += self.rating

  votes.save()
  total.save()

  BaseModel.save(self)

 def ___getAverage___():
  votesCounter = GlobalCounter.all().filter("name = ", "numberOfVotes").get()
  totalCounter = GlobalCounter.all().filter("name = ", "totalAmountOfVotes").get()
  getcontext().prec = 3
  return Decimal(str(totalCounter.value)) / Decimal(str(votesCounter.value))

 average = Callable(___getAverage___) # average() будет теперь вести себя как статическая переменная

 def ___getTotal___():
  totalCounter = GlobalCounter.all().filter("name = ", "totalAmountOfVotes").get()
  return totalCounter.value

 sum = Callable(___getTotal___) # sum() будет теперь вести себя как статическая переменная</pre>
<p>А использовать это можно так&#8230; Файл  <strong>tests.py:</strong></p>
<pre>import os
from decimal import Decimal, getcontext

#импортируем загрушки
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
from google.appengine.api import mail_stub
from google.appengine.api import urlfetch_stub
from google.appengine.api import user_service_stub

from google.appengine.api import users

# импортируем замену Django Forms из SDK App Enginee
from google.appengine.ext.db.djangoforms import ModelForm

# Я использую библиотеку Python Mocker для создания тестовых объектов: http://labix.org/mocker
import mocker
from mocker import MockerTestCase

# импортируем нашу модель
from avgtest.models import Rating
from avgtest.models import GlobalCounter

class TestStart(MockerTestCase):

 def setUp(self):

  apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()

  # Используем мнимое хранилище.
  # В этой точке приложения задается, что все обращения к хранилищу, например операции get и put,
  # будут выполняться с временными данными, расположенными в памяти.

  stub = datastore_file_stub.DatastoreFileStub(u'myTemporaryDataStorage', '/dev/null', '/dev/null')
  apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)

  # Используем заглушку для UserService.
  apiproxy_stub_map.apiproxy.RegisterStub('user', user_service_stub.UserServiceStub())
  os.environ['AUTH_DOMAIN'] = 'gmail.com'
  os.environ['USER_EMAIL'] = 'myself@appengineguy.com' # set to '' for no logged in user
  os.environ['SERVER_NAME'] = 'fakeserver.com'
  os.environ['SERVER_PORT'] = '9999'

  # Используем заглушку для urlfetch.
  apiproxy_stub_map.apiproxy.RegisterStub('urlfetch', urlfetch_stub.URLFetchServiceStub())

  # Используем заглушку для почты.
  apiproxy_stub_map.apiproxy.RegisterStub('mail', mail_stub.MailServiceStub())

  self.HttpResponseRedirect = self.mocker.replace("django.http.HttpResponseRedirect")

  self.render_to_response = self.mocker.replace("django.shortcuts.render_to_response")

 def testAverage(self):
  rA = Rating(user = users.get_current_user(), rating = 1)
  rA.save()
  self.assertEquals(1, Rating.average())
  rB = Rating(user = users.get_current_user(), rating = 4)
  rB.put()
  self.assertEquals(Decimal("2.5"), Rating.average())
  rC = Rating(user = users.get_current_user(), rating = 2)
  rC.save()
  self.assertEquals(Decimal("2.33"), Rating.average())

 def testSum(self):
  rA = Rating(user = users.get_current_user(), rating = 2)
  rA.save()
  rB = Rating(user = users.get_current_user(), rating = 4)
  rB.put()
  rC = Rating(user = users.get_current_user(), rating = 3)
  rC.save()
  self.assertEquals(9, Rating.sum())

 def testAverageChangeVote(self):
  rA = Rating(user = users.get_current_user(), rating = 1)
  rA.save()
  self.assertEquals(1, Rating.average())
  rB = Rating(user = users.get_current_user(), rating = 4)
  rB.put()
  self.assertEquals(Decimal("2.5"), Rating.average())
  rC = Rating(user = users.get_current_user(), rating = 2)
  rC.save()
  self.assertEquals(Decimal("2.33"), Rating.average())
  rC.set_rating(3) # пользователь C изменил свою оценку на 3
  rC.save()
  self.assertEquals(Decimal("2.67"), Rating.average())</pre>
<p><strong>Замечание по производительности:</strong> Приведенный выше пример показывает неэффективную работу с счетчиками, однако он указан только для объяснения принципов работы. На практике же необходимо использовать <a href="http://techwork.ru/2008/07/30/%d1%8d%d1%84%d1%84%d0%b5%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d0%b0%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d0%b3%d0%bb%d0%be%d0%b1%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%bc%d0%b8-%d1%81%d1%87%d0%b5/">разделение счетчиков</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/14/avg-and-sum-in-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Правильная работа с Unicode в Python</title>
		<link>http://techwork.ru/2008/08/13/good-work-with-unicode/</link>
		<comments>http://techwork.ru/2008/08/13/good-work-with-unicode/#comments</comments>
		<pubDate>Wed, 13 Aug 2008 12:57:06 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[unicode]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=213</guid>
		<description><![CDATA[
Есть замечательная презентация в виде тезисов или видео. Усвоение этого материала снимет большинство типовых вопросов.
Не используйте нигде в приложении однобайтовые кодировки (cp1251, koi8-r и подобные) &#8211; только UTF-8. App Engine всегда пытается задействовать по умолчанию UTF-8.
Убедитесь, что ваш текстовый редактор сохраняет по умолчанию файл в UTF-8.
В начало каждого файла с кодом добавляйте строчку # coding=UTF-8
Поместите [...]]]></description>
			<content:encoded><![CDATA[<ol>
<li>Есть замечательная презентация в виде <a href="http://www.rupy.ru/static/files/07/02/12/rupyru2007-yurevich-unicode-thesis.pdf" target="_blank">тезисов</a> или <a href="http://video.google.com/videoplay?docid=5035388502434680056" target="_blank">видео</a>. Усвоение этого материала снимет большинство типовых вопросов.</li>
<li>Не используйте нигде в приложении однобайтовые кодировки (cp1251, koi8-r и подобные) &#8211; только UTF-8. App Engine всегда пытается задействовать по умолчанию UTF-8.</li>
<li>Убедитесь, что ваш текстовый редактор сохраняет по умолчанию файл в UTF-8.</li>
<li>В начало каждого файла с кодом добавляйте строчку <strong># coding=UTF-8</strong></li>
<li>Поместите оператор <strong>u</strong> перед каждой строкой, которая содержит символы не из кодировки ascii. К примеру, на немецком u&raquo;Jag är en liten hatt och är bög&raquo;</li>
<li>При обработке входящих запросов убедитесь что корректно используется request.encoding = &laquo;UTF-8&#8243;</li>
<li>Используйте ugettext как псевдоним для _</li>
<li>Если метод (такой как quote() или hashlib.sha224() требует указать в параметрах байтовую строку, приведите ее в формат unicode: theunicodestring.encode(&laquo;utf-8&#8243;)</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/13/good-work-with-unicode/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Кроссдоменный прокси на Google App Engine</title>
		<link>http://techwork.ru/2008/08/12/cross-domain-proxy-on-google-app-engine/</link>
		<comments>http://techwork.ru/2008/08/12/cross-domain-proxy-on-google-app-engine/#comments</comments>
		<pubDate>Tue, 12 Aug 2008 12:00:27 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[flickr]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=208</guid>
		<description><![CDATA[Большинство браузеров не позволяют Javascript Ajax обращения к другим доменам, поэтому использование Flickr API с Google App Engine весьма затруднено. Описанный ниже код позволяет организовать простой прокси-сервер:
import cgi
import urllib
from google.appengine.ext import webapp
from google.appengine.api import urlfetch

class FlickrController(webapp.RequestHandler):
	"""Прокси для Ajax запросов к Flickr"""
	def get(self):
		flickrapiendpoint = 'http://api.flickr.com/services/rest/'
		flickrapikey = 'you_flicker_api_key'

		params = self.request.GET
		params.add('api_key', flickrapikey)
		params.add('format', 'json')
		apiquery = urllib.urlencode(params)

		result = urlfetch.fetch(url=flickrapiendpoint + [...]]]></description>
			<content:encoded><![CDATA[<p>Большинство браузеров не позволяют Javascript Ajax обращения к другим доменам, поэтому использование Flickr API с Google App Engine весьма затруднено. Описанный ниже код позволяет организовать простой прокси-сервер:</p>
<pre class="python"><span style="color:#ff7700;font-weight:bold;">import</span> <span style="color:#dc143c;">cgi</span>
<span style="color:#ff7700;font-weight:bold;">import</span> <span style="color:#dc143c;">urllib</span>
<span style="color:#ff7700;font-weight:bold;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">ext</span> <span style="color:#ff7700;font-weight:bold;">import</span> webapp
<span style="color:#ff7700;font-weight:bold;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">api</span> <span style="color:#ff7700;font-weight:bold;">import</span> urlfetch

<span style="color:#ff7700;font-weight:bold;">class</span> FlickrController<span style="color:black;">(</span>webapp.<span style="color:black;">RequestHandler</span><span style="color:black;">)</span>:
	<span style="color:#483d8b;">""</span><span style="color:#483d8b;">"Прокси для Ajax запросов к Flickr"</span><span style="color:#483d8b;">""</span>
	<span style="color:#ff7700;font-weight:bold;">def</span> get<span style="color:black;">(</span><span style="color:#008000;">self</span><span style="color:black;">)</span>:
		flickrapiendpoint = <span style="color:#483d8b;">'http://api.flickr.com/services/rest/'</span>
		flickrapikey = <span style="color:#483d8b;">'you_flicker_api_key'</span>

		params = <span style="color:#008000;">self</span>.<span style="color:black;">request</span>.<span style="color:black;">GET</span>
		params.<span style="color:black;">add</span><span style="color:black;">(</span><span style="color:#483d8b;">'api_key'</span>, flickrapikey<span style="color:black;">)</span>
		params.<span style="color:black;">add</span><span style="color:black;">(</span><span style="color:#483d8b;">'format'</span>, <span style="color:#483d8b;">'json'</span><span style="color:black;">)</span>
		apiquery = <span style="color:#dc143c;">urllib</span>.<span style="color:black;">urlencode</span><span style="color:black;">(</span>params<span style="color:black;">)</span>

		result = urlfetch.<span style="color:black;">fetch</span><span style="color:black;">(</span>url=flickrapiendpoint + <span style="color:#483d8b;">'?'</span> + apiquery, method=urlfetch.<span style="color:black;">GET</span><span style="color:black;">)</span>
		<span style="color:#008000;">self</span>.<span style="color:black;">response</span>.<span style="color:black;">out</span>.<span style="color:black;">write</span><span style="color:black;">(</span>result.<span style="color:black;">content</span><span style="color:black;">)</span>

<span style="color:#ff7700;font-weight:bold;">def</span> main<span style="color:black;">(</span><span style="color:black;">)</span>:
	application = webapp.<span style="color:black;">WSGIApplication</span><span style="color:black;">(</span>
		<span style="color:black;">[</span><span style="color:black;">(</span><span style="color:#483d8b;">'/flickr/'</span>, FlickrController<span style="color:black;">)</span><span style="color:black;">]</span>,
		debug=<span style="color:#008000;">True</span><span style="color:black;">)</span>
	wsgiref.<span style="color:black;">handlers</span>.<span style="color:black;">CGIHandler</span><span style="color:black;">(</span><span style="color:black;">)</span>.<span style="color:black;">run</span><span style="color:black;">(</span>application<span style="color:black;">)</span>

<span style="color:#ff7700;font-weight:bold;">if</span> __name__ == <span style="color:#483d8b;">"__main__"</span>:
	main<span style="color:black;">(</span><span style="color:black;">)</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/12/cross-domain-proxy-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Пользовательские фильтры Django в Google App Engine</title>
		<link>http://techwork.ru/2008/08/12/custom-django-filters/</link>
		<comments>http://techwork.ru/2008/08/12/custom-django-filters/#comments</comments>
		<pubDate>Tue, 12 Aug 2008 11:28:56 +0000</pubDate>
		<dc:creator>techworkru</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://techworkru.wordpress.com/?p=204</guid>
		<description><![CDATA[
Хотите использовать собственные фильтры Django в App Engine без необходимости тащить вместе с приложением весь код Django целиком? Это делается легко несколькими строчками кода.
Сначала создаем специальный файл, который будет размещен в корне приложения. В моем случае я назвал его customfilters.py:
import re
from google.appengine.ext import webapp

register = webapp.template.create_template_register()

def escapeimg(body):
	return re.sub(r'&#38;lt;img (.*)/&#38;gt;', '[IMG]', body)

register.filter(escapeimg)
Затем добавим следующую строчку вне [...]]]></description>
			<content:encoded><![CDATA[<div class="postentry">
<p>Хотите использовать собственные фильтры Django в App Engine без необходимости тащить вместе с приложением весь код Django целиком? Это делается легко несколькими строчками кода.</p>
<p>Сначала создаем специальный файл, который будет размещен в корне приложения. В моем случае я назвал его customfilters.py:</p>
<pre class="python"><span style="font-weight:bold;color:#ff7700;">import</span> <span style="color:#dc143c;">re</span>
<span style="font-weight:bold;color:#ff7700;">from</span> google.<span style="color:black;">appengine</span>.<span style="color:black;">ext</span> <span style="font-weight:bold;color:#ff7700;">import</span> webapp

register = webapp.<span style="color:black;">template</span>.<span style="color:black;">create_template_register</span><span style="color:black;">(</span><span style="color:black;">)</span>

<span style="font-weight:bold;color:#ff7700;">def</span> escapeimg<span style="color:black;">(</span>body<span style="color:black;">)</span>:
	<span style="font-weight:bold;color:#ff7700;">return</span> <span style="color:#dc143c;">re</span>.<span style="color:black;">sub</span><span style="color:black;">(</span>r<span style="color:#483d8b;">'&amp;lt;img (.*)/&amp;gt;'</span>, <span style="color:#483d8b;">'[IMG]'</span>, body<span style="color:black;">)</span>

register.<span style="color:#008000;">filter</span><span style="color:black;">(</span>escapeimg<span style="color:black;">)</span></pre>
<p>Затем добавим следующую строчку вне определения функции main() приложения, например, сразу после операторов импорта модулей:</p>
<pre class="python"><span style="color:#483d8b;">""</span><span style="color:#483d8b;">"Загрузка своих фильтров Django "</span><span style="color:#483d8b;">""</span>
webapp.<span style="color:black;">template</span>.<span style="color:black;">register_template_library</span><span style="color:black;">(</span><span style="color:#483d8b;">'customfilters'</span><span style="color:black;">)</span></pre>
<p>После этого вы можете использовать определенные в файле customfilters.py фильтры в любых шаблонах без необходимости задания <em>% load foobar % </em></div>
]]></content:encoded>
			<wfw:commentRss>http://techwork.ru/2008/08/12/custom-django-filters/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
