28.02.2010

Собираем свой vim под cygwin

VIM в пакетах Cygwin по умолчанию идет без поддержки python. В нем не доступно omni дополнение и много прочих вкусных штук. В частности нельзя писать плагины к VIM на питоне. Ну почти нельзя, всегда можно вызвать питоний скрипт из шелла. Я уже год как то с этим жил, но начав углубленное изучение питона решил, что дальше так продолжаться не может. В интернете есть статьи по компиляции Vim для Windows с использованием cygwin, но все подразумевают компиляцию VIM как отдельной программы. Такой редактор в cygwin шелле у вас работать не будет.

Итак, скачиваем исходники VIM, свеженькие из транка:

svn co https://vim.svn.sourceforge.net/svnroot/vim/vim7

Нужно убедиться, что в cygwin установлены пакеты:

Devel/gcc-g++, Devel/make, System/man и Devel/gcc-mingw, Shells/bash, Utils/bzip2 и Devel/mingw-runtime, ncurses, cscope

Идем в src и редактируем Makefile

CONF_OPT_GUI = --disable-gui
CONF_OPT_PYTHON = --enable-pythoninterp
CONF_OPT_CSCOPE = --enable-cscope
CONF_OPT_MULTIBYTE = --enable-multibyte
CONF_OPT_INPUT = --enable-xim
CONF_OPT_OUTPUT = --enable-fontset
CONF_OPT_FEAT = --with-features=huge
CONF_OPT_X = --without-x

Все, делаем make и после успешного окончания make install

Наслаждаемся

23.02.2010

Паттерны программирования в питоне

Как известно впервые концепция паттернов программирования (проектирования) была опубликована "Бандой четырех" (Erich Gamma, Richard Helm, Ralph Johnson, Jon Vlissides; 1995). В ней они описали стандартные шаблоны проектирования программного кода, с которыми так или иначе сталкивается каждый разработчик. Это было сделано с целью, решить проблему "изобретения велосипедов" в программном коде, а также стандартизировать терминологию используемую разработчиками в общении. По моему скромному мнению, часть этих шаблонов узкоспециализирована и относится непоседственно к типизированным языкам C++ и Java, на примере которых и строилась вся концепция. Например паттерн Singleton предназначен для описания класса имеющего в любой момент времени единственный объект реализацию и доступный всем компонентам программы. В python уже есть модули, которые и являются по факту Singleton. Пишем модуль singleton.py и в любом месте программы делаем import syngleton. Вот, всегда единственный инстанс (sys.modules[наш модуль]), всегда доступен, никаких дополнительных телодвижений. Или паттерны Iterators и Generators, присутствующие в python изначально.

Другое дело например паттерн Observer. Он относится к паттернам поведения и реализует обработку событий. Реализуется связь один ко многим, между объектом и зависимыми от него компонентами. При изменении состояния объекта, все зависимые компоненты оповещаются и изменяются автоматически.

Вот простейшая реализация этого шаблона:

class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        if not observer in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        try:
            self._observers.remove(observer)
        except ValueError:
            pass

    def notify(self, modifier=None):
        for observer in self._observers:
            if modifier != observer:
                observer.update(self)


# Example usage
class Data(Subject):
    def __init__(self, name=''):
        Subject.__init__(self)
        self.name = name
        self.data = 0

    def setData(self, data):
        self.data = data
        self.notify()

    def getData(self):
        return self.data


class HexViewer:
    def update(self, subject):
        print 'HexViewer: Subject %s has data 0x%x' % (subject.name, subject.getData())


class DecimalViewer:
    def update(self, subject):
        print 'DecimalViewer: Subject %s has data %d' % (subject.name, subject.getData())


# Example usage...
def main():
    data1 = Data('Data 1')
    data2 = Data('Data 2')
    view1 = DecimalViewer()
    view2 = HexViewer()
    data1.attach(view1)
    data1.attach(view2)
    data2.attach(view2)
    data2.attach(view1)

    print "Setting Data 1 = 10"
    data1.setData(10)
    print "Setting Data 2 = 15"
    data2.setData(15)
    print "Setting Data 1 = 3"
    data1.setData(3)
    print "Setting Data 2 = 5"
    data2.setData(5)
    print "Detach HexViewer from data1 and data2."
    data1.detach(view2)
    data2.detach(view2)
    print "Setting Data 1 = 10"
    data1.setData(10)
    print "Setting Data 2 = 15"
    data2.setData(15)

if __name__ == '__main__':
    main()

В примере в принципе все понятно из кода. Мы заводим специальный класс способный подписывать, отписывать и оповещать наблюдателей и наследуем от него основной класс. У наблюдателей соответственно должны присутствовать специальные интерфейсы для работы с событиями.

Вот пример посложнее:

Допустим у нас есть объект-таймер, каждую секунду оповещающий подписанных на него наблюдателей о своем изменении. Код класса этого объекта может выглядеть так:

class ClockTimer:
        def GetHour(self):
                return self._hour
        def GetMinute(self):
                return self._minute
        def GetSecond(self):
                return self._second

        TickEvent = Event()
        def OnTick(self):
                ClockTimer.TickEvent.call(self, self.GetHour(),
                        self.GetMinute(), self.GetSecond())

        def Tick(self):
                # update internal time-keeping state
                # ...
                self.OnTick()

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

class DigitalClock(Widget):
        def __init__(self, clockTimer):
                self.__subject = clockTimer
                clockTimer.TickEvent += self.Update

        def close(self):
                self.__subject.TickEvent -= self.Update

        def Update(self, subject, hour, min, sec):
                self.displayedTime = (hour, min, sec)
                self.Draw()

        def Draw(self):
                # draw the digital clock

Создадим вспомогательный класс Delegate, способный манипулировать коллекцией методов или функций, мы же не знаем сколько наблюдателей подпишутся на изменения.

class Delegate:
    '''Handles a list of methods and functions
    Usage:
        d = Delegate()
        d += function    # Add function to end of delegate list
        d(*args, **kw)   # Call all functions, returns a list of results
        d -= function    # Removes last matching function from list
        d -= object      # Removes all methods of object from list
    '''
    def __init__(self):
        self.__delegates = []

    def __iadd__(self, callback):
        self.__delegates.append(callback)
        return self

    def __isub__(self, callback):
        # If callback is a class instance,
        # remove all callbacks for that instance
        self.__delegates = [ cb
            for cb in self.__delegates
                if getattr(cb, 'im_self', None) != callback]

        # If callback is callable, remove the last
        # matching callback
        if callable(callback):
            for i in range(len(self.__delegates)-1, -1, -1):
                if self.__delegates[i] == callback:
                    del self.__delegates[i]
                    return self
        return self

    def __call__(self, *args, **kw):
        return [ callback(*args, **kw)
            for callback in self.__delegates]

Класс способен принять функцию, удалить функцию и вызвать все функции по порядку. И наконец описываем класс события.

class Event(property):
    '''Class event notifier
    Usage:
        class C:
            TheEvent = Event()
            def OnTheEvent(self):
                self.TheEvent(self, context)

        instance = C()
        instance.TheEvent += callback
        instance.OnTheEvent()
        instance.TheEvent -= callback
    '''
    def __init__(self):
        self.attrName = attrName = "__Event_" + str(id(self))
        def getEvent(subject):
            if not hasattr(subject, attrName): 
                setattr(subject, attrName, Delegate())
            return getattr(subject, attrName)
        super(Event, self).__init__(getEvent)

    def call(self, subject, *args, **kw):
        if hasattr(subject, self.attrName):
            getattr(subject, self.attrName)(subject, *args, **kw)

Самое сложное разобраться в классе Event. Его основная задача создать объект Delegate не сразу, а только при необходимости. Итак в DigitalClock мы подписываемся на событие TickEvent, а в ClockTimer зажигаем это событие.

18.02.2010

Quicksand: плагин плавного эффекта перехода и фильтрации сетов элементов


Библиотека предназначена для фильтрации и плавного переключения наборов HTML элементов. Используя ее как jQuery плагин вы можете написать следующий код:

$('#source').quicksand( $('#destination li') );

И создать плавные визуальные переключения наборов данных:

<ul id="source">
<li data-id="iphone">iPhone OS</li>
<li data-id="android">Android</li>
<li data-id="winmo">Windows Mobile</li>
</ul>
<ul id="destination" class="hidden">
<li data-id="macosx">Mac OS X</li>
<li data-id="macos9">Mac OS 9</li>
<li data-id="iphone">iPhone OS</li>
</ul>

Вы можете дополнительно настраивать параметры эффектов. Документация и примеры доступны здесь.

31 тезис

1. Невозможно стремиться к своей мечте, продолжая держаться за «надежное» прошлое;

2. Лучше отважиться на великие дела и достичь триумфа, даже если придется потерпеть множество неудач, чем оставаться в рядах посредственностей, не знающих ни радости, ни страданий и живущих жизнью, в которой нет места ни победам, ни поражениям.

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

4. Даже если вы не хотите принимать решение, на самом деле вы его уже приняли. Вы решили, что все должно оставаться по-старому. Решили ничего не решать. Однако такое состояние сопряжено с самыми большими затратами вашей энергии и оно убивает вас.

5. Любое решение лучше чем его отсутствие.

6. Победители принимают решения быстро т.к. знают, что даже плохое решение лучше чем никакое.

7. Если вы перестали расти и учиться, то жизнь становится пустой и бессмысленной.

8. Не существует нейтрального направления, поэтому стоит постоянно задавать себе вопрос: «В каком направлении я двигаюсь?!»

9. Если вы решите улучшить свою жизнь всего на 1% в месяц по пяти компонентам: здоровье, общении, финансах, эмоциях и работе, то всего в течение года вы улучшите ее на 60%

10. Даже в телешоу тот, кто не знает ответа на вопрос, не проходит в следующий круг. Также и в жизни!

11. Люди любят «тайм-менеджмент». Это модно! Он помогает втиснуть в ваше время больше различных дел. Но люди часто забывают что «тайм-менеджменты» крадут ваше время, в которое вы могли бы радоваться жизни.

12. Очень часто человек обожествляет часы и забывает про компас. Он ловит и экономит часы и минуты, утрачивая впустую годы своей жизни.

13. «Скучают» только те, кто не умеет пользоваться и наслаждаться моментом и ситуацией, в которой он оказался.

14. Время от времени необходимо останавливаться, чтобы вас не унес поток «повседневных дел».

15. Что можно сделать для того, чтобы сегодняшний день прошел более осознанно?

16. Мелочи имеют власть только над теми, кто мелок душою.

17. 80% заработка определяется всего 20% ваших усилий.

18. Многие ищут жизни где царит вечное лето, но смена сезонов это данность и с ней надо уметь смиряться.

19. Рассматривайте «зиму» в делах не как личную неудачу, а как часть системы где вы оказались.

20. Как правило ошибки совершаются там, где работа более сложна и уникальна. Ничто грандиозное вы не сможете сделать, если боитесь ошибок и провала.

21. Нельзя влить бутылку вина в стакан целиком. Он слишком мал. А не мала ли ваша личность для ваших желаний?!

22. Чем лучше мы становимся, тем больше возможностей у нас появляется.

23. Успехи делают нас богаче, но благодаря неудачам мы растем.

24. Шесть препятствий на пути к успеху: самомнение, невежество, тщеславие, страх, сомнение и чувство вины.

25. Три признака хорошей работы:

— она доставляет вам удовольствие
— соответствует вашим талантам и способностям
— позволяет разрешать проблемы других и зарабатывать деньги

26. Любую работу можно делать руководствуясь правильным и неправильным к ней отношением.

27. Четыре сочетания «работа — отношение» и их итоги:

«Плохая работа и неправильное отношение» (катастрофа!)
«Плохая работа и правильное отношение» (небольшой успех очень дорогой ценой)
«Хорошая работа и неправильное отношение» (небольшой успех)
«Хорошая работа и правильное отношение» (Успех в кратчайший срок)

28. Где вы в сочетаниях п.27?!

29. Каждый из нас рождается с глыбой мрамора внутри и инструментами для ее обработки. Мы можем таскать в себе ее, можем разбить ее в щебень, а можем придать ей изумительную форму.

30. Знание — всего лишь потенциальная сила. Морально-волевые качества — вот в чем секрет успеха!

31. Сократ считал ленивым того, кто способен на большее, но останавливается на достигнутом.

17.02.2010

Простейшая реализация jsonp на javascript

Вот подсмотрел простейшую реализацию этого кросс-доменного протокола на js. Сгодится для моих API.

// simplest JSONP request implementation
function jsonp (url, callback) {
    var name = 'jsonp' +  +new Date,
        script = document.createElement('script'),
        head = document.getElementsByTagName('head')[0];
    window[name] = callback;
    script.src = url.replace(/=\?/, '=' + name);
    head.insertBefore(script, head.firstChild);
}

И еще одно сегодняшнее открытие, прекрасная среда для онлайн коллективного редактирования JS http://jsbin.com/

15.02.2010

Встаю в шесть утра

Делая попытку хоть что-то поменять в своей жизни, начал своеобразный эксперимент со сном и сегодня впервые проснулся в шесть утра. Обычное время подъема для меня 2-3 часа дня.

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

Любимая женщина запирается в соседней комнате с ноутом и сидит круглыми сутками в контакте, да в скайпе с друзьями. Смертельно устал от этого, думаю уйду к незаменимому верному товарищу по цеху, пожить на недельку другую. Нет никакого смысла это выносить, думаю нам стоит отдохнуть от друг друга.

Ну вот, такая мини-заметка получилась, а сейчас пора спать, завтра ждет новый день.

14.02.2010

Cygwin, sqlite, windows 7

В процессе изучения Django, самое простое решение для хранения данных, первым приходящее в голову это sqlite. Я не стал усложнять для себя задачу и заполняя конфигурационный файл поставил именно этот движок. Добавил необходимые приложения, запустил синхронизацию базы и отгреб «unable to open database file».

На сайта django есть секция распространенных ошибок новичков и там эта проблема описывается. Рекомендуется проверить права на файл базы и временную директорию и указывать полный путь. В моем случае эти советы оказались как мертвому припарка, ошибка все равно повторялась. Я уж было расстроился и попытался сначала установить в систему pysqlite2, потом начал устанавливать postgresql для cygwin, но внезапно мне пришла в голову одна идея. Оказалось все гораздо проще.

При синхронизации базы и запуске devserver, открывайте сессию с правами администратора. Ошибки исчезнут. В тоже самое время разработку вы можете вести параллельно используя обычную сессию.

Автоматическая проверка синтаксиса в VIM

Неожиданно обнаружил прекрасный скрипт автоматической проверки синтаксиса для VIM: Syntastic

Скрипт поддерживает ruby, html, xhtml, js, python, haml, sass, perl, php, haskel. Установка стандартная, но почти для каждого языка в системе должны быть установлены дополнительные утилиты.

Скачиваем архив скрипта и распаковываем его в нашу директорию с настройками VIM.

Добавляем следующие опции в наш vim.rc конфигурационный файл:

set statusline=%<%f%h%m%r%=%#warningmsg#%{SyntasticStatuslineFlag()}%*\ format=%{&fileformat}\ file=%{&fileencoding}\ enc=%{&encoding}\ %b\ 0x%B\ %l,%c%V\ %P
let g:syntastic_enable_signs=1
let g:syntastic_auto_loc_list=1

Для проверки html, xhtml устанавливаем tidy. Для проверки python устанавливаем pyflakes (pip instal pyflakes). Для проверки js устанавливаем jsl (Java Script Lint). На cygwin jsl.exe просто копируется в папку bin. Для проверки других языков смотрите соответствующие syntax checkers из установки скрипта.

13.02.2010

Установка easy_install и pip под cygwin

Используя компьютер под управлением windows я веду разработку в cygwin. Во-первых потому, что я слишком привык к консольным утилитам, таким как openssh, svn, git, hg, vim и тп. Практически для всего перечисленного можно найти GUI аналоги, но это все будет зависеть от платформы, а в консоли у вас все под рукой. Про настройку консоли я расскажу позже, а сейчас буквально три строчки кода для настройки питон-окружения в этой среде.

Устанавливать пакеты для питона максимально удобно используя такие распространенные утилиты как easy_install и PIP. В рамках данной заметки я не буду рассказывать о достоинствах или недостатках этих утилит, только о том как их правильно установить.

Для начала убедитесь, что у вас стоят следующие cygwin пакеты: python, wget, gcc. Если нужно добавьте их пользуясь установкой cygwin.

Дальше введите в консоли следующие команды:

wget http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9-py2.5.egg
sh setuptools-0.6c9-py2.5.egg

Все easy_install, установлена в систему. Ну, а pip уже элементарно устанавливается с ее использованием.

easy_install pip

Вот и все, теперь можно устанавливать другие пакеты в одну команду. Например продолжая изучение python я установил прекрасный интерпретатор питона bpython. И во время уроков наслаждаюсь автодополнением и подсветкой кода.

pip install bpython

Проблема со чтением CHM-файлов

Вообще то это старая проблема, но раньше я с ней не сталкивался. Решив изучить Python и скачав документацию в формате CHM, чтобы использовать в качестве справочника, я увидел в открытом файле справа пустое пространство с надписью: "Navigation to the webpage was canceled"


В вашем случае это может быть например "Action canceled". 

Такое поведение связано с проблемами безопасности Windows, а именно возможны два варианта:

  • Содержимое загруженного CHM файла не может быть открыто (как раз мой случай). Лечится так: правой кнопкой на файле, открываем свойства и кликаем "Unblock"

Вуаля, CHM файл отображается нормально, можно продолжать изучение питона.