<?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/"
	
	xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>graywolf&#039;s lair &#187; Code snippets</title>
	<atom:link href="http://graywolf.org.ua/category/blog/it/snippets/feed/" rel="self" type="application/rss+xml" />
	<link>http://graywolf.org.ua</link>
	<description>Inhuman being&#039;s diary...</description>
	<lastBuildDate>Thu, 02 Sep 2010 14:11:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Google App Engine + Django</title>
		<link>http://graywolf.org.ua/2010/04/08/google-app-engine-django/</link>
		<comments>http://graywolf.org.ua/2010/04/08/google-app-engine-django/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 21:14:46 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[appengine]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[gaeutilities]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[itblog-ua]]></category>
		<category><![CDATA[zipimport]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/?p=1385</guid>
		<description><![CDATA[Яким би поганим не здався мені на перший погляд Datastore у Google App Engine, але тим не менш для багатьох проектів і його буде цілком достатньо (тим паче, що у roadmap його розвитку майорить довгоочікуваний повнотекстовий пошук). Тому для платформи одного з нових міні-проектів, які нещодавно спали мені на думку мну вибрав саме Google App [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://graywolf.org.ua/wp-content/uploads/2010/04/django-logo-negative-300x136.png" alt="django-logo-negative" title="django-logo-negative" width="120" class="alignright size-medium wp-image-1397" /> <img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" alt="Powered by Google App Engine" align="right" />Яким би <a href="http://graywolf.org.ua/2009/05/24/google-datastore-sucks/">поганим не здався мені на перший погляд Datastore</a> у <a href="http://code.google.com/appengine/">Google App Engine</a>, але тим не менш для багатьох проектів і його буде цілком достатньо (тим паче, що у roadmap його розвитку майорить довгоочікуваний повнотекстовий пошук). Тому для платформи одного з нових міні-проектів, які нещодавно спали мені на думку мну вибрав саме <em>Google App Engine</em>. Водночас мну дуже вже звик до фреймворку <a href="http://www.djangoproject.com/">Django</a> і мається на увазі не лише його <em>ORM</em>, тому вирішив підключити його останню версію (в комплекті з GAE йде 0.96, яка вже ну дууууже застаріла). Але не за допомогою костилів (<a href="http://code.google.com/p/google-app-engine-django/">цього</a> чи <a href="http://code.google.com/p/app-engine-patch/">ось цього</a>) як минулого разу, а просто напряму і викинувши все зайве (тобто фактично все, що було зав&#8217;язано на <em>ORM</em>). І не дивлячись на те, що в Інеті було повно мануалів по підключенню <em>Django</em> помучитись в неочікуваних місцях трохи довелося.<br />
<span id="more-1385"></span><br />
По-перше, сама збірка <em>Django</em>. Я підбирав модулі частково експериментальним шляхом і щоб не прописувати все вручну постійно зробив собі простенький <strong>.bat</strong>-файл, який пакує в архів необхідну частину джанги:</p>
<pre class="brush: plain;">
&quot;C:\Program Files\7-Zip\7z.exe&quot; a django.zip ^
django\__init__.py ^
django\bin ^
django\core ^
django\conf ^
django\db ^
django\dispatch ^
django\forms ^
django\http ^
django\middleware ^
django\shortcuts ^
django\template ^
django\templatetags ^
django\test ^
django\utils ^
django\views ^
django\contrib\__init__.py ^
django\contrib\contenttypes ^
django\contrib\localflavor ^
django\contrib\markup ^
django\contrib\sitemaps ^
django\contrib\humanize ^
django\contrib\formtools
</pre>
<p>Зібраний цим скриптом архівний файлик я підклав у корінь новоствореного gae-проекту. Причому пакування в архів тут робиться не задля економії дискового простору. Просто у <em>App Engine</em> є обмеження на кількість файлів, а в проекті <em>Django</em> їх дуже багато. Тепер залишилась справа за малим: підмінити <em>Django</em> що йде у комплекті з <em>GAE</em> на нашу версію, яку ми завантажимо з архіву за допомогою фічі <a href="http://docs.python.org/release/2.6/library/zipimport.html">zipimport</a>. Тут все досить просто (це мій поточний варіант скрипта, але думаю без якихось проблем має запрацювати і той, що виклдаений на <a href="http://code.google.com/intl/uk/appengine/articles/django10_zipimport.html">офіційній сторінці інтеграції GAE та Django</a>):</p>
<p><strong>main.py</strong></p>
<pre class="brush: python;">#!/usr/bin/env python
# main.py

import os, sys, logging
os.environ[&quot;DJANGO_SETTINGS_MODULE&quot;] = &quot;projectname.settings&quot;

# Google App Engine imports.
from google.appengine.ext.webapp import util

# Uninstall Django 0.96.
for k in [k for k in sys.modules if k.startswith('django')]:
    del sys.modules[k]

# Add Django 1.0 archive to the path.
django_path = 'django.zip'
sys.path.insert(0, django_path)

# Force Django to reload its settings.
from django.conf import settings
settings._target = None

import django.core.handlers.wsgi
import django.core.signals
import django.db

def log_exception(*args, **kwds):
    logging.exception('Exception in request:')

# Log errors.
django.core.signals.got_request_exception.connect(log_exception)

# Unregister the rollback event handler.
django.core.signals.got_request_exception.disconnect(django.db._rollback_on_exception)

def main():
    # Create a Django application for WSGI.
    application = django.core.handlers.wsgi.WSGIHandler()

    # Run the WSGI CGI handler with that application.
    util.run_wsgi_app(application)

if __name__ == &quot;__main__&quot;:
    main()
</pre>
<p>Але найцікавіша частина над якою я намучився найбільше &#8211; це налаштування файлу <strong>settings.py</strong> в самому <em>Django</em>. По-перше, треба повідключати модулі зав&#8217;язані на Django ORM, тобто видалити або закоментити Middleware-класи:</p>
<p><code>django.contrib.sessions.middleware.SessionMiddleware<br />
django.middleware.csrf.CsrfViewMiddleware<br />
django.contrib.auth.middleware.AuthenticationMiddleware<br />
django.contrib.messages.middleware.MessageMiddleware</code></p>
<p><strong>NOTE:</strong> <em>SessionMiddleware</em> варто замінити на той, що йде у комплекті з <a href="http://gaeutilities.appspot.com/">gaeutilities</a> &#8211; тоді ви принаймні зможете скористатись портованим аналогом сессій.</p>
<p>Контекст-процесори:</p>
<p><code>django.contrib.auth.context_processors.auth<br />
django.contrib.messages.context_processors.messages</code></p>
<p>Та додатки:</p>
<p><code>django.contrib.auth<br />
django.contrib.sessions<br />
django.contrib.sites<br />
django.contrib.messages</code></p>
<p>Також, наскільки я зрозумів, портована версія Django має певні проблеми з підтримкою i18n, тому в конфігураційному файлі її варто відключити (але питання інтернаціоналізації для мене вельми актуальне, тому найближчим часом постараюсь цю проблему вирішити):</p>
<p><code>USE_I18N = False</code></p>
<p>Ну от в принципі і все. Мну черпав джерело натхнення з:</p>
<ul>
<li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/">Django on Google App Engine in 13 simple steps by Thomas Brox Røst</a></li>
<li><a href="http://habrahabr.ru/blogs/django/25696/">Django приложение на Google App Engine by diadya_vova</a></li>
<li><a href="http://code.google.com/intl/uk/appengine/articles/django10_zipimport.html">Using Django 1.0 on App Engine with Zipimport by Dan Sanderson</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2010/04/08/google-app-engine-django/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2010/04/django-logo-negative-150x150.png" />
		<media:content url="http://graywolf.org.ua/wp-content/uploads/2010/04/django-logo-negative.png" medium="image">
			<media:title type="html">django-logo-negative</media:title>
			<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2010/04/django-logo-negative-150x150.png" />
		</media:content>
		<media:content url="http://code.google.com/appengine/images/appengine-silver-120x30.gif" medium="image">
			<media:title type="html">Powered by Google App Engine</media:title>
			<media:thumbnail url="http://code.google.com/appengine/images/appengine-silver-120x30.gif" />
		</media:content>
	</item>
		<item>
		<title>Google JS API</title>
		<link>http://graywolf.org.ua/2009/10/22/google-js-api/</link>
		<comments>http://graywolf.org.ua/2009/10/22/google-js-api/#comments</comments>
		<pubDate>Thu, 22 Oct 2009 15:48:51 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[js]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/?p=1201</guid>
		<description><![CDATA[Ті, хто займаються Вебом, звісно, давно знають про цю фічу, тому я публікую це не стільки в розрахунку на когось, скільки на згадку собі, бо задовбався при створоенні кожного нового сайту шукати в шаблонах старих сайтів ці заповітні декілька рядків: &#60;script type=&#34;text/javascript&#34; src=&#34;http://www.google.com/jsapi&#34;&#62;&#60;/script&#62; &#60;script type=&#34;text/javascript&#34;&#62; google.load(&#34;jquery&#34;, &#34;1.3.2&#34;); &#60;/script&#62; Фрагмент вище дозволяє завантажувати потрібну версію jQuery [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://graywolf.org.ua/wp-content/uploads/2009/10/code_logo.png" alt="Google Code" title="Google Code" width="161" height="40" class="alignleft size-full wp-image-1204" style="border: 0;" />Ті, хто займаються Вебом, звісно, давно знають про цю фічу, тому я публікую це не стільки в розрахунку на когось, скільки на згадку собі, бо задовбався при створоенні кожного нового сайту шукати в шаблонах старих сайтів ці заповітні декілька рядків:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://www.google.com/jsapi&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  google.load(&quot;jquery&quot;, &quot;1.3.2&quot;);
&lt;/script&gt;
</pre>
<p>Фрагмент вище дозволяє завантажувати потрібну версію <em>jQuery</em> прямо з Гугла і при виході нової версії не потрібно щось перезаливати на свій сайт &#8211; лише змінити в шаблоні заголовку сторінки номер версії і все готово.</p>
<p>Звісно ж таким чином можна завантажувати не лише <em>jQuer</em>y, а й декілька інших Javascript-фреймворків. На сторінці <a href="http://code.google.com/apis/ajaxlibs/documentation/">Google AJAX Libraries API</a> можна знайти всі бібліотеки та їх версії, що наразі підтримуються.</p>
<p>Більше того, таким чином можна також завантажувати Javascript&#8217;ові сервіси Гугла, наприклад такі як <a href="http://maps.google.com/">Google Maps</a>, але для того доведеться звісно ж при завантаженні <em>JSAPI</em> вказати <em>API Key</em>, який отримується <a href="http://code.google.com/apis/ajaxsearch/signup.html">тут</a> під конкретний домен. Тобто загальний вигляд підключення <em>Google Maps API</em> виглядатиме так:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://www.google.com/jsapi?key=ABCDEFG&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  google.load(&quot;maps&quot;, &quot;2&quot;);
&lt;/script&gt;
</pre>
<p>Детальніше про роботу з <em>JSAPI</em> можна знайти на сторінці <a href="http://code.google.com/intl/uk-UA/apis/ajax/documentation/">Google AJAX API</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2009/10/22/google-js-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2009/10/code_logo-150x40.png" />
		<media:content url="http://graywolf.org.ua/wp-content/uploads/2009/10/code_logo.png" medium="image">
			<media:title type="html">Google Code</media:title>
			<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2009/10/code_logo-150x40.png" />
		</media:content>
	</item>
		<item>
		<title>Приклад використання Twitter REST API</title>
		<link>http://graywolf.org.ua/2009/05/13/twitter-rest-api/</link>
		<comments>http://graywolf.org.ua/2009/05/13/twitter-rest-api/#comments</comments>
		<pubDate>Wed, 13 May 2009 10:39:11 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[bit.ly]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[tricks]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/?p=1018</guid>
		<description><![CDATA[Вчора вночі щось мене пробило і я до 2-ї ночі сидів писав одну штуку собі в допомогу, а сьогодні вранці, коли хотів вже залити виявилось, що це вже зробили до мене, причому найцікавіше, що виглядає все один в один як те, що зробив мну. Не розумію, правда, як я не знайшов його вчора &#8211; мабуть [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.python.org/"><img alt="" src="http://www.python.org/images/python-logo.gif" title="Python logo" class="alignleft" width="211" height="71" /></a>
<p>Вчора вночі щось мене пробило і я до 2-ї ночі сидів писав одну штуку собі в допомогу, а сьогодні вранці, коли хотів вже залити виявилось, що це <a href="http://twitter.com/lostfilmnews/">вже зробили до мене</a>, причому найцікавіше, що виглядає все один в один як те, що зробив мну. Не розумію, правда, як я не знайшов його вчора &#8211; мабуть вже заспаний був, але суть не втому. Щоб робота марно не пропадала поділюся парою коротких сніпетів на пітоні.</p>
<p><span id="more-1018"></span><br />
Перший &#8211; для скорочення URL за допомогою сервісу <a href="http://bit.ly/">bit.ly</a> (перед використанням там треба зареєструватись, щоб отримати API key):</p>
<pre class="brush: python;">import urllib, urllib2
import json # є лише Python 2.6 і старше

bitly_login = &quot;your bitly login&quot;
bitly_apikey = &quot;your API key&quot;

def ShortenURL(url):
  try:
    request = urllib2.urlopen(&quot;http://api.bit.ly/shorten?version=2.0.1&amp;longUrl=%s&amp;login=%s&amp;apiKey=%s&quot; % (url, bitly_login, bitly_apikey))
    result = json.loads(request.read())
    return result[&quot;results&quot;][url][&quot;shortUrl&quot;]
  except:
    return None
</pre>
<p>Другий &#8211; для публікації повідомлення в <a href="http://twitter.com">Twitter</a> (тут використовується несек&#8217;юрна Basic HTTP Authorization, бо з <a href="http://oauth.net/">oAuth</a> це було б набагато складніше, а мені вночі було ліньки возитись).</p>
<pre class="brush: python;">import urllib, urllib2
import base64

login = &quot;your twitter login&quot;
passwd = &quot;your twitter password&quot;

secret = base64.encodestring(&quot;%s:%s&quot; % (login, passwd))[:-1]

def Post(message):
  request = urllib2.Request(&quot;http://twitter.com/statuses/update.xml&quot;, urllib.urlencode({&quot;status&quot;: message}), {&quot;Authorization&quot;: &quot;Basic &quot; + secret})

  try:
    urllib2.urlopen(request)
    return message # returning message if it was successfully sent
  except:
    return None
</pre>
<p>Обожнюю python <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_rolleyes.gif' alt=':roll:' class='wp-smiley' />  </p>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2009/05/13/twitter-rest-api/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:thumbnail url="http://www.python.org/images/python-logo.gif" />
		<media:content url="http://www.python.org/images/python-logo.gif" medium="image">
			<media:title type="html">Python logo</media:title>
			<media:thumbnail url="http://www.python.org/images/python-logo.gif" />
		</media:content>
	</item>
		<item>
		<title>Найпростіша plugin-архітектура на python</title>
		<link>http://graywolf.org.ua/2007/12/20/%d0%bd%d0%b0%d0%b9%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%96%d1%88%d0%b0-plugin-%d0%b0%d1%80%d1%85%d1%96%d1%82%d0%b5%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bd%d0%b0-python/</link>
		<comments>http://graywolf.org.ua/2007/12/20/%d0%bd%d0%b0%d0%b9%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%96%d1%88%d0%b0-plugin-%d0%b0%d1%80%d1%85%d1%96%d1%82%d0%b5%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bd%d0%b0-python/#comments</comments>
		<pubDate>Thu, 20 Dec 2007 19:24:22 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[itblog-ua]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/2007/12/20/%d0%bd%d0%b0%d0%b9%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%96%d1%88%d0%b0-plugin-%d0%b0%d1%80%d1%85%d1%96%d1%82%d0%b5%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bd%d0%b0-python/</guid>
		<description><![CDATA[Так вже вийшло, що мені вдруге довелося писати модульну систему на пітоні. Причому вдруге довелося йти по одних і тих самих граблях, бо приклад минулої реалізації залишився на ноуті, який зараз лежить в сервісному центрі. Шлях нижче не являється оптимальним чи там найкращим, але головне, що він 100% робочий і задовольняє моїм потребам А потреби [...]]]></description>
			<content:encoded><![CDATA[<p>Так вже вийшло, що мені вдруге довелося писати модульну систему на пітоні. Причому вдруге довелося йти по одних і тих самих граблях, бо приклад минулої реалізації залишився на ноуті, який зараз лежить в сервісному центрі. Шлях нижче не являється оптимальним чи там найкращим, але головне, що він 100% робочий і задовольняє моїм потребам <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  А потреби полягають в тому, що кожен плагін представляє собою реалізацію класу з певним набором функцій та має засіб для інстанціювання об&#8217;єкту цього класу не знаючи його імені. Отож&#8230; <span id="more-181"></span></p>
<p>Для початку створимо приблизно наступну систему підкаталогів з файлами, а потім почнемо її наповнювати:</p>
<pre>+ / plugins
| |
| + / plugin1
| | |
| | - __init__.py
| |
| + / plugin2
| | |
| | - __init__.py
| |
| - __init__.py
|
- pluginmanager.py</pre>
<p>Як неважко здогадатись, модуль <em>plugingmanager.py</em> &#8211; буде відповідати за завантаження плагінів. Папка <em>plugins</em> міститиме власне плагіни (кожен з яких буде займати окремий каталог) та файл ініціалізації пакету &#8220;plugins&#8221; (файл <em>__init__.py</em>) &#8211; його можна лишити порожнім, оскільки надпакет <em>plugins</em> використовується лише для групування.</p>
<pre class="brush: python;">#!/usr/bin/python
import os

plugins = []

def LoadModule(module):
    # build package name
    packagename = &quot;plugins.&quot; + module
    # using built-in function __import__() to dynamically load modules; ensure that
    # directory containing &quot;plugins&quot; folder exists in python's sys.path list
    # if not, it can be specified by sys.path.append(path_to_plugins) command before calling __import__()
    mod = __import__(packagename, globals(), locals(), [])

    # if module import was successful
    if ( mod ):
        # try to get the plugin module itself
        components = packagename.split('.')
        for component in components[1:]:
            mod = getattr(mod, component)
        # add the instance of plugin-implemented class to the list
        plugins.append(mod.CreateInstance())

# dynamicaly load plugin packages
def LoadPlugins(pluginpath):
    # enum plugins directory
    for dir in os.listdir(pluginpath + &quot;\\plugins&quot;):
        # for each non-hidden file or directory
        if (dir[0] != &quot;_&quot;) and (dir.endswith (&quot;.py&quot;)):
            # try to load corresponding plugin
            LoadModule(dir)

LoadPlugins(&quot;.&quot;)
for plugin in plugins:
	plugin.Say(&quot;Joey&quot;)
</pre>
<p>Сподіваюсь, коментарі зрозумілі (хоч і англомовні <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ). Отож, ми перебираємо папку з плагінами і для кожого файлу (тобто в нашому випадку каталогу) намагаємось імпортувати його по імені. В пітоні завдяки вищенаведеній структурі будується дерево пакетів. Тобто в нашому випадку у нас є пакет верхнього рівня <em>plugins</em>, який має два підпакети: <em>plugins.plugin1</em> та <em>plugins.plugin2</em>. Для імпорту використовується вбудована функція __import__(). Вона повертає клас модуля верхнього рівня (в нашому випадку &#8211; &#8220;plugins&#8221;), але оскільки нам потрібно дійти до пакета нижнього рівня ми &#8220;занурюємось&#8221; далі використовуючи рекурсивно функцію attrib. В нашому випадку, звісно, можна було зробити простіше, оскільки вкладеність тут має лише один рівень і назва пакету будується в цій же функції, але код я трохи ускладнив спеціально, щоб дати більш загальну та гнучку реалізацію завантаження пакетів чи модулів.</p>
<p>Отримавши клас модуля ми викликаємо його глобальну функцію <em>CreateInstance()</em>, що поверне нам екземпляр реалізованого в модулі класу.</p>
<p>Тепер розглянемо реалізацію власне плагінів. Для нескладних випадків ми можемо запихнути її прямо в опис пакету plugin1 &#8211; файл <em>__init__.py</em>:</p>
<pre class="brush: python;">#!/usr/bin/python

class Plugin1():
	def Say(self, name):
		print &quot;Hello, %s!&quot; % name

def CreateInstance():
	return Plugin1()</pre>
<p>По аналогії можна зробити і реалізацію plugin2. Тепер запустивши програму отримаємо наступне:</p>
<pre class="brush: python;">$ ./pluginmanager.py
Hello, Joey!
Konnichiwa, Joey!</pre>
<p>Набір відповідних плагінів можна тепер збільшувати просто додаючи по аналогії інші підкаталоги-пакети. Звісно, можна переробити це все з підтримкою модулів, а не пакетів (чи не лише пакетів), але мені все ж така структура подобається більше.</p>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2007/12/20/%d0%bd%d0%b0%d0%b9%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%96%d1%88%d0%b0-plugin-%d0%b0%d1%80%d1%85%d1%96%d1%82%d0%b5%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bd%d0%b0-python/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
	</item>
		<item>
		<title>Заміна URL для Live search в IE</title>
		<link>http://graywolf.org.ua/2007/08/30/%d0%b7%d0%b0%d0%bc%d1%96%d0%bd%d0%b0-url-%d0%b4%d0%bb%d1%8f-live-search-%d0%b2-ie/</link>
		<comments>http://graywolf.org.ua/2007/08/30/%d0%b7%d0%b0%d0%bc%d1%96%d0%bd%d0%b0-url-%d0%b4%d0%bb%d1%8f-live-search-%d0%b2-ie/#comments</comments>
		<pubDate>Thu, 30 Aug 2007 14:45:37 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/?p=54</guid>
		<description><![CDATA[Одразу маленька відмазка: те, що надалі описане мені потрібно було виключно по роботі. Сам я IE не користуюсь &#8211; лише Оперою та Файрфоксом Не дивлячись на те, що сабжева задача здається вельми і вельми тривіальною насправді все не атк просто. Мається на увазі URL, по якому переходить браузер, якщо ви введети якусь маячню в Location [...]]]></description>
			<content:encoded><![CDATA[<p>Одразу маленька відмазка: те, що надалі описане мені потрібно було виключно по роботі. Сам я IE не користуюсь &#8211; лише Оперою та Файрфоксом <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  </p>
<p>Не дивлячись на те, що сабжева задача здається вельми і вельми тривіальною насправді все не атк просто. Мається на увазі URL, по якому переходить браузер, якщо ви введети якусь маячню в <em>Location Bar</em> (напр. набір слів, або неіснуючий домен). Точніше велику проблему це становить для IE6. Для Internet Explorer 7 все вирішується просто &#8211; URL для закачки міняється правкою ключа реєстру:</p>
<pre class="brush: plain;">HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\SearchScopes\{0633EE93-D776-472f-A0FF-E1416B8B2E3A}\URL</pre>
<p>Міняємо прописаний там майкрософтівський Live search на щось типу:</p>
<pre class="brush: plain;">http://www.google.com.ua/search?hl=uk&amp;q=&lt;strong&gt;{searchTerms}&lt;/strong&gt;&amp;meta=</pre>
<p>де <em>{searchTerms}</em> &#8211; макрос, на місце якого підставляється введені в рядку браузера лексеми. І все ОК. А от в IE6 такого ключа нема. І там все робиться через дупу. <span id="more-54"></span>Саме цей романтичний шлях і буде далі описано&#8230;</p>
<p>Для визначення URL для підстановки в <em>Address bar</em> в разі, коли IE не може знайти протокол по якому потрібно працювати (ви не вказали його, а дреса не містить ніяких підказок, напр. у вигляді префікса &#8220;www.&#8221;) викликається COM-об&#8217;єкт, що реалізує інтерфейс <a href="http://msdn2.microsoft.com/en-us/library/ms631445.aspx">IURLSearchHook</a>. Цей інтерфейс має лише одну член-функцію: <a href="http://msdn2.microsoft.com/en-us/library/ms631448.aspx">Translate</a>. В комплекті з IE6 йде власна реалізація цього інтерфейсу, яка відкриває сторінку <em>auto.search.msn.com</em>. CLSID об&#8217;єкта, який відповідатиме за постачання URL прописано в ключі <em>HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\URLSearchHooks</em>. По замовучуванню використовується об&#8217;єкт з CLSID <em>{CFBFAE00-17A6-11D0-99CB-00C04FD64497}</em>. Для того, щоб встановити власний URL для пошуку за замовчуванням нам потрібно замінити його своєю реалізацію.</p>
<p>Для цього створюємо звичанйий проект ATL:</p>
<p><center><img src='http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-1.jpg' alt='Customizing IE live search - Create ATL Project' /></center></p>
<p>Параметри залишаємо без змін. Далі в щойноствореному проекті через Class Wizard додаємо <em>ATL Simple Object</em> &#8211; клас, який і буде реалізацією інтерфейсу <a href="http://msdn2.microsoft.com/en-us/library/ms631445.aspx">IURLSearchHook</a>.</p>
<p><center><img src='http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-2.jpg' alt='Customizing IE live search - Create ATL Simple Class' /></center></p>
<p>Даємо цьому класу якесь ім&#8217;я. Вкладку &#8220;Options&#8221; можна не чіпати і залишити все по дефолту.</p>
<p><center><img src='http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-3.jpg' alt='Customizing IE live search - ATL Simple Class Wizard' /></center></p>
<p>Class Wizard зробив для нас практично всю брудну роботу і тепер можна, власне, дописувати необхідні дані.</p>
<p>Файл з описанням класу (в нашому прикладі Searcher.h) доводимо до наступного вигляду:</p>
<pre class="brush: cpp;">// Searcher.h : Declaration of the CSearcher

#pragma once
#include &quot;resource.h&quot;       // main symbols

#include &lt;..\PlatformSDK\include\comdef.h&gt;
#include &lt;shlobj.h&gt;

// ISearcher
[
    object,
    uuid(&quot;A548953B-21A1-490F-A189-0F9FD714BDE2&quot;),
    dual,	helpstring(&quot;ISearcher Interface&quot;),
    pointer_default(unique)
]
__interface ISearcher : IDispatch
{
};

// CSearcher
[
    coclass,
    threading(&quot;apartment&quot;),
    vi_progid(&quot;IESearchHook.Searcher&quot;),
    progid(&quot;IESearchHook.Searcher.1&quot;),
    version(1.0),
    uuid(&quot;94D618D6-E6E7-4EE9-B1B7-24C0B2327853&quot;),
    helpstring(&quot;Searcher Class&quot;)
]
class ATL_NO_VTABLE CSearcher :
    public ISearcher,
    public IURLSearchHook
{
public:
    CSearcher() {}

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(CSearcher)
        COM_INTERFACE_ENTRY(ISearcher)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IURLSearchHook)
    END_COM_MAP()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease() {}

public:
    STDMETHODIMP Translate(LPWSTR lpwszSearchURL, DWORD cchBufferSize)
    {
        LPWSTR wszURL = new WCHAR[cchBufferSize];
        ::LoadStringW(_AtlBaseModule.GetModuleInstance(), IDS_URL404, wszURL, cchBufferSize);
        wcsncat(wszURL, lpwszSearchURL, cchBufferSize);

        ZeroMemory(lpwszSearchURL, wcslen(wszURL));
        wcsncpy( lpwszSearchURL, wszURL, cchBufferSize);
        delete [] wszURL;

        return S_OK;
    }
};
</pre>
<p>Далі створюємо ресурс в String table з ідентифікатором <em>IDS_URL404</em> і змістом типу: <em>http://www.google.com/search?q=</em> (це пошук по гуглю) і збираємо продукт. Реєстрацію COM-об&#8217;єкта Visual Studio зробить за нас, але для того, щоб зареєструвати цю dll-бібліотеку на іншому компі потрібно буде виконати команду:</p>
<pre class="brush: plain;">regsvr32 IESearchHook.dll</pre>
<p>До речі, по коду ще прошу звернути увагу на рядок:</p>
<pre class="brush: cpp;">#include &lt;..\PlatformSDK\include\comdef.h&gt;</pre>
<p>Справа в тому, що принаймні в Visual Studio .NET (2003) є два файли <em>comdef.h</em> і в одному з них не прописано GUID для <a href="http://msdn2.microsoft.com/en-us/library/ms631445.aspx">IURLSearchHook</a>. І тоді при компіляції в рядку:</p>
<pre class="brush: cpp;">        COM_INTERFACE_ENTRY(IURLSearchHook)</pre>
<p>може видаватись помилка типу:</p>
<pre class="brush: cpp;">d:\Work\Other\IESearchHook\Searcher.h(49) : error C2787: 'IURLSearchHook' : no GUID has been associated with this object</pre>
<p>Тому для того, щоб використовувати нормальний <em>comdef.h</em> пересвідчіться, що використовується саме той, який лежить в:</p>
<pre class="brush: plain;">PATH_TO_VISUAL_STUDIO\Vc7\PlatformSDK\Include\</pre>
<p>Ну і нарешті останній крок &#8211; це підстановка нашого COM-об&#8217;єкту замість експлорерівського. Для цього в ключі</p>
<pre class="brush: plain;">HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\URLSearchHooks</pre>
<p>видаляємо параметр, ім&#8217;я якого є CLSID поточного об&#8217;єкту, що видає рядок пошуку і створюємо параметр з іменем, рівним CLSID нашого об&#8217;єкту, обгонувши його в фігурні дужки. Для прикладу вище &#8211; це буде &#8220;{94D618D6-E6E7-4EE9-B1B7-24C0B2327853}&#8221;.</p>
<p>От і все. Запускаємо <em>Internet Explorer</em> і насолоджуємось пошуком в <em>Google</em> замість <em>Live Search</em> <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Цей спосіб випробувано для <em>Internet Explorer</em> версій 6 та 7. По ідеї має працювати починаючи з 4-го, але це не перевірялося.</p>
<p>Звісно, для реальної роботи функцію Translate треба буде дещо ускладнити, але то вже більш тривіальна справа.</p>
<p>За реалізацію цього завдання вчасно я мушу подякувати наявності наступних двох ресурсів:</p>
<ol>
<li><a href="http://answers.google.com/answers/threadview?id=305908">Google Answers: Change Autosearch URL in IE6</a>. Але спосіб описаний тут не зовсім працює <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Одна з помилок &#8211; це якраз описаний вище прикол з <em>comdef.h</em></li>
<li><a href="http://www.renpeicheng.com/html/2007-03/1653.html">Якийсь китайський ресурс</a>. Там добре, що була купа скрінів та багато коду і я інтуітивно здогадався в цій купі ієрогліфів в чому була лажа <img src='http://graywolf.org.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  </li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2007/08/30/%d0%b7%d0%b0%d0%bc%d1%96%d0%bd%d0%b0-url-%d0%b4%d0%bb%d1%8f-live-search-%d0%b2-ie/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-1.jpg" />
		<media:content url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-1.jpg" medium="image">
			<media:title type="html">Customizing IE live search - Create ATL Project</media:title>
			<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-1.jpg" />
		</media:content>
		<media:content url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-2.jpg" medium="image">
			<media:title type="html">Customizing IE live search - Create ATL Simple Class</media:title>
			<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-2.jpg" />
		</media:content>
		<media:content url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-3.jpg" medium="image">
			<media:title type="html">Customizing IE live search - ATL Simple Class Wizard</media:title>
			<media:thumbnail url="http://graywolf.org.ua/wp-content/uploads/2007/08/ie-hook-3.jpg" />
		</media:content>
	</item>
		<item>
		<title>Доступ до Event&#8217;у, що створюється в сервісі</title>
		<link>http://graywolf.org.ua/2007/08/23/%d0%b4%d0%be%d1%81%d1%82%d1%83%d0%bf-%d0%b4%d0%be-event%d1%83-%d1%89%d0%be-%d1%81%d1%82%d0%b2%d0%be%d1%80%d1%8e%d1%94%d1%82%d1%8c%d1%81%d1%8f-%d0%b2-%d1%81%d0%b5%d1%80%d0%b2%d1%96%d1%81%d1%96/</link>
		<comments>http://graywolf.org.ua/2007/08/23/%d0%b4%d0%be%d1%81%d1%82%d1%83%d0%bf-%d0%b4%d0%be-event%d1%83-%d1%89%d0%be-%d1%81%d1%82%d0%b2%d0%be%d1%80%d1%8e%d1%94%d1%82%d1%8c%d1%81%d1%8f-%d0%b2-%d1%81%d0%b5%d1%80%d0%b2%d1%96%d1%81%d1%96/#comments</comments>
		<pubDate>Thu, 23 Aug 2007 16:49:03 +0000</pubDate>
		<dc:creator>graywolf</dc:creator>
				<category><![CDATA[Code snippets]]></category>

		<guid isPermaLink="false">http://graywolf.org.ua/?p=25</guid>
		<description><![CDATA[Сьогодні мав кількагодинний геморой з написанням сервісу. Точніше завдяки кодпроджекту сам сервіс написався на раз (стаття про те як написати сервіс [англ.]), а от взаємодія його з експлореровським BHO вилилась в здоровецьку проблему. А все через кляті права доступу до об&#8217;єктів ядра. Проблема, власне, була в тому, що Windows service який мну написав запускається по [...]]]></description>
			<content:encoded><![CDATA[<p>Сьогодні мав кількагодинний геморой з написанням сервісу. Точніше завдяки кодпроджекту сам сервіс написався на раз (<a href="http://www.codeproject.com/system/windows_nt_service.asp">стаття про те як написати сервіс [англ.]</a>), а от взаємодія його з експлореровським BHO вилилась в здоровецьку проблему. А все через кляті права доступу до об&#8217;єктів ядра. Проблема, власне, була в тому, що Windows service який мну написав запускається по дефолту як і всі інші сервіси &#8211; від користувача SYSTEM. І от з тим-то і халепа, бо права на доступ до об&#8217;єктів ядра, що створються в сервісі виявляються спартанськими і звичанйому смертному, чи то пак додатку запущенному користувачем його не відкрити. Була купа ідей як його зробити, але зрештою як завжди допоміг Гугль, який вивів мене спочатку <a href="http://discuss.joelonsoftware.com/default.asp?joel.3.226319.5">сюди</a>, а потім і <a href="http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1322">сюди</a>. В результаті і народилось рішення.</p>
<p><span id="more-25"></span><br />
Некрасиве, несек&#8217;юрне, але робоче:</p>
<pre class="brush: cpp;">SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor( &amp;sd, SECURITY_DESCRIPTOR_REVISION );
SetSecurityDescriptorDacl( &amp;sd, TRUE, NULL, FALSE );

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &amp;sd;
sa.bInheritHandle = false;

ATL::CEvent event;
event.Create( &amp;sa, 1, 0, c_eventName );</pre>
<p>В результаті маємо об&#8217;єкт Event з доступом для службового користувача &#8220;Everyone&#8221;. Отаке. Можна, звісно, позаморачуватись з токенами, видирати ім&#8217;я залогіненого користувача і виставляти доступ лише для нього, але то такий геморой&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://graywolf.org.ua/2007/08/23/%d0%b4%d0%be%d1%81%d1%82%d1%83%d0%bf-%d0%b4%d0%be-event%d1%83-%d1%89%d0%be-%d1%81%d1%82%d0%b2%d0%be%d1%80%d1%8e%d1%94%d1%82%d1%8c%d1%81%d1%8f-%d0%b2-%d1%81%d0%b5%d1%80%d0%b2%d1%96%d1%81%d1%96/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
	</item>
	</channel>
</rss>
