Публикации с меткой «programming»

Блог python на хабрахабре

[Перевод] PHP: фрактал плохого дизайна

Хабы: Программирование, Python, PHP

Предисловие


Я капризный. Я жалуюсь о многих вещах. Многое в мире технологий мне не нравиться и это предсказуемо: программирование — шумная молодая дисциплина, и никто из нас не имеет ни малейшего представления, что он делает. Учитывая закон Старджона, у нас достаточно вещей для постижения на всю жизнь.

Тут другое дело. PHP не просто неудобен в использовании, плохо мне подходит, субоптимален или не соответствует моим религиозным убеждениям. Я могу рассказать вам много хороших вещей о языках, которые я стараюсь избегать, и много плохих вещей о языках, которые мне нравяться. Вперёд, спрашивайте! Получаются интересные обсуждения.

PHP — единственное исключение. Фактически каждая деталь PHP в какой-то мере поломана. Язык, структура, экосистема: всё плохо. И даже нельзя указать на одну убийственную вещь, настолько дефект систематичный. Каждый раз когда я пытаюсь систематизировать недостатки PHP, я теряюсь в поиске в глубину обнаруживая всё больше и больше ужасных мелочей(отсюда фрактал).

PHP — препятствие, отрава моего ремесла. Я схожу с ума от того, насколько он сломан и насколько воспеваем каждым уполномоченным любителем нежелающим научиться чему-либо ещё. У него ничтожно мало оправдывающих положительных качеств и я бы хотел забыть, что он вообще существует.
Читать дальше →

Oduvan’s Web Blog

Фансы и шортсы. Такого программирования вы еще не видели :)

Вчера для CheckIO придумали вам новую развлекуху, друзья мои, на эти выходные

Идея развлекухи в том, что надо придумать максимально необычное решение для самой обычной задачи. Например, самое простое из раздела Funny, и проще уже не придумаешь - Funny addition. На вход – массив из 2х интов, а на выходе их сумма. Но функция sum(data) – это же не смешно. Смешнее что-то вроде max(data) + min(data).

Заходим, пишем свое аморальное решение, и рейтенгуем решения друзей.

По результатам выложу сюда топ аморальщины. Развлекайтесь и удачных вым выходных

ЗЫ: У нас есть еще прикольные шортсы, но о них уже в другой раз

Oduvan’s Web Blog

ЧекЁ или почему я не сплю последние несколько месяцев

Я последние несколько месяцев любую свободную минутку посвещаю именно этому проекту www.checkio.org, т. к. чувствую, что делаю что-то полезное, что-то что может помогать молоды специалистам.

Я уже писал о нем, но сейчас, когда считаю, что закончен большой этап в развитии этого проекта решил написать еще раз, а лучше показать.

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

Так-же у нас на стадии тестирования терниры между программами. И первой задачей для турнира сейчас выбрана игра five in row. Надо написать программу, которая будет играть в эту игру, в простонароде – бота

После прохождения квалификации — вы сами, интерктивно, можете поиграть в нее. Можете поиграть с другими прораммами на арене. Турнир между программами у нас запланирован на конец марта. Хотелось бы увидеть по больше участников, должно быть интересно.

Еще хотелось бы услышать от вас отзыв. Как Вам проект?

Александр Соловьёв

SQLAlchemy: как втянуться

SQLAlchemy сейчас - очевидный лидер ORM в питоне, но у неë есть один довольно неприятный недостаток: чтобы начать пользоваться, приходится прочитать немало документации. Поэтому я решил написать (очень) короткий пост-введение в алхимию.

Уровень 1: SQL руками

Первым делом нам нужно соединение к базе, с которым можно что-то делать:

>>> from sqlalchemy import create_engine
>>> e = create_engine('mysql://user:pass@host/db')
>>> for r in e.execute('select * from table where id < %s', 2):
...     print dict(r)
{u'id': 1, u'info': u'first row'}

Из объектов, которые получаются итерацией результата - RowProxy - данные можно вытаскивать и индексом, и ключом, и атрибутом:

>>> r[0] == r['id'] == r.id
    True

Нужна транзакция?

>>> c = e.connect()
>>> c.begin()
>>> # work work work
>>> c.commit() # try/except: c.rollback() по желанию :)

Это уже что-то и так можно жить, тем более что оно экранирует параметры автоматически.

Уровень 2: SQL-выражения в питоне

Можно получить объект таблицы из базы (с автоопределением колонок) и работать с ним, если так будет удобнее:

>>> from sqlalchemy import Table, MetaData
>>> meta = MetaData(bind=e, reflect=True)
>>> table = meta.tables['table']
>>> list(e.execute(table.select(table.c.id < 2)))
    [(1, u'first row')]

Т.е. абсолютно идентичный запрос, но уже в питоне.

Уровень 3: ORM

Ну и если приятнее с объектами работать, которым можно поведение задавать:

>>> from sqlalchemy import orm
>>> class Table(object):
...     pass
>>> orm.Mapper(Table, meta.tables['table'])
>>> s = orm.Session(bind=e)
>>> s.query(Table).filter(Table.id < 2).first().info
    u'first row'

Тут уже можно использовать ORM по полной:

>>> class Artist(object):
...     pass
>>> orm.Mapper(Artist, meta.tables['artist'])
>>> class Album(object):
...     pass
>>> orm.Mapper(Album, meta.tables['album'])
>>> class Song(object):
...     pass
>>> orm.Mapper(Song, meta.tables['song'])
>>> s.query(Song).join(Album).filter(Album.id == 10).count()
    12L
>>> # Song первый, поэтому его нужно джоинить с альбомом
>>> s.query(Song.name, Album.name).join(Album).join(Artist).filter(Artist.id == 2).first()
    (u'Hex', u'Inflikted')
>>> print s.query(Song.name, Album.name).join(Album).join(Artist).filter(Artist.id == 2)
SELECT song.name AS song_name, album.name AS album_name
FROM song JOIN album ON album.id = song.album_id JOIN artist ON artist.id = album.artist_id
WHERE artist.id = %(id_1)s

Еще у сессий есть приятный момент - именованные параметры (не знаю, кстати, почему их нет у Engine или Connection):

>>> list(s.execute('select * from table where id < :id', {'id': 2}))
    [(1, u'first row')]

Разное

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

Метаданные с рефлексией и ранним биндингом - не совсем принятый подход, это только для маленьких наколенных скриптов и работы в шелле, скорее, а так обычно Engine к MetaData добавляют где-то отдельно, в чтении настроек, когда уже все таблицы определены (через meta.bind = e).

Сессию обычно тоже так напрямик не используют, особенно если многопоточное приложение - есть orm.scoped_session, который создаëт тред-локальную сессию.

Вот в принципе и всë, дальше есть документация. :)

Oduvan’s Web Blog

UCSVLOG. Нужна точка.

Это продолжение моего общения с собой на тему логов.

Размышляя на тему логов, у которых нет конца – я пришел к выводу – что у записи конец обязан быть. Парсеру возможно это и не важно, но человеку надо знать, что эта запись полная.

Например у Вас такая часть лога


"date time,"log,"inc sum: 100

Парсер эту строку распарсит очень просто, а вот у человека не будет уверенности в цифре. А может там был мильен.

Именно по этому у записи должна быть точка. И пока кроме идеи – тупо добавлять ячейку с точкой мне ничего в голову не пришло.

Так что теперь наша запись в логах меняется на следующую:

"date time,"log,"inc sum: 100,".

В принципе можно заморочиться на том, чтоб записи, в которых реально только одна точка – заменять на 2, а 2 – на 3. Ну т.е. при парсе надо понимать, что если в записи – одни точки, то нужно уменьшить их кол на одну. Но чем дольше я об этом думаю, тем больше понимаю, что это уже перебор. И такую погрешность можно взять в учет.

Откуда вообще у меня тараканы в голове на тему логов.

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

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

Сегодня уже фиксы в текущие логи внести не получится, но завтра постараюсь выкроить время.

Александр Соловьёв

Антиметапрограммирование

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

Строишь себе такие планы в голове: вот бы переключиться на язык, где побольше метапрограммирования - Руби там, может, или Лисп. Не очень серьёзные, но всё равно блокам в Руби иногда завидуешь, или там исполняемому классу.

А потом ты вдруг попадаешь в ситуацию, где код не очень хорош. И, например, пару человек из команды не парятся о качестве кода.

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

На этом мысль пока останавливается. ;-)

Александр Соловьёв

Антиметапрограммирование

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

Строишь себе такие планы в голове: вот бы переключиться на язык, где побольше метапрограммирования - Руби там, может, или Лисп. Не очень серьёзные, но всё равно блокам в Руби иногда завидуешь, или там исполняемому классу.

А потом ты вдруг попадаешь в ситуацию, где код не очень хорош. И, например, пару человек из команды не парятся о качестве кода.

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

На этом мысль пока останавливается. ;-)

Oduvan’s Web Blog

CheckIO.org – We are betta almost

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

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

Итак, за этот месяц нам удалось реализовать 3 идеи, причем в условиях полного рефакторинга ядра.

Первая идея— Learning. Я назвал это «Подарок для наших маленьких слушателей», хотя она может быть полезна и большим дядям. Если попытаться изложить их идею одним предложением — это цепочка задач от легкой к сложной, причем в описании самой задачи есть все справочные данные для ее решения. Таким образом пользователь на примерах учится питону и параллельно пробует свои силы, т. к. я абсолютно уверен, что в программировании — знания без практики не имеют никакой ценности.

В последствии это может быть использовано для изучения и получения навыков работы с оупенсорсными библиотеками (я бы написал, к примеру, обучалку для Stream). К тому же, если Вы – разработчик какой-либо библиотеки, Вы можете отправить мне список задач для использования Вашей библиотеки и мы добавим ее. Еще такой вариант: если это какие-нибудь системные библиотеки, то для них можно писать заглушки, полностью описывающие интерфейс, но ничего системного не делающие. Вообщем, мне кажется вижу, куда раскручивать эту тему, и мы с командой будем это делать.

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

Появление следующих 2 фич связаны с тем, что я люблю играть в логические игры: саперы, реверси, 5 в линию, лаинс, сокобан, судоку и многие другие. Благодаря чекио Вы сможете попробовать описать стратегию этих игр — программно. Т.е. Вы пишите программу, которая сама играет в игру.

Итак, следующий тип задач — это Score Games или Single Player Game. Это игры, в которых выиграть нельзя, но можно постараться набрать как можно больше очков. Первым представителем этих игр стал Lines lite. От классической игры в Lines эту игру отличает то, что не нужно, чтобы шар проходил путь полностью, а достаточно, чтоб он просто становился на пустое место. По мере того, как пользователи буду справляться с этой задачей, мы добавим и не «облегченную» версию.

Играть довольно просто. Вашей программе checkio передается дикт из следующих ключей:

  • steps — это массив координат только что поставленных шаров
  • map — это текущая карта. В этой версии игры размер карты 9х9. Каждый шар обозначен цифрой от 1 до 9, пустые места обозначены 0
  • colors — цвета следующих трех маркеров.

Как результат, Ваша функция должна вернуть массив из 2х координат (координата – это массив из 2х чисел) – откуда и куда необходимо передвинуть шарик на поле. Тот, кто впервые слышит об этой игре, тот может ознакомиться с ее правилами более подробно на wikipedia.

Как только Вы написали программу, нажимайте play. Вы увидите в результатах работы программы поле, которое будет заполняться шарами разных цветов. Нажимая next или prev, Вы будете двигаться по итерациям этой игры. Под игровым полем выводится количество набранных очков. Если Вы набираете больше 0, то попадаете в High Score этой игры. Мы попали в High Score с оглушительным счетом – 5ть очков. Попробуйте нас переплюнуть

Ну и о последнем обновлении Вы, наверное, уже догадались. Это Competition или Multi Player Game. Тут уж выигрывать можно и нужно. Для тестирования Вашей программы нужен соперник, т.е. уже написанная программа кем-то другим на этом портале, т.е. Ваши программы будут играть друг с другом. Давайте лучше на примерах. Первым представителем этой игры является 5 in row или Gomoku(wikipedia).

Вам нужно написать программу, которая бы играла с «кем-то» в эту игру. На вход (как уже принято) подается дикт со следующими ключами:

  • you — твой марк в игре
  • data — данные игры

Данные игры — это так же дикт со следующими значениями:

  • map — текущая карта. Массив 19х19 со значениями 1 или 2, если занято первым или вторым игроком и 0, если не занято никем
  • step — ход противника

На выходе – ваша программа должна вернуть координаты вашего хода.

Для выбора соперника вам необходимо нажать на Check With и обозначить пользователя, находящегося сейчас на арене, нажав напротив понравившегося кнопку check. Можете для тестирования выбирать в соперниках – oduvan. Можете посмотреть на нашей странице фейсбука или линкедина, как она играет пока сама с собой.

Как и в score game справа выведутся результаты игры. В соревнованиях игра прокручивается дважды: в первой игре первым ходишь ты, во второй — твой соперник. Именно поэтому и передается параметр you в функцию.

Переключаться между итерациями можно так же как и в score game, с одной лишь разницей, что над этим еще идет переключение между этими двумя играми. Ну и, конечно же, если пользователей двое и игр две, то, одним из результатов встречи может быть ничья.

Когда Вы решили, что ваша программа теперь очень даже ничего и пришло время и ей найти свое место на арене – смело нажимайте «On Arena» на верхней панели. Теперь другие пользователи могут тестить свою игру с Вашей программой. Игры, помещенные на Арену, будут периодически запускаться системой для проведения между ними соревнований. Если во время соревнования Ваша программа выпала с ошибкой, то она вылетает из турнирной таблицы, иначе, своими победами она будет достигать все лучших и лучших результатов.

Так что, попробуйте написать своего непобедимого игрока, мы своего уже написали.

Когда мы пытались написать свое решение для этих типов задач, самой большой проблемой было несовершенство браузерного редактора кода. Для средних тасков из каталога – его очень даже хватает, но Competition и Score Game – это уже более сложные и комплексные задачи. Теперь Вы можете просто скачать эту программу себе на компьютер и решать ее в своем любимом редакторе кода, тут же тестировать и дебажить работу программы. Для этого необходимо распаковать скачанный архив, написать свою программу в файл solution.py и запустить checkio.py для проверки ее работы.
В случае Score Game, ваша программа будет играть с компьютером и зарабатывать очки, а в случае с Competition Game, Вы будете сами играть со своей программой, либо можете использовать возможность заранее составить файл с массивом ходов противника и передать ее checkio.py во время запуска.

Ну и конечно, самое крутое, это то, что у проекта появились новости и рсс через feedbearn, свое место в FaceBook, LinkedIn и Twitter, теперь Вы сможете быть в курсе всего и удобным для Вас способом. И именно поэтому на своем блоге я больше ни слова не напишу о чекио, дабы не засорять агрегаторы, в которых нахожусь, нецелевой информацией. Тут теперь только о питоне, джанге и еще всякой интересной программистической штукенции. Но, тем не менее, буду очень признателен тем своим читателям, которые отметят этот скромный проект отзывами на своих блогах, думаю, это приведет в проект еще больше сильных игроков с новыми и интересными решениями. Буду очень признателен за отзывы, критику и предложения, которые касаются этого проекта, в FaceBook и LinkedIn.

Еще появилась возможность у себя в профиле указывать ссылки на свой сайт, на профили в LinkedIn, FaceBook и Twitter. Мир должен знать своих героев в лицо. А также можете повесить на свой сайт нашу плюшку (чем-то напоминающую плюшку из StackOverflow), тем самым пригласить своих читателей в наш сервис, ну и похвастать своими результатами в нем. Я себе уже такую повесил.



Еще немного о критике. Во время первого запуска мы выложили несколько сырых либо некорректных программ. Подобные ошибки я бы хотел устранять благодаря Тестеровщикам/Контролерам качества (название для этой группы людей еще обсуждается). Эти люди будут видеть еще не опубликованные задачи. Они смогут попробовать решить их раньше всех, высказать свое мнение об описании, сложности и по существу самой задачи. Еще не знаю как, но уверяю, что их заслуги не будут забыты. Подать заявку о входе в ряды Тестеровщиков можно в разделе «Как помочь?» и выбрать «Стать тестером» или просто написать мне.

Вообщем, это вкратце то, что я хотел сегодня рассказать об этом проекте. Хочу сказать спасибо моей команде. Отдельные слова благодарности Денисенко Екатерине за помощь в переводе некоторых частей сайта на английский язык. Спасибо noonerus за помощь в нарезке и за то, что дал мне несколько хороших и весьма полезных уроков. И самое большое спасибо Вам за то, что помогаете своими багрепортами, отзывами и программами

Ту би континюэд….

Блог python на хабрахабре

Язык программирования Python / Материалы продвинутого уровня по Питону

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

После прочтения Dive into Python или подобной ей и ознакомления с документацией возникает вопрос, а что читать дальше? Можно обратиться к списку книг на python.org. Там есть раздел Advanced Books, но в нем всего лишь 6 книг (седьмая не выходила), и только одну я бы назвал по-настоящему стоящей.

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

Ниже собраны сложные материлы про Питон, его устройство и возможности. Все на английском (грех, не знать технический английский). Про Dive into Python я слукавил. Большинство приведенных материалов требуют хорошее знание Питона и наличие опыта программирования на нем.

Александр Соловьёв

GAE писали идиоты

К сожалению, этот пост не изменит ничего, но тем не менее я не могу не поделиться наблюдением о том, что авторы питоновской части GAE — идиоты. Они попытались скопировать синтаксис джангового орма, при этом порядочно искалечив его, но не это самое плохое. Самое плохое, что я на сегодняшний день нашëл, выражается вот таким кусочком кода:

def improved(request):
    d = date.today()
    q = Stat.all().filter('date', d — timedelta(1))
    def value(q):
        try:
            return q.get().value
        except AttributeError:
            return 0
    return {
        'users': value(q.filter('type', Stat.TOTALUSERS)),
        'questions': value(q.filter('type', Stat.TOTALQUESTIONS)),
        }

Тут всë выглядит клëво, правда? Вот только есть один нюанс: в questions у меня тут попадает 0 (хотя судя по базе данных — должно попасть куда более другое число). Я долго-долго думал и поменял местами users и questions, и после этого 0 стал попадать в users ( questions в это время оказались заполнены как надо).

Я не знаю, какой идиот это сделал, но надеюсь, что он хотя бы икнëт. ;)

Александр Соловьёв

GAE писали идиоты

К сожалению, этот пост не изменит ничего, но тем не менее я не могу не поделиться наблюдением о том, что авторы питоновской части GAE - идиоты. Они попытались скопировать синтаксис джангового орма, при этом порядочно искалечив его, но не это самое плохое. Самое плохое, что я на сегодняшний день нашëл, выражается вот таким кусочком кода:

def improved(request):
    d = date.today()
    q = Stat.all().filter('date', d - timedelta(1))
    def value(q):
        try:
            return q.get().value
        except AttributeError:
            return 0
    return {
        'users': value(q.filter('type', Stat.TOTALUSERS)),
        'questions': value(q.filter('type', Stat.TOTALQUESTIONS)),
        }

Тут всë выглядит клëво, правда? Вот только есть один нюанс: в questions у меня тут попадает 0 (хотя судя по базе данных - должно попасть куда более другое число). Я долго-долго думал и поменял местами users и questions, и после этого 0 стал попадать в users ( questions в это время оказались заполнены как надо).

Я не знаю, какой идиот это сделал, но надеюсь, что он хотя бы икнëт. ;)

Александр Соловьёв

Opster

Two months ago I've released little command line parsing library for Python called opster (actually it was called finaloption then, but I've renamed it because of remark from native speaker ;-)). What's the reason to write one more command line parser when Python already has getopt and optparse in standard library and not so long time ago argparse and optfunc were released?

Well... as usually, because I think that they are going wrong way and doing wrong things. Of course, IMO (but what matters if not opinion? :P).

It started to happen when Zed Shaw wrote big article on Python warts and mentioned that Lamson has much better command line parsing solution than argparse/optparse. I was interested in this topic a little at the time and I looked at the code. It would be lie to say that I liked it. In fact I thought that this is a heresy of the same level as optparse. ;-)

I've written in Twitter that it's funny to say that Lamson has superior command system and got some amount of sarcasm from Zed and clear understanding that Zed see nothing bad when:

  • command functions should be defined in a single module
  • default settings are defined by calling separate function inside a command function
  • specifying option in command line with mistake wouldn't raise error
  • formatting of help text on options is done by hands

So I thought that world needs Mercurial's command system. ;) And I've rewritten it as library, keeping main idea.

Small example of usage:

from opster import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

I think that you should understand what's going on here. For example, option is required to have long name (keyword argument name), possibly short name (using '' will discard short name), some default value and help string. Default value determines what should be done to incoming data:

  • string - nothing happens, incoming value will remain as string
  • int - incoming value is parsed by calling int()
  • list - incoming value is appended to the list
  • function is called with incoming value and output is used
  • True/False/None - option needs no argument, just switching default value in opposite value

After wrapping with @command your function main() can be called:

  • without arguments at all; it will parse sys.argv in this case
  • with argument named argv, which needs to be list of strings (same as sys.argv[1:])
  • with usual arguments/keyword arguments, which are defined in function

I think it may be not obvious that you will get clean values in your function (for example, port will contain value 8000), and not some strange three-tuples.

And you get such help for free:

piranha@gto ~/dev/misc/opster>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Furthermore, underscores in argument names are converted to hyphens to support conventions of command line. ;)

I should mention that option names (and subcommand names, if you're using them) can be shortened: i.e. you can say --pi instead of --pid-file.

If I'm going to compare opster with something, this should be optfunc by Simon Willison. Most noticeable differences are syntax of command definitions and subcommand support. Actually optfunc has subcommand support, but it's pretty incomplete.

Opster uses getopt inside to parse options and that's the reason why it's somewhat bigger than optfunc (which is essentially optparse wrapper). Opster's internal API - options are list of four-tuples (short name, long name, default value, help string) - is exactly the same as Mercurial's API for defining options. This means that such code will work (taken from test_cmd.py):

import opster

config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]

@opster.command(options=config_opts)
def initdb(config):
    """Initialize database"""
    pass

@opster.command(options=config_opts)
def runserver(listen=('l', 'localhost', 'ip to listen on'),
              port=('p', 5000, 'port to listen on'),
              **opts):
    """Run development server"""
    print locals()

if __name__ == '__main__':
    opster.dispatch()

Interesting thing happens in definition of runserver, help and output of which looks like this:

piranha@gto ~/dev/misc/opster> ./test_cmd.py help runs
test_cmd.py runserver [OPTIONS]

Run development server

options:

 -l --listen  ip to listen on (default: localhost)
 -p --port    port to listen on (default: 5000)
 -c --config  config file to use (default: webshops.ini)
 -h --help    display help

piranha@gto ~/dev/misc/opster> ./test_cmd.py runs
{'port': 5000, 'opts': {'config': 'webshops.ini'}, 'listen': 'localhost'}

You can factor out common options and pass them to @command decorator, keeping your pants DRY. ;-)

So... Read documentation and use it! :) Any feedback, questions, suggestions and patches are highly welcome. ;-)

Александр Соловьёв

Opster

Two months ago I've released little command line parsing library for Python called opster (actually it was called finaloption then, but I've renamed it because of remark from native speaker ;-)). What's the reason to write one more command line parser when Python already has getopt and optparse in standard library and not so long time ago argparse and optfunc were released?

Well... as usually, because I think that they are going wrong way and doing wrong things. Of course, IMO (but what matters if not opinion? :P).

It started to happen when Zed Shaw wrote big article on Python warts and mentioned that Lamson has much better command line parsing solution than argparse/optparse. I was interested in this topic a little at the time and I looked at the code. It would be lie to say that I liked it. In fact I thought that this is a heresy of the same level as optparse. ;-)

I've written in Twitter that it's funny to say that Lamson has superior command system and got some amount of sarcasm from Zed and clear understanding that Zed see nothing bad when:

  • command functions should be defined in a single module
  • default settings are defined by calling separate function inside a command function
  • specifying option in command line with mistake wouldn't raise error
  • formatting of help text on options is done by hands

So I thought that world needs Mercurial's command system. ;) And I've rewritten it as library, keeping main idea.

Small example of usage:

from opster import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

I think that you should understand what's going on here. For example, option is required to have long name (keyword argument name), possibly short name (using '' will discard short name), some default value and help string. Default value determines what should be done to incoming data:

  • string - nothing happens, incoming value will remain as string
  • int - incoming value is parsed by calling int()
  • list - incoming value is appended to the list
  • function is called with incoming value and output is used
  • True/False/None - option needs no argument, just switching default value in opposite value

After wrapping with @command your function main() can be called:

  • without arguments at all; it will parse sys.argv in this case
  • with argument named argv, which needs to be list of strings (same as sys.argv[1:])
  • with usual arguments/keyword arguments, which are defined in function

I think it may be not obvious that you will get clean values in your function (for example, port will contain value 8000), and not some strange three-tuples.

And you get such help for free:

piranha@gto ~/dev/misc/opster>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Furthermore, underscores in argument names are converted to hyphens to support conventions of command line. ;)

I should mention that option names (and subcommand names, if you're using them) can be shortened: i.e. you can say --pi instead of --pid-file.

If I'm going to compare opster with something, this should be optfunc by Simon Willison. Most noticeable differences are syntax of command definitions and subcommand support. Actually optfunc has subcommand support, but it's pretty incomplete.

Opster uses getopt inside to parse options and that's the reason why it's somewhat bigger than optfunc (which is essentially optparse wrapper). Opster's internal API - options are list of four-tuples (short name, long name, default value, help string) - is exactly the same as Mercurial's API for defining options. This means that such code will work (taken from test_cmd.py):

import opster

config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]

@opster.command(options=config_opts)
def initdb(config):
    """Initialize database"""
    pass

@opster.command(options=config_opts)
def runserver(listen=('l', 'localhost', 'ip to listen on'),
              port=('p', 5000, 'port to listen on'),
              **opts):
    """Run development server"""
    print locals()

if __name__ == '__main__':
    opster.dispatch()

Interesting thing happens in definition of runserver, help and output of which looks like this:

piranha@gto ~/dev/misc/opster> ./test_cmd.py help runs
test_cmd.py runserver [OPTIONS]

Run development server

options:

 -l --listen  ip to listen on (default: localhost)
 -p --port    port to listen on (default: 5000)
 -c --config  config file to use (default: webshops.ini)
 -h --help    display help

piranha@gto ~/dev/misc/opster> ./test_cmd.py runs
{'port': 5000, 'opts': {'config': 'webshops.ini'}, 'listen': 'localhost'}

You can factor out common options and pass them to @command decorator, keeping your pants DRY. ;-)

So... Read documentation and use it! :) Any feedback, questions, suggestions and patches are highly welcome. ;-)

Александр Соловьёв

Finaloption

Я тут выпустил библиотеку для парсинга коммандлайновых аргументов. Зачем еще одна, когда уже в питоне есть getopt и optparse, когда не так давно появились argparse и optfunc?

Ну… как обычно, потому что они все неправильные и делают не то и не так, как хочется. ;)

Всë началось тогда, когда Зед Шоу (Zed Shaw) написал здоровенную статью про проблемы в питоне и упомянул, что он в Lamson’e использует систему парсинга аргументов значительно более приятную, чем argparse/optparse. Меня в тот момент как раз беспокоила эта тема и я пошëл посмотреть. Сказать, что она мне понравилась, я не могу. Даже наоборот, мне она показалась не меньшей ересью, чем optparse, но с другого боку.

Я в твиттере написал, что это смешно, и удостоился некоторого количества сарказма и понимания того, что он не видит ничего плохого в том, что:

  • у функций команд должно быть определëнное имя
  • функции команд должны быть определены в одном модуле
  • дефолтные значения определяются вызовом левой функции в функции-команде
  • подача опции с ошибкой в написании не приведëт к ошибке
  • форматировать хелп по опциям нужно ручками самому

В общем, я подумал, что миру нужна система команд из меркуриала. ;) И выдрал — большую часть, конечно, переписал, потому как оно было под меркуриал заточено, но сама идея осталась как есть.

Вот небольшой пример кода:

from finaloption import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

Думаю, что примерно понятно, к чему это всë. Например, опция обязательно имеет длинное имя (имя keyword argument’а), возможно короткое имя ('' — убивает короткое имя), какой-то дефолт и помощь. Дефолтное значение определяет, что делать с пришедшими данными:

  • строка — ничего не происходит, так и остаëтся строкой
  • int — на пришедшей строке вызывается int()
  • список — пришедший аргумент просто прибавляется к списку
  • функция исполняется с пришедшим аргументом
  • True/False/None — не требует аргумента, просто переключает дефолт в противоположное значение

Что еще неочевидно — main() может не принимать аргументов (будет парсить sys.argv), принимать список строк (аналогичный sys.argv) или те же самые аргументы, которые принимает обëрнутая функция.

Помощь генерируется такого вида:

piranha@gto ~/dev/misc/finaloption>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Кроме всего прочего, подчëркивания в именах аргументов превращаются в дефисы, чтоб не нарушать конвенций, принятых для коммандлайна. ;)

Что еще хорошего? :) Ну, например то, что имена опций (и сабкомманд, если такие используются), можно сокращать: вместо --pid-file использовать --pi, например.

Самый близкий аналог этой библиотеки — это optfunc Саймона Виллисона. Из отличий — синтаксис объявления аргументов и поддержка сабкоманд. Точнее у него поддержка тоже есть, но на уровне хака.

Плюс optfunc — это фактически просто надстройка над optparse, небольшое облегчение его синтаксиса, а finaloption использует только getopt — поэтому внутренний апи на самом деле начинался как внешний, а @command — это просто обëртка для finaloption.parse. ;-) И, кстати, @command вполне понимает внутреннее представление опций:

>>> opts = [('l', 'listen', 'localhost', 'ip to listen on'),
...         ('p', 'port', 8000, 'port to listen on')]
>>> main = command(options=opts)(main)

Благодаря этому генерация кучи команд с потенциально похожими опциями упрощается. Я вот подумываю, что может при наличии кваргсов у функции и переданных опций одновременно их просто объединять?.. Тогда можно будет выделять общие опции в такие списки, а уникальные писать кваргсами у функции.

В общем, пользуйтесь, пожалуйста. ;) Отзывы, вопросы, предложения и код крайне приветствуются. :)

Александр Соловьёв

Finaloption

Я тут выпустил библиотеку для парсинга коммандлайновых аргументов. Зачем еще одна, когда уже в питоне есть getopt и optparse, когда не так давно появились argparse и optfunc?

Ну... как обычно, потому что они все неправильные и делают не то и не так, как хочется. ;)

Всë началось тогда, когда Зед Шоу (Zed Shaw) написал здоровенную статью про проблемы в питоне и упомянул, что он в Lamson'e использует систему парсинга аргументов значительно более приятную, чем argparse/optparse. Меня в тот момент как раз беспокоила эта тема и я пошëл посмотреть. Сказать, что она мне понравилась, я не могу. Даже наоборот, мне она показалась не меньшей ересью, чем optparse, но с другого боку.

Я в твиттере написал, что это смешно, и удостоился некоторого количества сарказма и понимания того, что он не видит ничего плохого в том, что:

  • у функций команд должно быть определëнное имя
  • функции команд должны быть определены в одном модуле
  • дефолтные значения определяются вызовом левой функции в функции-команде
  • подача опции с ошибкой в написании не приведëт к ошибке
  • форматировать хелп по опциям нужно ручками самому

В общем, я подумал, что миру нужна система команд из меркуриала. ;) И выдрал - большую часть, конечно, переписал, потому как оно было под меркуриал заточено, но сама идея осталась как есть.

Вот небольшой пример кода:

from finaloption import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

Думаю, что примерно понятно, к чему это всë. Например, опция обязательно имеет длинное имя (имя keyword argument'а), возможно короткое имя ('' - убивает короткое имя), какой-то дефолт и помощь. Дефолтное значение определяет, что делать с пришедшими данными:

  • строка - ничего не происходит, так и остаëтся строкой
  • int - на пришедшей строке вызывается int()
  • список - пришедший аргумент просто прибавляется к списку
  • функция исполняется с пришедшим аргументом
  • True/False/None - не требует аргумента, просто переключает дефолт в противоположное значение

Что еще неочевидно - main() может не принимать аргументов (будет парсить sys.argv), принимать список строк (аналогичный sys.argv) или те же самые аргументы, которые принимает обëрнутая функция.

Помощь генерируется такого вида:

piranha@gto ~/dev/misc/finaloption>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Кроме всего прочего, подчëркивания в именах аргументов превращаются в дефисы, чтоб не нарушать конвенций, принятых для коммандлайна. ;)

Что еще хорошего? :) Ну, например то, что имена опций (и сабкомманд, если такие используются), можно сокращать: вместо --pid-file использовать --pi, например.

Самый близкий аналог этой библиотеки - это optfunc Саймона Виллисона. Из отличий - синтаксис объявления аргументов и поддержка сабкоманд. Точнее у него поддержка тоже есть, но на уровне хака.

Плюс optfunc - это фактически просто надстройка над optparse, небольшое облегчение его синтаксиса, а finaloption использует только getopt - поэтому внутренний апи на самом деле начинался как внешний, а @command - это просто обëртка для finaloption.parse. ;-) И, кстати, @command вполне понимает внутреннее представление опций:

>>> opts = [('l', 'listen', 'localhost', 'ip to listen on'),
...         ('p', 'port', 8000, 'port to listen on')]
>>> main = command(options=opts)(main)

Благодаря этому генерация кучи команд с потенциально похожими опциями упрощается. Я вот подумываю, что может при наличии кваргсов у функции и переданных опций одновременно их просто объединять?.. Тогда можно будет выделять общие опции в такие списки, а уникальные писать кваргсами у функции.

В общем, пользуйтесь, пожалуйста. ;) Отзывы, вопросы, предложения и код крайне приветствуются. :)

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс